Omahub
← All plugins
S

Layout Presets

by Sridhar Katakam

Preset window layouts: columns, optionally with stacked windows

Security review

No obvious issues detected

Deterministic scan — not a security guarantee

None
Risk level
None
Analyzed commit
ae6b3c8
Scanned
1 month ago

No potentially dangerous behavior detected in the analyzed commit.

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

None
AI risk level
None
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
ae6b3c8
Reviewed
1 month ago

The plugin is a straightforward window-layout manager: it parses user-defined layout strings and applies them through hyprctl, with no network access, persistence, obfuscation, or destructive commands. The QML and Python helper code are clear and match the documented behavior, and the deterministic scan found no issues.

How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/srikat/omarchy-layout-presets --enable
Desktop #Hyprland #bar #workspaces

Layout Presets

An Omarchy shell plugin. A bar icon <img src="bar-icon.png" width="18" alt="the bar icon: a rectangle split into two columns"> opens a panel of preset layouts; picking one arranges every tiled window on the focused workspace to match - columns of the widths you chose, windows stacked inside a column, or rows of windows across the screen.

Each row previews the arrangement it produces and is labelled with the split it will actually land on, so the panel shows what you will get rather than what the preset nominally says. Sizes come out exact to the pixel.

<img src="panel.png" alt="The Layout Presets panel: a header naming the workspace's window count and layout, a COLUMNS section and a STACKED section of preset rows each with a proportional preview, and a footer naming the mouse and keyboard shortcuts." width="330">

FAQ

How do I put STACKED above COLUMNS? Lead your preset list with a stacked layout. Whichever kind comes first is the section shown first, everywhere.

omarchy bar set sridhar.layout-presets presets \
  "25(50/50)-50-25, 50(50-50)/50(33-33-33), 50-50, 25-75-25, 25-25-25-25"

How do I change which presets are offered? Same setting. The list is also the cycle order, so trimming it shortens the panel and what SUPER+ALT+L steps through. See Layouts for the syntax.

Where did the STACKED section go? You are on a workspace tiling with scrolling, which cannot hold a stacked column, so those presets are not offered there. The panel header names the layout it is looking at. See Layouts other than dwindle.

A preset vanished from the panel. Two layouts that come out identical at your current window count are shown once. At three windows 33-33-33 and 25-25-25-25 are both thirds, so only the first is listed. Open another window and it comes back.

Why don't the numbers match what I typed? The label is the split you will actually land on, not the preset's raw weights, and it follows the window count. 25-75-25 reads 20 · 60 · 20 on three windows and 20 · 60 · 10 · 10 on four.

I clicked a preset and nothing happened. The workspace was already in that layout, so there was nothing to move. The bar icon pulses on every apply, so you can tell the click landed.

Can I set the width of a single window? Yes, but not with a preset - a lone tiled window has no sibling to resize against. The panel swaps to a width slider when a workspace has one window. See Single-window workspaces.

My single-window width reset. It rides on a global Hyprland option that reverts to whatever looknfeel.lua says on any config reload.

How do I bind cycling to a key? Two lines in ~/.config/hypr/bindings.lua; the panel footer then reads your binding back out of the config. See Command line and keybindings.

How do I move the icon in the bar?

omarchy bar move sridhar.layout-presets --section left

I edited the QML and nothing changed. Run omarchy restart shell. The shell logs a hot-reload line for local plugins but keeps rendering the old code. Editing bin/omarchy-layout-presets needs no restart.

How do I update or remove it?

omarchy plugin update sridhar.layout-presets
omarchy plugin remove sridhar.layout-presets

Layouts

A layout is a list of relative column widths. The numbers are normalised, so 25-75-25 and 20-60-20 mean the same thing - a wide middle column flanked by two narrow ones.

A group can hold more than one window, written as the split inside parentheses. 25(50/50)-50-25 is four windows in three columns: two of equal height in the narrow left column, one large middle window, one tall right window.

