From 5e67c1c79589dcde7d5bfc3d7e3ee1f718fa4e5a Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 03:49:45 -0700 Subject: [PATCH 01/17] hyprland: preserve tiled geometry when dragging float Snapshot tiled window geometry before enabling floating from the mouse drag/resize bindings, then restore it so detached tiled windows keep their current size. --- dotfiles/config/hypr/hyprland.lua | 41 +++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/dotfiles/config/hypr/hyprland.lua b/dotfiles/config/hypr/hyprland.lua index ad1c1cec..a8ca1f2e 100644 --- a/dotfiles/config/hypr/hyprland.lua +++ b/dotfiles/config/hypr/hyprland.lua @@ -520,6 +520,33 @@ local function window_center(window) numeric_component(at, "y", 2) + numeric_component(size, "y", 2) / 2 end +local function tiled_window_geometry(window) + if not window or window.floating then + return nil + end + + local selector = window_selector(window) + if not selector then + return nil + end + + local at = window.at or {} + local size = window.size or {} + local width = math.floor(numeric_component(size, "x", 1)) + local height = math.floor(numeric_component(size, "y", 2)) + if width <= 0 or height <= 0 then + return nil + end + + return { + selector = selector, + x = math.floor(numeric_component(at, "x", 1)), + y = math.floor(numeric_component(at, "y", 2)), + width = width, + height = height, + } +end + local function window_distance_squared(window, x, y) local wx, wy = window_center(window) local dx = wx - x @@ -1411,13 +1438,23 @@ local function apply_scratchpad_geometry(name, window, target_monitor) end end +local function float_active_window_preserving_tiled_geometry() + local geometry = tiled_window_geometry(hl.get_active_window()) + hl.dsp.window.float({ action = "enable", window = geometry and geometry.selector or nil })() + if geometry then + hl.dsp.window.resize({ x = geometry.width, y = geometry.height, relative = false, window = geometry.selector })() + hl.dsp.window.move({ x = geometry.x, y = geometry.y, relative = false, window = geometry.selector })() + end + return geometry +end + local function float_and_drag_active_window() - hl.dsp.window.float({ action = "enable" })() + float_active_window_preserving_tiled_geometry() hl.dsp.window.drag()() end local function float_and_resize_active_window() - hl.dsp.window.float({ action = "enable" })() + float_active_window_preserving_tiled_geometry() hl.dsp.window.resize()() end From 9a28a63ba3cf32b9851ae66f66c4e82b8affa684 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 12:37:08 -0700 Subject: [PATCH 02/17] Add Hyprland rofi layout selector --- dotfiles/config/hypr/hyprland.lua | 17 ++++++++ dotfiles/lib/bin/hypr_rofi_layout | 70 +++++++++++++++++++++++++++++++ nixos/hyprland.nix | 14 +++++++ 3 files changed, 101 insertions(+) create mode 100755 dotfiles/lib/bin/hypr_rofi_layout diff --git a/dotfiles/config/hypr/hyprland.lua b/dotfiles/config/hypr/hyprland.lua index a8ca1f2e..986f7420 100644 --- a/dotfiles/config/hypr/hyprland.lua +++ b/dotfiles/config/hypr/hyprland.lua @@ -728,6 +728,22 @@ local function set_layout(layout) end end +_G.im_hyprland_set_layout = function(layout) + if not layout_names[layout] then + hl.notification.create({ + text = "Unknown layout: " .. tostring(layout), + duration = 1800, + icon = notification_icons.warning, + color = "rgba(edb443ff)", + font_size = 13, + }) + return + end + + set_layout(layout) + notify_layout(layout) +end + local function sync_layout_for_active_workspace() current_layout = current_workspace_layout() hl.config({ general = { layout = hyprland_layout(current_layout) } }) @@ -2308,6 +2324,7 @@ bind(hyper .. " + P", exec("rofi-pass")) bind(hyper .. " + H", exec([[grim -g "$(slurp)" - | swappy -f -]])) bind(hyper .. " + C", exec("rofi_tmcodex.sh")) bind(hyper .. " + SHIFT + L", exec("hyprlock")) +bind(hyper .. " + L", exec("hypr_rofi_layout")) bind(hyper .. " + K", exec("rofi_kill_process.sh")) bind(hyper .. " + SHIFT + K", exec("rofi_kill_all.sh")) bind(hyper .. " + R", exec("rofi-systemd")) diff --git a/dotfiles/lib/bin/hypr_rofi_layout b/dotfiles/lib/bin/hypr_rofi_layout new file mode 100755 index 00000000..1458d6df --- /dev/null +++ b/dotfiles/lib/bin/hypr_rofi_layout @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +set -euo pipefail + +ensure_hyprland_instance() { + if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then + return + fi + + local runtime="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" + local hypr_dir="$runtime/hypr" + if [[ ! -d "$hypr_dir" ]]; then + return + fi + + local socket_dir + socket_dir="$( + find "$hypr_dir" -mindepth 2 -maxdepth 2 -name .socket.sock -printf '%T@ %h\n' 2>/dev/null | + sort -nr | + awk 'NR == 1 { print $2 }' + )" + + if [[ -n "$socket_dir" ]]; then + export HYPRLAND_INSTANCE_SIGNATURE="${socket_dir##*/}" + fi +} + +current_layout() { + local state_file="${XDG_RUNTIME_DIR:-}/hyprland-layout-state" + if [[ -r "$state_file" ]]; then + awk -F= '$1 == "layout" { print $2; exit }' "$state_file" + fi +} + +ensure_hyprland_instance + +layouts=( + "nStack Columns" + "master Large main" + "grid Grid" + "monocle Monocle" +) + +current="$(current_layout || true)" +selected_row=0 +labels=() + +for index in "${!layouts[@]}"; do + layout="${layouts[$index]%%$'\t'*}" + label="${layouts[$index]#*$'\t'}" + labels+=("$label") + + if [[ "$layout" == "$current" ]]; then + selected_row="$index" + fi +done + +selection="$( + printf '%s\n' "${labels[@]}" | + rofi -dmenu -i -p "Layout" -selected-row "$selected_row" +)" || exit 0 + +for entry in "${layouts[@]}"; do + layout="${entry%%$'\t'*}" + label="${entry#*$'\t'}" + + if [[ "$label" == "$selection" ]]; then + hyprctl dispatch "_G.im_hyprland_set_layout(\"$layout\")" >/dev/null + exit 0 + fi +done diff --git a/nixos/hyprland.nix b/nixos/hyprland.nix index 237e46eb..6cee82a8 100644 --- a/nixos/hyprland.nix +++ b/nixos/hyprland.nix @@ -116,6 +116,19 @@ exec ${../dotfiles/lib/bin/hypr_shell_ui} "$@" ''; }; + hyprRofiLayout = pkgs.writeShellApplication { + name = "hypr_rofi_layout"; + runtimeInputs = [ + pkgs.coreutils + pkgs.findutils + pkgs.gawk + pkgs.rofi + hyprlandPackage + ]; + text = '' + exec ${../dotfiles/lib/bin/hypr_rofi_layout} "$@" + ''; + }; hyprscratchSettings = { daemon_options = "clean"; global_options = ""; @@ -267,6 +280,7 @@ ddcutil # Monitor input switching over DDC/CI # For scripts + hyprRofiLayout hyprRofiWindow hyprShellUi jq From 968abf1a05d0c9d0c786f04ca70392e68f9a1fcb Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 18:00:11 -0700 Subject: [PATCH 03/17] Remove obsolete ryzen-shine original config --- nixos/machines/ryzen-shine-original.nix | 105 ------------------------ 1 file changed, 105 deletions(-) delete mode 100644 nixos/machines/ryzen-shine-original.nix diff --git a/nixos/machines/ryzen-shine-original.nix b/nixos/machines/ryzen-shine-original.nix deleted file mode 100644 index a20ef0b5..00000000 --- a/nixos/machines/ryzen-shine-original.nix +++ /dev/null @@ -1,105 +0,0 @@ -{ - lib, - pkgs, - ... -}: { - imports = [ - ../configuration.nix - ]; - - features.full.enable = true; - myModules.kubelet.enable = false; - myModules.nvidia.enable = true; - # Needed for now because monitors have different refresh rates - myModules.xmonad.picom.vSync.enable = false; - myModules.cache-server = { - enable = true; - port = 3090; - }; - myModules.gitea-runner.enable = true; - myModules.postgres.enable = true; - myModules.railbird-k3s = { - enable = true; - # extraFlags = ["--node-taint preferNoSchedule=true:NoSchedule"]; - }; - - boot.loader.systemd-boot.configurationLimit = 5; - - networking.hostName = "ryzen-shine"; - myModules.hostIdentity = { - emoticon = "☀️"; - tmux.background = "#ea580c"; - }; - - environment.systemPackages = with pkgs; [ - perf - ]; - - boot.initrd.systemd.enable = true; - boot.plymouth = { - enable = false; - }; - - hardware.enableRedistributableFirmware = true; - - networking.interfaces.enp5s0.useDHCP = true; - networking.interfaces.wlp4s0.useDHCP = true; - - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod"]; - boot.initrd.luks.devices."cryptroot".device = "/dev/nvme0n1p5"; - boot.initrd.kernelModules = ["dm-snapshot"]; - - # install nvidia drivers in addition to intel one - hardware.graphics.extraPackages = [pkgs.linuxPackages.nvidia_x11.out]; - hardware.graphics.extraPackages32 = [pkgs.linuxPackages.nvidia_x11.lib32]; - services.xserver = { - videoDrivers = ["nvidia"]; - }; - - hardware.nvidia.modesetting.enable = true; - - hardware.graphics.enable32Bit = true; - - boot.kernelModules = ["kvm-amd"]; - boot.extraModulePackages = []; - - fileSystems."/" = { - device = "/dev/disk/by-uuid/356173ab-d076-43e0-aeb6-6a6829c4402b"; - fsType = "ext4"; - }; - - fileSystems."/boot" = { - device = "/dev/disk/by-uuid/B270-C7E6"; - fsType = "vfat"; - }; - - fileSystems."/shared" = { - device = "/dev/disk/by-uuid/D4009CE8009CD33A"; - fsType = "ntfs"; - options = ["nofail" "uid=0" "gid=users" "umask=002"]; - }; - - swapDevices = [ - {device = "/dev/disk/by-uuid/f719b44e-295a-4909-9a60-84f87acb7f77";} - ]; - - # nix.settings.maxJobs = lib.mkDefault 16; - # High-DPI console - console.font = lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-u28n.psf.gz"; - - # services.xrdp.enable = true; - # services.xrdp.defaultWindowManager = "startplasma-x11"; - # services.xrdp.openFirewall = true; - - system.stateVersion = "20.03"; - home-manager.sharedModules = [ - { - home.stateVersion = "21.05"; - } - ]; - - # users.extraUsers.dean.home = "/shared/dean"; -} From 787f312cbe8577b1acb4af15a0d6d8b8f52716ff Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 18:00:31 -0700 Subject: [PATCH 04/17] hyprland: dispatch bind callbacks explicitly --- dotfiles/config/hypr/hyprland.lua | 114 +++++++++--------- .../checks/hyprland-config-syntax/default.nix | 5 + .../hyprland-config-syntax/smoke-test.lua | 31 +++-- 3 files changed, 87 insertions(+), 63 deletions(-) diff --git a/dotfiles/config/hypr/hyprland.lua b/dotfiles/config/hypr/hyprland.lua index 986f7420..95c170b4 100644 --- a/dotfiles/config/hypr/hyprland.lua +++ b/dotfiles/config/hypr/hyprland.lua @@ -116,6 +116,10 @@ local function exec(command) return hl.dsp.exec_cmd(command) end +local function dispatch(dispatcher) + return hl.dispatch(dispatcher) +end + local function shell_quote(value) return "'" .. tostring(value):gsub("'", "'\\''") .. "'" end @@ -652,7 +656,7 @@ local function update_nstack_count() end stack_count = math.max(stack_count, 2) - hl.dsp.layout("setstackcount " .. tostring(stack_count))() + dispatch(hl.dsp.layout("setstackcount " .. tostring(stack_count))) end local function schedule_nstack_count_update() @@ -788,24 +792,24 @@ end local function monocle_next() local window = hl.get_active_window() if window and window.group and window.group.size and window.group.size > 1 then - hl.dsp.group.next({ window = window_selector(window) })() + dispatch(hl.dsp.group.next({ window = window_selector(window) })) elseif current_layout == monocle_layout then - hl.dsp.layout("cyclenext")() + dispatch(hl.dsp.layout("cyclenext")) update_monocle_notice() else - hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false })() + dispatch(hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false })) end end local function monocle_prev() local window = hl.get_active_window() if window and window.group and window.group.size and window.group.size > 1 then - hl.dsp.group.prev({ window = window_selector(window) })() + dispatch(hl.dsp.group.prev({ window = window_selector(window) })) elseif current_layout == monocle_layout then - hl.dsp.layout("cycleprev")() + dispatch(hl.dsp.layout("cycleprev")) update_monocle_notice() else - hl.dsp.window.cycle_next({ next = false, tiled = true, floating = false })() + dispatch(hl.dsp.window.cycle_next({ next = false, tiled = true, floating = false })) end end @@ -820,30 +824,30 @@ local function focus_direction(direction) return end - hl.dsp.focus({ direction = direction })() + dispatch(hl.dsp.focus({ direction = direction })) end local function swap_direction(direction) if enable_nstack and is_nstack_layout(current_layout) and active_group_size() <= 1 then - hl.dsp.layout("swapdirection " .. direction)() + dispatch(hl.dsp.layout("swapdirection " .. direction)) return end - hl.dsp.window.swap({ direction = direction })() + dispatch(hl.dsp.window.swap({ direction = direction })) end local function focus_workspace(workspace_id) - hl.dsp.focus({ workspace = tostring(workspace_id), on_current_monitor = true })() + dispatch(hl.dsp.focus({ workspace = tostring(workspace_id), on_current_monitor = true })) end local function move_window_to_workspace(workspace_id, follow, window) local target_window = window or hl.get_active_window() local target_selector = window_selector(target_window) - hl.dsp.window.move({ workspace = tostring(workspace_id), follow = false, window = target_selector })() + dispatch(hl.dsp.window.move({ workspace = tostring(workspace_id), follow = false, window = target_selector })) if follow then focus_workspace(workspace_id) if target_selector then - hl.dsp.focus({ window = target_selector })() + dispatch(hl.dsp.focus({ window = target_selector })) end end end @@ -871,8 +875,8 @@ local function move_window_into_group(window, anchor) end for _, direction in ipairs(grouping_directions(window, anchor)) do - hl.dsp.focus({ window = selector })() - hl.dsp.window.move({ into_group = direction, window = selector })() + dispatch(hl.dsp.focus({ window = selector })) + dispatch(hl.dsp.window.move({ into_group = direction, window = selector })) local active = hl.get_active_window() if active and active.group and active.group.size and active.group.size > 1 then @@ -960,12 +964,12 @@ local function restore_workspace_tabbed_group() return end - hl.dsp.focus({ window = anchor_selector })() - hl.dsp.group.toggle({ window = anchor_selector })() + dispatch(hl.dsp.focus({ window = anchor_selector })) + dispatch(hl.dsp.group.toggle({ window = anchor_selector })) tabbed_workspace_groups[key] = nil set_layout(columns_layout) restore_tabbed_group_window_order(state, target_workspace_id) - hl.dsp.focus({ window = anchor_selector })() + dispatch(hl.dsp.focus({ window = anchor_selector })) schedule_nstack_count_update() end @@ -1014,8 +1018,8 @@ local function gather_workspace_into_tabbed_group() set_layout(columns_layout) - hl.dsp.focus({ window = anchor_selector })() - hl.dsp.group.toggle({ window = anchor_selector })() + dispatch(hl.dsp.focus({ window = anchor_selector })) + dispatch(hl.dsp.group.toggle({ window = anchor_selector })) local group_windows = {} for _, window in ipairs(candidates) do @@ -1037,8 +1041,8 @@ local function gather_workspace_into_tabbed_group() end if grouped_count <= 1 then - hl.dsp.focus({ window = anchor_selector })() - hl.dsp.group.toggle({ window = anchor_selector })() + dispatch(hl.dsp.focus({ window = anchor_selector })) + dispatch(hl.dsp.group.toggle({ window = anchor_selector })) notify_tabbed_group("Unable to group tiled windows") return elseif grouped_count < #candidates then @@ -1050,7 +1054,7 @@ local function gather_workspace_into_tabbed_group() order = original_order, windows = candidate_addresses, } - hl.dsp.focus({ window = anchor_selector })() + dispatch(hl.dsp.focus({ window = anchor_selector })) end local function force_columns_layout() @@ -1114,7 +1118,7 @@ local function enter_workspace_swap_mode() color = "rgba(edb443ff)", font_size = 13, }) - hl.dsp.submap("swap-workspace")() + dispatch(hl.dsp.submap("swap-workspace")) end local function focus_next_empty_workspace() @@ -1149,10 +1153,10 @@ local function move_window_to_monitor(direction, follow) end local original_monitor = hl.get_active_monitor() - hl.dsp.window.move({ monitor = direction, follow = follow, window = window_selector(window) })() + dispatch(hl.dsp.window.move({ monitor = direction, follow = follow, window = window_selector(window) })) if not follow and original_monitor then - hl.dsp.focus({ monitor = original_monitor })() + dispatch(hl.dsp.focus({ monitor = original_monitor })) end end @@ -1170,9 +1174,9 @@ local function move_window_to_empty_workspace_on_monitor(direction) return end - hl.dsp.focus({ monitor = target_monitor })() + dispatch(hl.dsp.focus({ monitor = target_monitor })) focus_workspace(workspace_id) - hl.dsp.focus({ monitor = original_monitor })() + dispatch(hl.dsp.focus({ monitor = original_monitor })) move_window_to_workspace(workspace_id, false, window) end @@ -1443,35 +1447,35 @@ local function apply_scratchpad_geometry(name, window, target_monitor) end local selector = window_selector(window) - hl.dsp.window.float({ action = "enable", window = selector })() - hl.dsp.window.tag({ tag = "+scratchpad", window = selector })() - hl.dsp.window.tag({ tag = "+scratchpad-" .. name, window = selector })() - hl.dsp.window.resize({ x = width, y = height, relative = false, window = selector })() - hl.dsp.window.move({ x = x, y = y, relative = false, window = selector })() + dispatch(hl.dsp.window.float({ action = "enable", window = selector })) + dispatch(hl.dsp.window.tag({ tag = "+scratchpad", window = selector })) + dispatch(hl.dsp.window.tag({ tag = "+scratchpad-" .. name, window = selector })) + dispatch(hl.dsp.window.resize({ x = width, y = height, relative = false, window = selector })) + dispatch(hl.dsp.window.move({ x = x, y = y, relative = false, window = selector })) if def.dropdown then - hl.dsp.window.set_prop({ prop = "border_size", value = "0", window = selector })() - hl.dsp.window.set_prop({ prop = "no_shadow", value = "1", window = selector })() + dispatch(hl.dsp.window.set_prop({ prop = "border_size", value = "0", window = selector })) + dispatch(hl.dsp.window.set_prop({ prop = "no_shadow", value = "1", window = selector })) end end local function float_active_window_preserving_tiled_geometry() local geometry = tiled_window_geometry(hl.get_active_window()) - hl.dsp.window.float({ action = "enable", window = geometry and geometry.selector or nil })() + dispatch(hl.dsp.window.float({ action = "enable", window = geometry and geometry.selector or nil })) if geometry then - hl.dsp.window.resize({ x = geometry.width, y = geometry.height, relative = false, window = geometry.selector })() - hl.dsp.window.move({ x = geometry.x, y = geometry.y, relative = false, window = geometry.selector })() + dispatch(hl.dsp.window.resize({ x = geometry.width, y = geometry.height, relative = false, window = geometry.selector })) + dispatch(hl.dsp.window.move({ x = geometry.x, y = geometry.y, relative = false, window = geometry.selector })) end return geometry end local function float_and_drag_active_window() float_active_window_preserving_tiled_geometry() - hl.dsp.window.drag()() + dispatch(hl.dsp.window.drag()) end local function float_and_resize_active_window() float_active_window_preserving_tiled_geometry() - hl.dsp.window.resize()() + dispatch(hl.dsp.window.resize()) end local function schedule_scratchpad_geometry(name, window, target_monitor) @@ -1493,7 +1497,7 @@ local function show_scratchpad_window(name, window, workspace, target_monitor) remove_minimized_window(window) move_window_to_workspace(workspace.id, false, window) - hl.dsp.focus({ window = window_selector(window) })() + dispatch(hl.dsp.focus({ window = window_selector(window) })) schedule_scratchpad_geometry(name, window, target_monitor or hl.get_active_monitor()) end @@ -1610,36 +1614,36 @@ local function activate_window_picker_candidate(index) local mode = window_picker_mode window_picker_mode = nil window_picker_candidates = {} - hl.dsp.submap("reset")() + dispatch(hl.dsp.submap("reset")) if not window then return end if mode == "go" then - hl.dsp.focus({ window = window_selector(window) })() + dispatch(hl.dsp.focus({ window = window_selector(window) })) return end local workspace = active_workspace() if mode == "bring" and workspace then move_window_to_workspace(workspace.id, false, window) - hl.dsp.focus({ window = window_selector(window) })() + dispatch(hl.dsp.focus({ window = window_selector(window) })) return end if mode == "minimized" and workspace then remove_minimized_window(window) restore_minimized_window(window, workspace) - hl.dsp.focus({ window = window_selector(window) })() + dispatch(hl.dsp.focus({ window = window_selector(window) })) return end if mode == "replace" then local focused = hl.get_active_window() if focused and focused ~= window then - hl.dsp.window.swap({ target = window_selector(window), window = window_selector(focused) })() - hl.dsp.focus({ window = window_selector(window) })() + dispatch(hl.dsp.window.swap({ target = window_selector(window), window = window_selector(focused) })) + dispatch(hl.dsp.focus({ window = window_selector(window) })) end end end @@ -1677,7 +1681,7 @@ local function enter_window_picker(mode) color = "rgba(edb443ff)", font_size = 11, }) - hl.dsp.submap("window-picker")() + dispatch(hl.dsp.submap("window-picker")) end local function gather_focused_class() @@ -1707,7 +1711,7 @@ end local function focus_next_class() local focused = hl.get_active_window() if not focused or not focused.class or focused.class == "" then - hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false })() + dispatch(hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false })) return end @@ -1736,7 +1740,7 @@ local function focus_next_class() local next_class = classes[(current_index % #classes) + 1] local target = first_by_class[next_class] if target then - hl.dsp.focus({ window = window_selector(target) })() + dispatch(hl.dsp.focus({ window = window_selector(target) })) end end @@ -1775,7 +1779,7 @@ local function raise_or_spawn(class_fragment, command) local fragment = string.lower(class_fragment) for _, window in ipairs(hl.get_windows()) do if is_normal_window(window) and window.class and string.find(string.lower(window.class), fragment, 1, true) then - hl.dsp.focus({ window = window_selector(window) })() + dispatch(hl.dsp.focus({ window = window_selector(window) })) return end end @@ -1805,7 +1809,7 @@ local function restore_last_minimized() local window = table.remove(minimized_windows) if window and window.address and is_minimized_window(window) then restore_minimized_window(window, workspace) - hl.dsp.focus({ window = window_selector(window) })() + dispatch(hl.dsp.focus({ window = window_selector(window) })) return end end @@ -2180,7 +2184,7 @@ hl.define_submap("swap-workspace", function() local workspace_id = i bind(tostring(i), function() swap_current_workspace_with(workspace_id) - hl.dsp.submap("reset")() + dispatch(hl.dsp.submap("reset")) end) end @@ -2282,8 +2286,8 @@ for i = 1, 9 do bind(main_mod .. " + " .. workspace, hl.dsp.focus({ workspace = workspace, on_current_monitor = true })) bind(main_mod .. " + SHIFT + " .. workspace, hl.dsp.window.move({ workspace = workspace, follow = false })) bind(main_mod .. " + CTRL + " .. workspace, function() - hl.dsp.window.move({ workspace = workspace, follow = false })() - hl.dsp.focus({ workspace = workspace, on_current_monitor = true })() + dispatch(hl.dsp.window.move({ workspace = workspace, follow = false })) + dispatch(hl.dsp.focus({ workspace = workspace, on_current_monitor = true })) end) end diff --git a/nixos/checks/hyprland-config-syntax/default.nix b/nixos/checks/hyprland-config-syntax/default.nix index 63467e0a..c5e1dadf 100644 --- a/nixos/checks/hyprland-config-syntax/default.nix +++ b/nixos/checks/hyprland-config-syntax/default.nix @@ -13,6 +13,11 @@ pkgs.runCommand "hyprland-config-syntax" { exit 1 fi + if grep -nE 'hl[.]dsp.*[)][(][)]' hyprland.lua; then + echo "hyprland.lua should use hl.dispatch(...) instead of calling dispatcher objects directly" >&2 + exit 1 + fi + lua ${./smoke-test.lua} ./hyprland.lua touch "$out" '' diff --git a/nixos/checks/hyprland-config-syntax/smoke-test.lua b/nixos/checks/hyprland-config-syntax/smoke-test.lua index f476d83b..724fc0d0 100644 --- a/nixos/checks/hyprland-config-syntax/smoke-test.lua +++ b/nixos/checks/hyprland-config-syntax/smoke-test.lua @@ -3,14 +3,18 @@ local callbacks = {} local function noop() end -local function dispatcher_proxy() - local proxy = {} - return setmetatable(proxy, { +local dispatcher = setmetatable({}, { + __call = function() + error("dispatcher objects cannot be called directly; use hl.dispatch(dispatcher)", 2) + end, +}) + +local function dispatcher_namespace() + return setmetatable({}, { __index = function() - return dispatcher_proxy() - end, - __call = function() - return noop + return function() + return dispatcher + end end, }) end @@ -48,6 +52,7 @@ hl = { bind = noop, config = noop, curve = noop, + dispatch = noop, env = noop, exec_cmd = noop, define_submap = function(_, reset_or_callback, callback) @@ -59,7 +64,17 @@ hl = { monitor = noop, workspace_rule = noop, window_rule = noop, - dsp = dispatcher_proxy(), + dsp = setmetatable({ + group = dispatcher_namespace(), + window = dispatcher_namespace(), + workspace = dispatcher_namespace(), + }, { + __index = function() + return function() + return dispatcher + end + end, + }), notification = { create = function() return notification From b8e6abd628e55daa8ad17249bfb081b3a23a444b Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 20:02:58 -0700 Subject: [PATCH 05/17] nixos: update host tmux colors --- nixos/machines/ryzen-shine.nix | 2 +- nixos/machines/strixi-minaj.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/machines/ryzen-shine.nix b/nixos/machines/ryzen-shine.nix index 9401f0d6..fd3fca62 100644 --- a/nixos/machines/ryzen-shine.nix +++ b/nixos/machines/ryzen-shine.nix @@ -49,7 +49,7 @@ networking.hostName = "ryzen-shine"; myModules.hostIdentity = { emoticon = "☀️"; - tmux.background = "#ea580c"; + tmux.background = "#2563eb"; }; environment.systemPackages = with pkgs; [ diff --git a/nixos/machines/strixi-minaj.nix b/nixos/machines/strixi-minaj.nix index b186883e..0f2ce457 100644 --- a/nixos/machines/strixi-minaj.nix +++ b/nixos/machines/strixi-minaj.nix @@ -104,7 +104,7 @@ networking.hostName = "strixi-minaj"; myModules.hostIdentity = { emoticon = "👩🏿"; - tmux.background = "#4f46e5"; + tmux.background = "#ea580c"; }; powerManagement.cpuFreqGovernor = lib.mkDefault "performance"; From 2d92e9d55dcdf438213eea79075721b821def927 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 20:48:05 -0700 Subject: [PATCH 06/17] Use WhiteSur ultrawide GRUB theme --- .../{grub-windows.nix => grub.nix} | 27 ++++++++++++++----- nixos/configuration.nix | 2 +- nixos/flake.lock | 21 +++++++++++++++ nixos/flake.nix | 4 +++ nixos/machines/ryzen-shine.nix | 11 +++++++- nixos/raspberry-pi.nix | 2 +- nixos/wsl.nix | 2 +- 7 files changed, 59 insertions(+), 10 deletions(-) rename nixos/bootloaders/{grub-windows.nix => grub.nix} (67%) diff --git a/nixos/bootloaders/grub-windows.nix b/nixos/bootloaders/grub.nix similarity index 67% rename from nixos/bootloaders/grub-windows.nix rename to nixos/bootloaders/grub.nix index 087ad849..152cf6c0 100644 --- a/nixos/bootloaders/grub-windows.nix +++ b/nixos/bootloaders/grub.nix @@ -3,11 +3,11 @@ lib, ... }: let - cfg = config.myModules.bootloaders.grubWindows; + cfg = config.myModules.bootloaders.grub; systemdBootCfg = config.myModules.bootloaders.systemdBoot; in { - options.myModules.bootloaders.grubWindows = { - enable = lib.mkEnableOption "GRUB with Windows chainloading support"; + options.myModules.bootloaders.grub = { + enable = lib.mkEnableOption "GRUB bootloader support"; configurationLimit = lib.mkOption { default = 5; @@ -24,6 +24,18 @@ in { entries discovered by os-prober. ''; }; + + theme = lib.mkOption { + default = null; + type = lib.types.nullOr lib.types.path; + description = "GRUB theme directory."; + }; + + gfxmode = lib.mkOption { + default = "auto"; + type = lib.types.str; + description = "GRUB graphical mode used for EFI and BIOS."; + }; }; config = lib.mkIf cfg.enable { @@ -31,14 +43,14 @@ in { { assertion = !systemdBootCfg.enable; message = '' - myModules.bootloaders.grubWindows.enable conflicts with + myModules.bootloaders.grub.enable conflicts with myModules.bootloaders.systemdBoot.enable. Disable systemdBoot before - enabling the GRUB Windows boot strategy. + enabling the GRUB boot strategy. ''; } { assertion = builtins.hasAttr "/boot" config.fileSystems; - message = "The GRUB Windows boot strategy expects an EFI filesystem mounted at /boot."; + message = "The GRUB boot strategy expects an EFI filesystem mounted at /boot."; } ]; @@ -55,6 +67,9 @@ in { useOSProber = true; configurationLimit = cfg.configurationLimit; timeoutStyle = "menu"; + theme = lib.mkIf (cfg.theme != null) (lib.mkDefault cfg.theme); + gfxmodeEfi = lib.mkDefault cfg.gfxmode; + gfxmodeBios = lib.mkDefault cfg.gfxmode; extraEntries = lib.optionalString (cfg.windowsEfiUuid != null) '' menuentry "Windows Boot Manager" { insmod part_gpt diff --git a/nixos/configuration.nix b/nixos/configuration.nix index 2bb02afe..5a8bbbe6 100644 --- a/nixos/configuration.nix +++ b/nixos/configuration.nix @@ -7,7 +7,7 @@ ./android.nix ./base.nix ./ben.nix - ./bootloaders/grub-windows.nix + ./bootloaders/grub.nix ./bootloaders/systemd-boot.nix ./cache-server.nix ./cache.nix diff --git a/nixos/flake.lock b/nixos/flake.lock index 38500f79..a97fcb7a 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -594,6 +594,26 @@ "type": "github" } }, + "grub2-themes": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1757136219, + "narHash": "sha256-tKU+vq34KHu/A2wD7WdgP5A4/RCmSD8hB0TyQAUlixA=", + "owner": "vinceliuice", + "repo": "grub2-themes", + "rev": "80dd04ddf3ba7b284a7b1a5df2b1e95ee2aad606", + "type": "github" + }, + "original": { + "owner": "vinceliuice", + "repo": "grub2-themes", + "type": "github" + } + }, "hercules-ci-effects": { "inputs": { "flake-parts": "flake-parts_3", @@ -1678,6 +1698,7 @@ "git-blame-rank": "git-blame-rank", "git-ignore-nix": "git-ignore-nix", "git-sync-rs": "git-sync-rs", + "grub2-themes": "grub2-themes", "home-manager": "home-manager", "hypr-workspace-history": "hypr-workspace-history", "hyprNStack": "hyprNStack", diff --git a/nixos/flake.nix b/nixos/flake.nix index fed1ad26..40666d65 100644 --- a/nixos/flake.nix +++ b/nixos/flake.nix @@ -236,6 +236,10 @@ }; }; + grub2-themes = { + url = "github:vinceliuice/grub2-themes"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = inputs @ { diff --git a/nixos/machines/ryzen-shine.nix b/nixos/machines/ryzen-shine.nix index fd3fca62..73c7a1d1 100644 --- a/nixos/machines/ryzen-shine.nix +++ b/nixos/machines/ryzen-shine.nix @@ -1,9 +1,11 @@ { + inputs, lib, pkgs, ... }: { imports = [ + inputs.grub2-themes.nixosModules.default ../configuration.nix ../nixified.ai.nix ]; @@ -41,9 +43,16 @@ boot.loader.systemd-boot.configurationLimit = 5; myModules.bootloaders.systemdBoot.enable = false; - myModules.bootloaders.grubWindows = { + myModules.bootloaders.grub = { enable = true; windowsEfiUuid = "B270-C7E6"; + gfxmode = "3440x1440,auto"; + }; + boot.loader.grub2-theme = { + enable = true; + theme = "whitesur"; + icon = "whitesur"; + screen = "ultrawide2k"; }; networking.hostName = "ryzen-shine"; diff --git a/nixos/raspberry-pi.nix b/nixos/raspberry-pi.nix index 746143f5..9a33b4b8 100644 --- a/nixos/raspberry-pi.nix +++ b/nixos/raspberry-pi.nix @@ -16,7 +16,7 @@ makeEnable config "myModules.raspberry-pi" false { hardware.raspberry-pi."4".fkms-3d.enable = true; # hardware.raspberry-pi."4".audio.enable = true; myModules.bootloaders.systemdBoot.enable = false; - myModules.bootloaders.grubWindows.enable = false; + myModules.bootloaders.grub.enable = false; boot = { initrd.systemd.tpm2.enable = false; diff --git a/nixos/wsl.nix b/nixos/wsl.nix index d2364d36..2ea05421 100644 --- a/nixos/wsl.nix +++ b/nixos/wsl.nix @@ -13,7 +13,7 @@ makeEnable config "myModules.wsl" false { myModules.base.enable = false; myModules.desktop.enable = false; myModules.bootloaders.systemdBoot.enable = false; - myModules.bootloaders.grubWindows.enable = false; + myModules.bootloaders.grub.enable = false; myModules.xmonad.enable = false; myModules.plasma.enable = false; # Tailscale-in-WSL is usually better handled by the Windows host Tailscale From d44736aec9d1164c94396700acc82a001b80b3ee Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 21:01:42 -0700 Subject: [PATCH 07/17] Add tmcodex resume launcher binding --- docs/tiling-wm-experience.md | 3 ++- dotfiles/config/hypr/hyprland.lua | 1 + dotfiles/config/xmonad/xmonad.hs | 1 + dotfiles/lib/bin/rofi_tmcodex.sh | 5 +++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/tiling-wm-experience.md b/docs/tiling-wm-experience.md index 54369a71..12893695 100644 --- a/docs/tiling-wm-experience.md +++ b/docs/tiling-wm-experience.md @@ -411,7 +411,8 @@ Required behavior: - `Hyper+p` opens the password picker with `rofi-pass`. - `Hyper+h` opens the screenshot tool with the compositor/session-appropriate screenshot command. -- `Hyper+c` opens a shell command prompt with `shell_command.sh`. +- `Hyper+c` opens the Codex launcher with `rofi_tmcodex.sh`. +- `Hyper+Shift+c` opens the Codex launcher with `tmcodex resume`. - `Hyper+k` opens the process killer with `rofi_kill_process.sh`. - `Hyper+Shift+k` opens the kill-all/process-tree killer with `rofi_kill_all.sh`. diff --git a/dotfiles/config/hypr/hyprland.lua b/dotfiles/config/hypr/hyprland.lua index 95c170b4..d2887172 100644 --- a/dotfiles/config/hypr/hyprland.lua +++ b/dotfiles/config/hypr/hyprland.lua @@ -2327,6 +2327,7 @@ bind(hyper .. " + V", exec([[cliphist list | rofi -dmenu -p "Clipboard" | cliphi bind(hyper .. " + P", exec("rofi-pass")) bind(hyper .. " + H", exec([[grim -g "$(slurp)" - | swappy -f -]])) bind(hyper .. " + C", exec("rofi_tmcodex.sh")) +bind(hyper .. " + SHIFT + C", exec("rofi_tmcodex.sh resume")) bind(hyper .. " + SHIFT + L", exec("hyprlock")) bind(hyper .. " + L", exec("hypr_rofi_layout")) bind(hyper .. " + K", exec("rofi_kill_process.sh")) diff --git a/dotfiles/config/xmonad/xmonad.hs b/dotfiles/config/xmonad/xmonad.hs index 6fdfec20..497cf568 100644 --- a/dotfiles/config/xmonad/xmonad.hs +++ b/dotfiles/config/xmonad/xmonad.hs @@ -1071,6 +1071,7 @@ addKeys conf@XConfig { modMask = modm } = , ((hyper, xK_p), spawn "rofi-pass") , ((hyper, xK_h), spawn "rofi_shutter") , ((hyper, xK_c), spawn "rofi_tmcodex.sh") + , ((hyper .|. shiftMask, xK_c), spawn "rofi_tmcodex.sh resume") , ((hyper .|. shiftMask, xK_l), spawn "dm-tool lock") , ((hyper, xK_l), selectLayout) , ((hyper, xK_k), spawn "rofi_kill_process.sh") diff --git a/dotfiles/lib/bin/rofi_tmcodex.sh b/dotfiles/lib/bin/rofi_tmcodex.sh index 815756f8..f84850ae 100755 --- a/dotfiles/lib/bin/rofi_tmcodex.sh +++ b/dotfiles/lib/bin/rofi_tmcodex.sh @@ -11,6 +11,7 @@ history_file="$state_dir/dirs" codex_home="${CODEX_HOME:-$HOME/.codex}" terminal="${TMCODEX_TERMINAL:-${TERMINAL:-ghostty}}" debug_log="$state_dir/debug.log" +tmcodex_args=("$@") mkdir -p "$state_dir" touch "$history_file" @@ -91,7 +92,7 @@ if [[ "${1:-}" == "--print-candidates" ]]; then exit 0 fi -debug "script=$0 codex_home=$codex_home history_file=$history_file terminal=$terminal" +debug "script=$0 codex_home=$codex_home history_file=$history_file terminal=$terminal args=${tmcodex_args[*]}" selected_dir="$( emit_candidates | dedup | existing_dirs | rofi -dmenu -i -p 'tmcodex dir' || true )" @@ -133,4 +134,4 @@ if (( ${#terminal_argv[@]} == 0 )); then exit 1 fi -("${terminal_argv[@]}" -e zsh -lc 'cd -- "$1" && exec tmcodex' zsh "$selected_dir" >/dev/null 2>&1 &!) +("${terminal_argv[@]}" -e zsh -lc 'cd -- "$1" && shift && exec tmcodex "$@"' zsh "$selected_dir" "${tmcodex_args[@]}" >/dev/null 2>&1 &!) From ad4b8c267e519d03740813826ba98120446427c7 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 22:16:38 -0700 Subject: [PATCH 08/17] Add pinned window indicators --- dotfiles/config/hypr/hyprland.lua | 33 +++++++++++++++++-- .../taffybar/TaffybarConfig/Workspaces.hs | 3 +- dotfiles/config/taffybar/flake.lock | 6 ++-- dotfiles/config/taffybar/taffybar | 2 +- dotfiles/config/taffybar/taffybar.css | 14 +++++++- nixos/flake.lock | 4 +-- nixos/flake.nix | 2 ++ 7 files changed, 53 insertions(+), 11 deletions(-) diff --git a/dotfiles/config/hypr/hyprland.lua b/dotfiles/config/hypr/hyprland.lua index d2887172..0999bcfe 100644 --- a/dotfiles/config/hypr/hyprland.lua +++ b/dotfiles/config/hypr/hyprland.lua @@ -1478,6 +1478,25 @@ local function float_and_resize_active_window() dispatch(hl.dsp.window.resize()) end +local function toggle_pinned_active_window() + local window = hl.get_active_window() + local selector = window_selector(window) + if not window or not selector then + return + end + + if window.pinned then + dispatch(hl.dsp.window.pin({ action = "disable", window = selector })) + dispatch(hl.dsp.window.float({ action = "disable", window = selector })) + return + end + + if not window.floating then + float_active_window_preserving_tiled_geometry() + end + dispatch(hl.dsp.window.pin({ action = "enable", window = selector })) +end + local function schedule_scratchpad_geometry(name, window, target_monitor) hl.timer(function() apply_scratchpad_geometry(name, window, target_monitor) @@ -1762,6 +1781,7 @@ local function show_active_window_info() "Class: " .. tostring(window.class or ""), "Title: " .. tostring(window.title or ""), "Workspace: " .. tostring(workspace), + "Pinned: " .. tostring(window.pinned or false), "Address: " .. tostring(window.address or ""), "PID: " .. tostring(window.pid or ""), } @@ -1943,10 +1963,10 @@ hl.config({ general = { gaps_in = 5, gaps_out = 10, - border_size = 0, + border_size = 2, col = { - active_border = { colors = { "rgba(edb443ee)", "rgba(33ccffee)" }, angle = 45 }, - inactive_border = "rgba(595959aa)", + active_border = { colors = { "rgba(3b82f6ee)", "rgba(33ccffee)" }, angle = 45 }, + inactive_border = "rgba(00000000)", }, layout = columns_layout, allow_tearing = false, @@ -2086,6 +2106,12 @@ local function apply_rules() decorate = false, no_shadow = true, }) + hl.window_rule({ + name = "subtle-pinned-window-border", + match = { pin = true }, + border_size = 2, + border_color = "rgba(edb443ff) rgba(ff4d5dcc)", + }) end bind(main_mod .. " + P", exec(launcher_command)) @@ -2232,6 +2258,7 @@ bind(main_mod .. " + CTRL + Space", gather_workspace_into_tabbed_group) bind(main_mod .. " + bracketright", monocle_next) bind(main_mod .. " + bracketleft", monocle_prev) bind(main_mod .. " + T", hl.dsp.window.float({ action = "disable" })) +bind(main_mod .. " + O", toggle_pinned_active_window) bind(main_mod .. " + M", minimize_active_window) bind(main_mod .. " + SHIFT + M", restore_last_minimized) bind(main_mod .. " + CTRL + SHIFT + M", function() diff --git a/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs b/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs index 370df5a5..059d6689 100644 --- a/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs +++ b/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs @@ -127,7 +127,8 @@ workspaceCandidateInfo name = WorkspaceModel.windowPosition = Nothing, WorkspaceModel.windowUrgent = False, WorkspaceModel.windowActive = False, - WorkspaceModel.windowMinimized = False + WorkspaceModel.windowMinimized = False, + WorkspaceModel.windowPinned = False } workspaceIconFromCandidate :: Int32 -> Text -> TaffyIO (Maybe Gdk.Pixbuf) diff --git a/dotfiles/config/taffybar/flake.lock b/dotfiles/config/taffybar/flake.lock index 6062ef96..a050b565 100644 --- a/dotfiles/config/taffybar/flake.lock +++ b/dotfiles/config/taffybar/flake.lock @@ -136,11 +136,11 @@ "xmonad-contrib": "xmonad-contrib" }, "locked": { - "lastModified": 1778147268, - "narHash": "sha256-q/1DIMXQEU4y95gAFacnrHwqxYbkpqwtpmVGTFmmTMo=", + "lastModified": 1778303085, + "narHash": "sha256-pDFWJ3BJ4jaZOi1hq7SHzRCdnII8PgvBNxcyoFgx4BE=", "owner": "taffybar", "repo": "taffybar", - "rev": "e52f00e8d6a0fe903dada5001778808d093eafb6", + "rev": "45b31b789fdfeff7251f0f77e1259ef4d1cc987a", "type": "github" }, "original": { diff --git a/dotfiles/config/taffybar/taffybar b/dotfiles/config/taffybar/taffybar index be3aa010..45b31b78 160000 --- a/dotfiles/config/taffybar/taffybar +++ b/dotfiles/config/taffybar/taffybar @@ -1 +1 @@ -Subproject commit be3aa010f7c3938a711e9191a7c0fae342ce6f45 +Subproject commit 45b31b789fdfeff7251f0f77e1259ef4d1cc987a diff --git a/dotfiles/config/taffybar/taffybar.css b/dotfiles/config/taffybar/taffybar.css index b4bef484..4eb35381 100644 --- a/dotfiles/config/taffybar/taffybar.css +++ b/dotfiles/config/taffybar/taffybar.css @@ -256,10 +256,22 @@ .workspaces .window-icon-container.active { background-color: rgba(255, 255, 255, 0.10); - border-color: rgba(255, 255, 255, 0.5); + border-color: rgba(59, 130, 246, 0.76); border-radius: 7px; } +.workspaces .window-icon-container.pinned { + border-color: rgba(255, 77, 93, 0.74); + box-shadow: inset 0 -2px 0 rgba(255, 77, 93, 0.72); +} + +.workspaces .window-icon-container.pinned.active { + border-color: rgba(237, 180, 67, 0.95); + box-shadow: + inset 0 -2px 0 rgba(237, 180, 67, 0.9), + 0 0 0 1px rgba(237, 180, 67, 0.22); +} + .workspaces .active .contents, .workspaces .visible .contents { background-color: transparent; diff --git a/nixos/flake.lock b/nixos/flake.lock index a97fcb7a..68bace72 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -1827,8 +1827,8 @@ ] }, "locked": { - "lastModified": 1778195259, - "narHash": "sha256-exYzNU6vVQg3z1v8ZNDLMjV8+GxbubiEqndT8rFzrm0=", + "lastModified": 1778303132, + "narHash": "sha256-P8W/cn7iiWbvrrkyv4LPENnsVbKbASK6+bLKZbxeIoA=", "path": "/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar", "type": "path" }, diff --git a/nixos/flake.nix b/nixos/flake.nix index 40666d65..0771b0a3 100644 --- a/nixos/flake.nix +++ b/nixos/flake.nix @@ -452,6 +452,7 @@ "https://cache.flox.dev" "https://org-agenda-api.cachix.org" "https://colonelpanic8-dotfiles.cachix.org" + "https://taffybar.cachix.org" "https://codex-cli.cachix.org" "https://claude-code.cachix.org" ]; @@ -473,6 +474,7 @@ "flox-cache-public-1:7F4OyH7ZCnFhcze3fJdfyXYLQw/aV7GEed86nQ7IsOs=" "org-agenda-api.cachix.org-1:liKFemKkOLV/rJt2txDNcpDjRsqLuBneBjkSw/UVXKA=" "colonelpanic8-dotfiles.cachix.org-1:O6GF3nptpeMFapX29okzO92eSWXR36zqW6ZF2C8P0eQ=" + "taffybar.cachix.org-1:beZotJ1nVEsAnJxa3lWn0zwzZM7oeXmGh4ADRpHeeIo=" "codex-cli.cachix.org-1:1Br3H1hHoRYG22n//cGKJOk3cQXgYobUel6O8DgSing=" "claude-code.cachix.org-1:YeXf2aNu7UTX8Vwrze0za1WEDS+4DuI2kVeWEE4fsRk=" ]; From 4a57e6f936c7255db47b5db5a3d915d216df5f98 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:12:06 -0700 Subject: [PATCH 09/17] zellij: add session switcher --- dotfiles/lib/bin/zellij_switch_session | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100755 dotfiles/lib/bin/zellij_switch_session diff --git a/dotfiles/lib/bin/zellij_switch_session b/dotfiles/lib/bin/zellij_switch_session new file mode 100755 index 00000000..b6f06619 --- /dev/null +++ b/dotfiles/lib/bin/zellij_switch_session @@ -0,0 +1,17 @@ +#!/usr/bin/env sh +set -eu + +if ! command -v sk >/dev/null 2>&1; then + echo "zellij_switch_session: sk is required" >&2 + exit 1 +fi + +selected=$( + zellij list-sessions --short --no-formatting | + sed '/^[[:space:]]*$/d' | + sk --prompt 'zellij session> ' --height 100% --reverse +) + +if [ -n "$selected" ]; then + zellij action switch-session "$selected" +fi From 46e3e7db5909f13931ebed04664f202940f371e2 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:12:32 -0700 Subject: [PATCH 10/17] nix: add elegant grub2 theme package --- nixos/nix.nix | 1 + .../packages/elegant-grub2-theme/default.nix | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 nixos/packages/elegant-grub2-theme/default.nix diff --git a/nixos/nix.nix b/nixos/nix.nix index 21c2869c..309c95f8 100644 --- a/nixos/nix.nix +++ b/nixos/nix.nix @@ -113,6 +113,7 @@ codex = inputs.codex-cli-nix.packages.${prev.stdenv.hostPlatform.system}.default; claude-code = inputs.claude-code-nix.packages.${prev.stdenv.hostPlatform.system}.default; git-sync-rs = inputs.git-sync-rs.packages.${prev.stdenv.hostPlatform.system}.default; + elegant-grub2-theme = final.callPackage ./packages/elegant-grub2-theme {}; kef = final.callPackage ./packages/kef {}; pykefcontrol = final.python3Packages.callPackage ./packages/pykefcontrol {}; roborock-control = final.callPackage ./packages/roborock-control {}; diff --git a/nixos/packages/elegant-grub2-theme/default.nix b/nixos/packages/elegant-grub2-theme/default.nix new file mode 100644 index 00000000..c00d9580 --- /dev/null +++ b/nixos/packages/elegant-grub2-theme/default.nix @@ -0,0 +1,86 @@ +{ + fetchFromGitHub, + imagemagick, + lib, + stdenvNoCC, + theme ? "wave", + style ? "window", + side ? "left", + color ? "dark", + screenVariant ? "1080p", + width ? null, + height ? null, +}: let + customResolution = width != null && height != null; + fontSizes = { + "1080p" = "16"; + "2k" = "24"; + "4k" = "32"; + }; + fontSize = builtins.getAttr screenVariant fontSizes; + themeName = + "Elegant-${theme}-${style}-${side}-${color}" + + lib.optionalString customResolution "-${width}x${height}"; +in + stdenvNoCC.mkDerivation { + pname = "elegant-grub2-theme"; + version = "2025-03-25"; + + src = fetchFromGitHub { + owner = "vinceliuice"; + repo = "Elegant-grub2-themes"; + rev = "2025-03-25"; + hash = "sha256-M9k6R/rUvEpBTSnZ2PMv5piV50rGTBrcmPU4gsS7Byg="; + }; + + nativeBuildInputs = lib.optional customResolution imagemagick; + + installPhase = + '' + runHook preInstall + + theme_dir="$out/share/grub/themes/${themeName}" + mkdir -p "$theme_dir" + + cp -a common/terminus*.pf2 "$theme_dir"/ + cp -a common/unifont-${fontSize}.pf2 "$theme_dir"/ + cp -a assets/assets-icons-${color}/icons-${color}-${screenVariant} "$theme_dir/icons" + cp -a config/theme-${style}-${side}-${color}-${screenVariant}.txt "$theme_dir/theme.txt" + cp -a assets/assets-other/other-${screenVariant}/select_e-${theme}-${color}.png "$theme_dir/select_e.png" + cp -a assets/assets-other/other-${screenVariant}/select_c-${theme}-${color}.png "$theme_dir/select_c.png" + cp -a assets/assets-other/other-${screenVariant}/select_w-${theme}-${color}.png "$theme_dir/select_w.png" + cp -a assets/assets-other/other-${screenVariant}/Default.png "$theme_dir/logo.png" + '' + + lib.optionalString customResolution '' + magick backgrounds/backgrounds-${theme}/background-${theme}-${style}-${side}-${color}.jpg \ + -resize ${width}x${height}^ \ + -gravity center \ + -extent ${width}x${height} \ + "$theme_dir/background.jpg" + magick assets/assets-other/other-${screenVariant}/${style}-${side}.png \ + -resize x${height} \ + -background none \ + -gravity west \ + -extent ${width}x${height} \ + "$theme_dir/info.png" + '' + + lib.optionalString (!customResolution) '' + cp -a backgrounds/backgrounds-${theme}/background-${theme}-${style}-${side}-${color}.jpg "$theme_dir/background.jpg" + cp -a assets/assets-other/other-${screenVariant}/${style}-${side}.png "$theme_dir/info.png" + '' + + '' + + runHook postInstall + ''; + + passthru = { + inherit themeName; + }; + + meta = { + description = "Elegant wave GRUB2 theme"; + homepage = "https://github.com/vinceliuice/Elegant-grub2-themes"; + license = lib.licenses.gpl3Only; + maintainers = []; + }; + } From 17f9f850732c5ea1904644831366cbf1189cfa22 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:15:46 -0700 Subject: [PATCH 11/17] taffybar: update tray animation --- dotfiles/config/taffybar/taffybar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotfiles/config/taffybar/taffybar b/dotfiles/config/taffybar/taffybar index 45b31b78..8ab1f339 160000 --- a/dotfiles/config/taffybar/taffybar +++ b/dotfiles/config/taffybar/taffybar @@ -1 +1 @@ -Subproject commit 45b31b789fdfeff7251f0f77e1259ef4d1cc987a +Subproject commit 8ab1f3399f71e48fd52dd209fc58b1691d7e72da From dd71d880f6aeba60d8f8c6cfc2e6fb846a75a375 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:15:58 -0700 Subject: [PATCH 12/17] nix: trust taffybar cachix --- nixos/nix.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nixos/nix.nix b/nixos/nix.nix index 309c95f8..3dc3bf92 100644 --- a/nixos/nix.nix +++ b/nixos/nix.nix @@ -70,6 +70,7 @@ "https://nix-community.cachix.org" "https://numtide.cachix.org" "https://colonelpanic8-dotfiles.cachix.org" + "https://taffybar.cachix.org" "https://codex-cli.cachix.org" "https://claude-code.cachix.org" ]; @@ -81,6 +82,7 @@ "https://nix-community.cachix.org" "https://numtide.cachix.org" "https://colonelpanic8-dotfiles.cachix.org" + "https://taffybar.cachix.org" "https://codex-cli.cachix.org" "https://claude-code.cachix.org" ]; @@ -91,6 +93,7 @@ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" "colonelpanic8-dotfiles.cachix.org-1:O6GF3nptpeMFapX29okzO92eSWXR36zqW6ZF2C8P0eQ=" + "taffybar.cachix.org-1:beZotJ1nVEsAnJxa3lWn0zwzZM7oeXmGh4ADRpHeeIo=" "codex-cli.cachix.org-1:1Br3H1hHoRYG22n//cGKJOk3cQXgYobUel6O8DgSing=" "claude-code.cachix.org-1:YeXf2aNu7UTX8Vwrze0za1WEDS+4DuI2kVeWEE4fsRk=" ]; From dbd58a9488e76184d0414ee6a3a11adab2417fa0 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:16:03 -0700 Subject: [PATCH 13/17] nixos: disable ryzen-shine gitea runner --- nixos/machines/ryzen-shine.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/machines/ryzen-shine.nix b/nixos/machines/ryzen-shine.nix index 73c7a1d1..1ab0d553 100644 --- a/nixos/machines/ryzen-shine.nix +++ b/nixos/machines/ryzen-shine.nix @@ -20,7 +20,7 @@ enable = true; port = 3090; }; - myModules.gitea-runner.enable = true; + myModules.gitea-runner.enable = false; myModules.postgres.enable = true; myModules.tts.enable = true; myModules.cua = { From e07d738857713638c2a4ece92eeb74b8fa1c3e5b Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:16:07 -0700 Subject: [PATCH 14/17] emacs: enable doom modeline on startup --- dotfiles/emacs.d/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotfiles/emacs.d/README.org b/dotfiles/emacs.d/README.org index b7f2c9b8..4e2b1809 100644 --- a/dotfiles/emacs.d/README.org +++ b/dotfiles/emacs.d/README.org @@ -4106,7 +4106,7 @@ Ensure all themes that I use are installed: :custom (doom-modeline-height 40)) -(defvar imalison:enable-doom-modeline-on-startup nil +(defvar imalison:enable-doom-modeline-on-startup t "Non-nil means enable `doom-modeline-mode' during startup.") #+end_src ** page-break-lines From 1c5dc8a0c732ad017ab6281fb6b50fd764522e98 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:16:13 -0700 Subject: [PATCH 15/17] flake: update hyprland inputs --- nixos/flake.lock | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/nixos/flake.lock b/nixos/flake.lock index 68bace72..2c380d70 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -47,11 +47,11 @@ ] }, "locked": { - "lastModified": 1776876344, - "narHash": "sha256-Ubqb/agkuMJK+k19gjQgHux/eOYRc1sRGoOZOho8+VY=", + "lastModified": 1777499565, + "narHash": "sha256-nU55VWk99Pn1QzQDDjFISocC4SgDZ3Xp+zb6ji3JclM=", "owner": "hyprwm", "repo": "aquamarine", - "rev": "648a13d0ee1e03a843b3e145b8ece15393058701", + "rev": "813c1e8981893c11e118b19c125d6bc282f51765", "type": "github" }, "original": { @@ -803,11 +803,11 @@ "xdph": "xdph" }, "locked": { - "lastModified": 1777497195, - "narHash": "sha256-I9z3somAUJ5jGwv4ZBjADco8CmdglEUqYs0AgJ5LSDs=", + "lastModified": 1778199997, + "narHash": "sha256-L+jaxpUg7l8dThBsuXddzd5rv1QKMiKLENuwM/aG3aA=", "ref": "refs/heads/main", - "rev": "56d7a43102b53f79a2311662783d5dd94cb2f1a5", - "revCount": 7215, + "rev": "de9f8dc9831d921cd1ee30d5d14f45f0e345a8ca", + "revCount": 7275, "submodules": true, "type": "git", "url": "https://github.com/hyprwm/Hyprland" @@ -936,11 +936,11 @@ ] }, "locked": { - "lastModified": 1776426736, - "narHash": "sha256-rl7i4aY+9p8LysJp7o8uRWahCkpFznCgGHXszlTw7b0=", + "lastModified": 1777320127, + "narHash": "sha256-Qu+Wf2Bp5qUjyn2YpZNq8a7JyzTGowhT1knrwE38a9U=", "owner": "hyprwm", "repo": "hyprlang", - "rev": "7833ff33b2e82d3406337b5dcf0d1cec595d83e9", + "rev": "090117506ddc3d7f26e650ff344d378c2ec329cc", "type": "github" }, "original": { @@ -1034,11 +1034,11 @@ ] }, "locked": { - "lastModified": 1777492286, - "narHash": "sha256-PwuoEJQcjSKJNP5T55qhfDwIP0tw5zxEhfu8GDfKfeg=", + "lastModified": 1778179779, + "narHash": "sha256-Ri6rVf54CRD3aISHLhSY6H4tBScVjm9ebkv7rF2lcZM=", "owner": "hyprwm", "repo": "hyprutils", - "rev": "ec5c0c709706bad5b82f667fd8758eae442577ce", + "rev": "3e170e5ad010602671f5f25b327e8bdb8fdd532c", "type": "github" }, "original": { @@ -1059,11 +1059,11 @@ ] }, "locked": { - "lastModified": 1777148232, - "narHash": "sha256-Uv0WZLhu89SafuSOmYDA7akrPt4wBRmsa1ucasO5aXg=", + "lastModified": 1777159683, + "narHash": "sha256-Jxixw6wZphUp+nHYxOKUYSckL17QMBx2d5Zp0rJHr1g=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "fec9cf1abcc1011e46f0a0986f46bf93c6bf8b92", + "rev": "b8632713a6beaf28b56f2a7b0ab2fb7088dbb404", "type": "github" }, "original": { @@ -1118,11 +1118,11 @@ ] }, "locked": { - "lastModified": 1776728575, - "narHash": "sha256-z9eGphrArEBpl1O/GCH0wlY6z4K9vA6yWh2gAS6qytU=", + "lastModified": 1777388329, + "narHash": "sha256-40YxVGF2rA9iH3D7am5fy4EOSBbMgpJtJ9yhl0Cx+qI=", "owner": "hyprwm", "repo": "hyprwire", - "rev": "f3a80888783702a39691b684d099e16b83ed4702", + "rev": "04be2897e05f9b271d532b5ae56ca088d2eeac02", "type": "github" }, "original": { @@ -1385,11 +1385,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1776877367, - "narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=", + "lastModified": 1777954456, + "narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0726a0ecb6d4e08f6adced58726b95db924cef57", + "rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", "type": "github" }, "original": { @@ -1910,11 +1910,11 @@ ] }, "locked": { - "lastModified": 1777035886, - "narHash": "sha256-m1TNuBoSXUBSKhD9UVMkU90M0wFTPTfvIOOltO8IM8A=", + "lastModified": 1777585783, + "narHash": "sha256-JTeWRy42VElroJ0rVdZuVXSoTLsx+NzQfGPKMbtn3SU=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "ecfcdcc781f48821d83e1e2a0e30d7beca0eeb5e", + "rev": "fa50d6fbaff8f42c61071b87b034a90d82a33558", "type": "github" }, "original": { From f56cb6ac428d4ce9340832fef3fe1463644df9df Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:36:50 -0700 Subject: [PATCH 16/17] nixos: use grub on strixi-minaj --- nixos/machines/strixi-minaj.nix | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/nixos/machines/strixi-minaj.nix b/nixos/machines/strixi-minaj.nix index 0f2ce457..c751a56d 100644 --- a/nixos/machines/strixi-minaj.nix +++ b/nixos/machines/strixi-minaj.nix @@ -7,6 +7,7 @@ }: { imports = [ ../configuration.nix + inputs.grub2-themes.nixosModules.default inputs.nixos-hardware.nixosModules.asus-rog-strix-g834jzr ]; @@ -30,7 +31,19 @@ # nixpkgs.config.cudaSupport = true; - boot.loader.systemd-boot.configurationLimit = 5; + myModules.bootloaders.systemdBoot.enable = false; + myModules.bootloaders.grub = { + enable = true; + configurationLimit = 5; + gfxmode = "2560x1600,auto"; + }; + boot.loader.grub2-theme = { + enable = true; + theme = "whitesur"; + icon = "whitesur"; + screen = "2k"; + customResolution = "2560x1600"; + }; environment.systemPackages = with pkgs; [ android-studio @@ -73,9 +86,6 @@ # PRIME sync hybrid mode to keep the compositor and displays on NVIDIA. hardware.nvidia.prime.offload.enable = lib.mkForce false; hardware.nvidia.prime.sync.enable = lib.mkForce false; - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - services.asusd.enable = true; services.supergfxd.settings = { mode = "AsusMuxDgpu"; From ab22bd551d9093eb2da47ed08f2ed6a99e02bc29 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 8 May 2026 23:38:35 -0700 Subject: [PATCH 17/17] chore: add remaining local artifacts --- dotfiles/config/zellij/config.kdl.bak | 24 +++++++++++++++++ .../console-2026-05-08T10-57-35-049Z.log | 1 + .../page-2026-05-08T10-57-16-545Z.yml | 26 +++++++++++++++++++ .../page-2026-05-08T10-57-35-450Z.yml | 9 +++++++ .../page-2026-05-08T10-57-42-387Z.yml | 9 +++++++ nixos/sqlite3 | 0 6 files changed, 69 insertions(+) create mode 100644 dotfiles/config/zellij/config.kdl.bak create mode 100644 nixos/.playwright-cli/console-2026-05-08T10-57-35-049Z.log create mode 100644 nixos/.playwright-cli/page-2026-05-08T10-57-16-545Z.yml create mode 100644 nixos/.playwright-cli/page-2026-05-08T10-57-35-450Z.yml create mode 100644 nixos/.playwright-cli/page-2026-05-08T10-57-42-387Z.yml create mode 100644 nixos/sqlite3 diff --git a/dotfiles/config/zellij/config.kdl.bak b/dotfiles/config/zellij/config.kdl.bak new file mode 100644 index 00000000..aeb8bd1c --- /dev/null +++ b/dotfiles/config/zellij/config.kdl.bak @@ -0,0 +1,24 @@ +keybinds { + // Keep Ctrl-p available for readline/history/up in shells and editors. + unbind "Ctrl p" + + shared_except "locked" "pane" { + bind "Ctrl Space" { SwitchToMode "Pane"; } + } + + pane { + bind "Ctrl Space" { SwitchToMode "Normal"; } + } + + tmux { + // Ctrl-b C: start a Codex pane from the current zellij tab. + bind "C" { + Run "codex" "--dangerously-bypass-approvals-and-sandbox" { + name "codex" + } + SwitchToMode "Normal" + } + } +} + +default_mode "locked" diff --git a/nixos/.playwright-cli/console-2026-05-08T10-57-35-049Z.log b/nixos/.playwright-cli/console-2026-05-08T10-57-35-049Z.log new file mode 100644 index 00000000..dbd93103 --- /dev/null +++ b/nixos/.playwright-cli/console-2026-05-08T10-57-35-049Z.log @@ -0,0 +1 @@ +[ 305ms] [ERROR] Failed to load resource: the server responded with a status of 403 () @ https://www.reddit.com/r/hyprland/comments/1t74dt6/pre055_discussion_share_your_new_lua_scripts_that/?solution=c0ac3501a8dec997c0ac3501a8dec997&js_challenge=1&token=bbbe4bf1c9a2b5160829c4be34da586130c27f161a9c565bc73f58c2dec5bfa9&jsc_orig_r=:0 diff --git a/nixos/.playwright-cli/page-2026-05-08T10-57-16-545Z.yml b/nixos/.playwright-cli/page-2026-05-08T10-57-16-545Z.yml new file mode 100644 index 00000000..36339728 --- /dev/null +++ b/nixos/.playwright-cli/page-2026-05-08T10-57-16-545Z.yml @@ -0,0 +1,26 @@ +- generic [active] [ref=e1]: + - link [ref=e3] [cursor=pointer]: + - /url: https://www.reddit.com + - img [ref=e4] + - generic [ref=e5]: + - img [ref=e6] + - heading "Prove your humanity" [level=1] [ref=e8] + - paragraph [ref=e9]: We’re committed to safety and security. But not for bots. Complete the challenge below and let us know you’re a real person. + - iframe [ref=e14]: + - generic [ref=f1e2]: + - generic [ref=f1e3]: + - checkbox "I'm not a robot" [ref=f1e7] + - generic [ref=f1e11]: I'm not a robot + - generic [ref=f1e15]: reCAPTCHA + - generic [ref=e15]: + - link "Reddit, Inc. © \"2026\". All rights reserved." [ref=e16] [cursor=pointer]: + - /url: https://www.redditinc.com/ + - generic [ref=e17]: + - link "User Agreement" [ref=e18] [cursor=pointer]: + - /url: https://www.reddit.com/help/useragreement + - link "Privacy Policy" [ref=e19] [cursor=pointer]: + - /url: https://www.reddit.com/help/privacypolicy + - link "Content Policy" [ref=e20] [cursor=pointer]: + - /url: https://www.reddit.com/help/contentpolicy + - link "Help" [ref=e21] [cursor=pointer]: + - /url: https://support.reddithelp.com/hc/en-us \ No newline at end of file diff --git a/nixos/.playwright-cli/page-2026-05-08T10-57-35-450Z.yml b/nixos/.playwright-cli/page-2026-05-08T10-57-35-450Z.yml new file mode 100644 index 00000000..a737f3ab --- /dev/null +++ b/nixos/.playwright-cli/page-2026-05-08T10-57-35-450Z.yml @@ -0,0 +1,9 @@ +- generic [ref=e3]: + - img [ref=e5] + - generic [ref=e7]: + - generic [ref=e8]: You've been blocked by network security. + - generic [ref=e10]: + - text: If you think you've been blocked by mistake, file a ticket below and we'll look into it. + - link "File a ticket" [ref=e12] [cursor=pointer]: + - /url: https://support.reddithelp.com/hc/en-us/requests/new?ticket_form_id=21879292693140 + - generic [ref=e14]: File a ticket \ No newline at end of file diff --git a/nixos/.playwright-cli/page-2026-05-08T10-57-42-387Z.yml b/nixos/.playwright-cli/page-2026-05-08T10-57-42-387Z.yml new file mode 100644 index 00000000..a737f3ab --- /dev/null +++ b/nixos/.playwright-cli/page-2026-05-08T10-57-42-387Z.yml @@ -0,0 +1,9 @@ +- generic [ref=e3]: + - img [ref=e5] + - generic [ref=e7]: + - generic [ref=e8]: You've been blocked by network security. + - generic [ref=e10]: + - text: If you think you've been blocked by mistake, file a ticket below and we'll look into it. + - link "File a ticket" [ref=e12] [cursor=pointer]: + - /url: https://support.reddithelp.com/hc/en-us/requests/new?ticket_form_id=21879292693140 + - generic [ref=e14]: File a ticket \ No newline at end of file diff --git a/nixos/sqlite3 b/nixos/sqlite3 new file mode 100644 index 00000000..e69de29b