From 2c8639ffbdab557af084642065f8c2cd73d830ed Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 23 Jul 2026 21:03:43 -0700 Subject: [PATCH] Persist workspace layout toggles across Hyprland reloads Co-Authored-By: Claude Fable 5 --- bin/omarchy-hyprland-workspace-layout-toggle | 8 ++- default/hypr/toggles.lua | 2 + default/hypr/workspace-layouts.lua | 8 +++ .../shell.d/hyprland-workspace-layout-test.sh | 69 +++++++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 default/hypr/workspace-layouts.lua create mode 100755 test/shell.d/hyprland-workspace-layout-test.sh diff --git a/bin/omarchy-hyprland-workspace-layout-toggle b/bin/omarchy-hyprland-workspace-layout-toggle index 28f76337..29b0c10e 100755 --- a/bin/omarchy-hyprland-workspace-layout-toggle +++ b/bin/omarchy-hyprland-workspace-layout-toggle @@ -3,13 +3,19 @@ # omarchy:summary=Toggle the layout on the current active workspace between dwindle and scrolling ACTIVE_WORKSPACE=$(hyprctl activeworkspace -j | jq -r '.id') +[[ $ACTIVE_WORKSPACE =~ ^-?[0-9]+$ ]] || exit 1 CURRENT_LAYOUT=$(hyprctl activeworkspace -j | jq -r '.tiledLayout') +LAYOUTS_DIR="$HOME/.local/state/omarchy/workspace-layouts" +LAYOUT_FILE="$LAYOUTS_DIR/$ACTIVE_WORKSPACE.lua" case "$CURRENT_LAYOUT" in dwindle) NEW_LAYOUT=scrolling ;; *) NEW_LAYOUT=dwindle ;; esac +mkdir -p "$LAYOUTS_DIR" +printf 'hl.workspace_rule({ workspace = "%s", layout = "%s" })\n' "$ACTIVE_WORKSPACE" "$NEW_LAYOUT" >"$LAYOUT_FILE" + hyprctl eval "hl.workspace_rule({ workspace = \"$ACTIVE_WORKSPACE\", layout = \"$NEW_LAYOUT\" })" >/dev/null 2>&1 || \ - hyprctl keyword workspace $ACTIVE_WORKSPACE, layout:$NEW_LAYOUT + hyprctl keyword workspace "$ACTIVE_WORKSPACE, layout:$NEW_LAYOUT" omarchy-notification-send -g 󱂬 "Workspace layout set to $NEW_LAYOUT" diff --git a/default/hypr/toggles.lua b/default/hypr/toggles.lua index 5f24e1cf..1ca62205 100644 --- a/default/hypr/toggles.lua +++ b/default/hypr/toggles.lua @@ -5,3 +5,5 @@ local toggles_dir = paths.state_home .. "/omarchy/toggles/hypr" package.path = toggles_dir .. "/?.lua;" .. package.path require_all.files(toggles_dir, nil, { reload = true }) + +require("default.hypr.workspace-layouts") diff --git a/default/hypr/workspace-layouts.lua b/default/hypr/workspace-layouts.lua new file mode 100644 index 00000000..6c33184e --- /dev/null +++ b/default/hypr/workspace-layouts.lua @@ -0,0 +1,8 @@ +-- Restore workspace layouts saved by omarchy-hyprland-workspace-layout-toggle. + +local paths = require("default.hypr.paths") +local require_all = require("default.hypr.require_all") + +local layouts_dir = paths.state_home .. "/omarchy/workspace-layouts" + +require_all.files(layouts_dir, "omarchy.workspace-layouts", { reload = true }) diff --git a/test/shell.d/hyprland-workspace-layout-test.sh b/test/shell.d/hyprland-workspace-layout-test.sh new file mode 100755 index 00000000..ef8b72fe --- /dev/null +++ b/test/shell.d/hyprland-workspace-layout-test.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh" + +require_command lua + +tmpdir=$(mktemp -d) +trap 'rm -rf "$tmpdir"' EXIT + +stub_dir="$tmpdir/bin" +home_dir="$tmpdir/home" +log_file="$tmpdir/hyprctl.log" +mkdir -p "$stub_dir" "$home_dir" + +cat >"$stub_dir/hyprctl" <<'EOF' +#!/bin/bash + +if [[ $1 == "activeworkspace" && -n $HYPRCTL_BROKEN ]]; then + printf '{}\n' +elif [[ $1 == "activeworkspace" ]]; then + printf '{"id":3,"tiledLayout":"dwindle"}\n' +else + printf '%s\n' "$*" >>"$HYPRCTL_LOG" +fi +EOF +chmod +x "$stub_dir/hyprctl" + +cat >"$stub_dir/omarchy-notification-send" <<'EOF' +#!/bin/bash +: +EOF +chmod +x "$stub_dir/omarchy-notification-send" + +HOME="$home_dir" HYPRCTL_LOG="$log_file" PATH="$stub_dir:$PATH" \ + "$ROOT/bin/omarchy-hyprland-workspace-layout-toggle" + +layout_file="$home_dir/.local/state/omarchy/workspace-layouts/3.lua" +[[ -f $layout_file ]] || fail "workspace layout toggle saves a workspace rule" +grep -Fx 'hl.workspace_rule({ workspace = "3", layout = "scrolling" })' "$layout_file" >/dev/null || + fail "workspace layout toggle saves the selected layout" +grep -Fx 'eval hl.workspace_rule({ workspace = "3", layout = "scrolling" })' "$log_file" >/dev/null || + fail "workspace layout toggle applies the selected layout immediately" +pass "workspace layout toggle persists and applies the selected layout" + +if HOME="$home_dir" HYPRCTL_LOG="$log_file" HYPRCTL_BROKEN=1 PATH="$stub_dir:$PATH" \ + "$ROOT/bin/omarchy-hyprland-workspace-layout-toggle" 2>/dev/null; then + fail "workspace layout toggle exits nonzero without a workspace id" +fi +[[ -f "$home_dir/.local/state/omarchy/workspace-layouts/null.lua" ]] && + fail "workspace layout toggle does not persist a rule without a workspace id" +pass "workspace layout toggle ignores broken hyprctl output" + +HOME="$home_dir" OMARCHY_PATH="$ROOT" lua <<'LUA' +local rules = {} + +hl = { + workspace_rule = function(rule) + table.insert(rules, rule) + end, +} + +dofile(os.getenv("OMARCHY_PATH") .. "/default/hypr/bootstrap.lua") +require("default.hypr.workspace-layouts") + +assert(#rules == 1) +assert(rules[1].workspace == "3") +assert(rules[1].layout == "scrolling") +LUA +pass "saved workspace layouts load into Hyprland configuration"