The top-level separator picks the axis. - lays the groups out side by side, / lays them one above the other, and the parentheses always split the other way. So 50(50-50)/50(33-33-33) is two rows of equal height: two windows across the top, three across the bottom. Write a layout without spaces.

25-75-25 three columns, wide middle
25-25-25-25 four equal columns
25(50/50)-50-25 two stacked left, large middle, tall right
25-50(50/50)-25 two stacked in the middle
50(50/50)-50(50/50) a 2×2 grid
70-30(33/33/33) one large window, three stacked beside it
50(50-50)/50(33-33-33) two across the top, three across the bottom
50(33-33-33)/50(50-50) three across the top, two across the bottom
50(50-50)/50(25-25-25-25) two across the top, four across the bottom
60/40(33-33-33) a master on top, three across the bottom
70/30(25-25-25-25) a tall master, a filmstrip of four beneath
50/50 one above the other, full width

Row-major layouts are the arrangements a column-major one cannot reach: with two windows above three, no vertical split runs the full height, so it is not a row of columns however the columns are subdivided. The reverse also holds - a uniform grid is reachable either way round, so 50(50/50)-50(50/50) and its row-major spelling are the same layout, and the panel shows only one of them.

One thing to know when writing your own: surplus windows subdivide a layout's last slot rather than being spread evenly, so 50/50(50-50) on five windows gives a bottom row of 50/17/17/17, not four equal windows. Write the layout for the window count you actually run - 50/50(25-25-25-25) for five - and check the preview, which draws the real proportions where the label does not.

The panel groups the plain rows of columns under COLUMNS and everything else under STACKED. Which section comes first follows your list: whichever kind you write first is the kind shown first. So to put STACKED on top, lead with a stacked layout:

omarchy bar set sridhar.layout-presets presets \
  "25(50/50)-50-25, 50(50-50)/50(33-33-33), 50-50, 25-75-25, 25-25-25-25"

The order applies everywhere, not just on the workspace you set it from, and it deliberately does not depend on the workspace's layout: the panel's order is also the cycle order, and a list that reshuffled per workspace would move every row out from under your muscle memory.

Trimming the list is worth doing at the same time. It shortens the panel and the cycle together, so the layouts you actually use are the only ones SUPER+ALT+L steps through.

The panel header names the workspace it is about: how many tiled windows it holds and which layout it tiles with (4 WINDOWS · DWINDLE LAYOUT). The layout is not trivia there - it is what decides whether the stacked presets are offered at all. Edit the set in ~/.config/omarchy/shell.json, on this plugin's bar entry:

{ "id": "sridhar.layout-presets", "presets": "50-50, 25-75-25, 25(50/50)-50-25" }

When the workspace has a different number of windows than the layout has slots, it stretches to fit. Fewer windows use the leading slots. More windows grow extra columns out of a flat layout, and extra rows in the last column of a stacked one - the direction that layout was already going. Every window always gets a slot.

Because several layouts can stretch to the same thing, the panel hides the duplicates: at three windows 33-33-33 and 25-25-25-25 are both thirds, so only the first is listed. The list is therefore shorter on workspaces with fewer windows - every row you see does something different. A layout hidden this way is still applied by its twin, and the keybinding for it still works.

Both halves of a panel row describe the stretched result rather than the layout's raw weights, so what you read is what you get. The label is the split as percentages of the workspace, with ×N marking a column holding N stacked windows, and the preview beside it draws the same thing to scale. 25-75-25 on three windows reads 20 · 60 · 20; on four it reads 20 · 60 · 10 · 10.

Interactions

Where Action
Bar icon, left click Open the panel
Bar icon, right click Step to the next layout (the icon pulses to acknowledge)
Bar icon, middle click Step to the previous layout
Bar icon, scroll Cycle - up for next, down for previous
Panel, click a row Apply that layout
Panel, ↑/↓ + Enter Apply the selected layout
Panel, Esc Close
SUPER+ALT+L / +SHIFT Cycle forward / back (whatever you have bound)

The panel marks the layout the workspace is currently sitting at, so the active arrangement is visible without remembering what was last clicked. Applying a layout the workspace is already in is a no-op by design - nothing on screen moves - so the bar icon pulses on every apply.

The mouse shortcuts cycle rather than re-apply: re-applying the layout already in effect is the one press guaranteed to do nothing, which reads as a broken button. Cycling from the icon and cycling from the keybinding are the same action, so they agree on where you are in the list.

Command line and keybindings

The same action is available over shell IPC, which is what a Hyprland binding should call:

omarchy-shell layouts apply 25-75-25
omarchy-shell layouts apply "25(50/50)-50-25"   # quote it: the shell eats ( )
omarchy-shell layouts next                      # step to the next layout
omarchy-shell layouts previous
omarchy-shell layouts toggle                    # open/close the panel

The panel footer names both shortcuts: the layout right-click will apply, and the combo bound to cycling. The combo is read out of ~/.config/hypr/*.lua at status time, not hardcoded, so it follows a rebind and the line is simply absent when nothing is bound. (hyprctl binds cannot answer this - under the Lua config every bind compiles to an opaque __lua reference with no command text, so the config files are the only place the combo and its command still appear together.)

next and previous step through the layouts offered on the focused workspace, without opening the panel. They start from the layout the workspace is actually in rather than the one last picked, so the cycle follows what is on screen; layouts hidden as duplicates at the current window count are skipped, and on a scrolling workspace the stacked ones are left out. The cycle is only ever as long as there are distinct layouts to reach.

-- ~/.config/hypr/bindings.lua
o.bind("SUPER + ALT + L", "Next layout preset", "omarchy-shell layouts next")
o.bind("SUPER + ALT + SHIFT + L", "Previous layout preset", "omarchy-shell layouts previous")
o.bind("SUPER + SHIFT + C", "Wide middle", "omarchy-shell layouts apply 25-75-25")

bin/omarchy-layout-presets is the backend and works standalone, including on workspaces that are not currently visible:

omarchy-layout-presets status
omarchy-layout-presets apply 25-75-25
omarchy-layout-presets apply "25(50/50)-50-25"
omarchy-layout-presets apply 33-33-33 --workspace 4
omarchy-layout-presets apply 25-75-25 --no-rebuild

How the arrangement works

Hyprland's dwindle layout has no notion of "columns" - it has a binary tree of splits, and a workspace's windows may well be stacked rather than side by side. So applying a preset is two steps:

  1. Rebuild (skipped when the windows are already in the right column/row shape). Every window is floated, which empties the workspace's tiling tree. Each column's first window is then re-tiled behind a preselect r, building the columns left to right; only once they all exist is each column split downwards with preselect d. That order matters: splitting a column into rows before the columns to its right exist would nest those windows inside the row split instead of beside it. The original left-to-right, top-to-bottom reading order is preserved.

  2. Resize. Each window is set to its target size with an exact-size dispatch. A dwindle resize only rebalances the two children of one node, so which sweep order converges depends on the shape of the tree; repeating a sweep in reading order converges for any of them, since sizes that are already correct are no-ops. Targets are computed from what the windows currently add up to, so gaps and reserved bar space need no special handling and the result is exact to the pixel.

Windows stay tiled throughout - floating is only a transient step inside the rebuild, and is undone even if the run fails partway.

Neither step disturbs the pointer. Focusing a window makes Hyprland warp the mouse onto it and the rebuild focuses several, so warping is switched off for the duration and switched back after - the pointer does not dart about and does not end up parked on whichever window was touched last. Keyboard focus does go back to the window that had it.

Install

omarchy plugin add https://github.com/srikat/omarchy-layout-presets.git --enable --yes
omarchy bar move sridhar.layout-presets --section right

Plugins run as unsandboxed code inside omarchy-shell, so omarchy plugin add lands them disabled by default and asks you to review the code first. Drop --enable --yes to take that path; it is four small files.

Optional - bind cycling to a key in ~/.config/hypr/bindings.lua:

o.bind("SUPER + ALT + L", "Next layout preset", "omarchy-shell layouts next")
o.bind("SUPER + ALT + SHIFT + L", "Previous layout preset", "omarchy-shell layouts previous")

Pick whatever combo is free on your system - the panel footer reads the binding back out of your config, so it will name the keys you actually chose.

Update or remove it later with omarchy plugin update sridhar.layout-presets and omarchy plugin remove sridhar.layout-presets.

Hacking on it

Clone it anywhere and symlink the checkout into the shell's plugin path:

ln -sfn /path/to/omarchy-layout-presets ~/.config/omarchy/plugins/sridhar.layout-presets
omarchy-shell shell rescanPlugins
omarchy plugin enable sridhar.layout-presets

Editing Panel.qml needs omarchy restart shell to take effect - the shell logs a hot-reload line for local plugins but keeps rendering the old code, and its file watcher does not follow a symlink out of the plugin directory in any case. Editing bin/omarchy-layout-presets needs no restart.

Single-window workspaces

With one window the presets all collapse to the same thing, so the panel offers the only dimension left: how much of the workspace that window fills. Quick buttons for 50 / 75 / 95 / 100%, and a slider for anything between 20 and 100. The slider applies on release, not while dragging - each change re-tiles the window.

<img src="panel-single.png" alt="The panel on a workspace with one window: a SINGLE WINDOW section with 50/75/95/100% buttons, a slider reading 95%, and a note that the width applies to every workspace with one window until Hyprland reloads." width="330">
omarchy-shell layouts single 75
omarchy-layout-presets single 75 --workspace 4

A lone tiled window has no sibling to rebalance against, so a resize dispatch does nothing to it - its size comes from Hyprland's layout:single_window_aspect_ratio clamp, which this sets and then re-tiles the window to pick up. Two consequences the panel states on screen: the clamp is a global option, so the width applies to every workspace that drops to one window, and it reverts on a config reload to whatever looknfeel.lua says.

layout:single_window_aspect_ratio_tolerance also discards insets smaller than its fraction of the screen, so widths close to 100% may snap to full width.

Layouts other than dwindle

Hyprland tracks the layout per workspace - general:layout is only the default a workspace starts from - so this follows the workspace you are on, reading its tiledLayout. Switch one workspace to scrolling and the panel changes there and nowhere else.

On a workspace tiling with scrolling the STACKED section is not shown at all: a scrolling workspace is a strip of columns, and stacking windows inside one is not an arrangement it holds on to. The panel notices the switch on its own, and cycling simply skips them, from the icon and from the keybinding alike. Stacked layouts remain available over IPC and on the command line, for when you know what you are asking for.

The column presets do work there. The rebuild is written against dwindle, but it holds up under scrolling too: floating every window still empties the workspace, and re-tiling behind a preselect still lands each window where it is wanted.

Two things do differ there and are handled explicitly. A scrolling workspace can put windows of different widths at the same x, so columns are grouped by left edge and width - treating those as one column would report a shape the workspace is not in, skip the rebuild, and leave the preset silently unapplied. And a scrolling strip can be wider than the screen, so the width to divide up is taken from the monitor's tiling span rather than from whatever the windows currently add up to; the gaps between columns are still measured off the live layout, so their exact size never has to be modelled. Under dwindle both come out identical to the old behaviour.

Requirements

Omarchy with the Quickshell-based omarchy-shell, Hyprland 0.56+ (its hyprctl dispatch arguments are Lua, which is the form this plugin emits), and Python 3. Written against the dwindle and scrolling layouts; other layouts are untested.

License

MIT - see LICENSE.