Design zone layouts for Hyprland on Omarchy, then move your windows between them. Browse and edit layouts directly over your desktop, save app arrangements as scenes, and manage your displays from the same overlay.
Watch Hypertile in two minutes — a narrated introduction with real desktop footage (1:56).
- Layouts: split and resize zones, choose their fill order, and tune gaps, corners, aspect ratios, and stacking.
- Window movement: move or swap windows with keyboard shortcuts, numbered destinations, or drag and drop.
- Scenes: assign apps to zones and save arrangements you can return to.
- Displays: arrange or mirror screens, adjust resolution and scale, and choose workspace placement and monitor layout defaults.
- Session recovery: save the desktop and restore supported apps when you log in again.
Watch the 73-second feature demo.
Install · Everyday use · Overlay · CLI · Update · Uninstall · Documentation · Development
Requires Omarchy 4 with its Lua Hyprland config and Omarchy shell. Development and local validation use Hyprland 0.56.2 / Omarchy 4.0.3. The separately installed compositor backports address window sizing, mirror-source output registration, mirror disconnection, and workspace restoration onto mirrors.
Runtime and setup use the Omarchy shell (Quickshell), Bash, lua, jq,
Python 3, coreutils, flock from util-linux, and systemd-run from systemd,
included with Omarchy. Setup uses a user service to restart a running shell
after plugin updates.
Setup runs as your user; it does not install packages or patch the compositor.
omarchy plugin add https://github.com/jdvmi00/hypertile.git --enableEnabling the plugin installs its runtime automatically. The widget briefly
shows Setting up Hypertile… while setup finishes. A newly created layouts
directory starts with welcome, a four-zone layout; existing layout directories
are left untouched.
Open the overlay with SUPER+ALT+L or the bar widget. Choose a layout and press
Enter to use it, or choose Edit to make it your own. SUPER+L cycles
through saved layouts and then dwindle; SUPER+SHIFT+L cycles backwards.
See the user manual for a first walkthrough.
What setup changes
Setup installs the engine and bridge under ~/.config/hypr/, the CLI commands
under ~/.local/bin/, and the display, Scenes, and session recovery services.
It adds the layout loader to hyprland.lua, a bar widget, a Layouts menu
entry, navigation shortcuts, and guarded logout/reboot/shutdown actions that
save the session before closing apps. Custom power-menu actions are left alone.
The cycling shortcut replaces Omarchy's default dwindle/scrolling toggle;
an existing custom Toggle workspace layout binding is left alone.
Every config file the installer edits is first copied to
<file>.hypertile.bak if that backup does not exist. Later installs preserve
that first backup. The installer reloads Hyprland and checks
hyprctl configerrors.
For a manual install with optional integrations omitted, add the plugin
without --enable, then run its install.sh with --no-menu and/or
--no-keybinds. Automatic updates remember these choices.
| Where | What |
|---|---|
SUPER+L, SUPER+SHIFT+L |
next or previous layout on this workspace; the name flashes in the OSD |
SUPER+ALT+L, the bar widget, SUPER+SPACE > Layouts |
open the overlay |
SUPER+ALT+T, then a zone number |
move the active window to that zone, swapping if occupied; Esc cancels |
SUPER+Arrow / SUPER+SHIFT+Arrow |
focus a window / move or swap it into the next zone |
SUPER + left mouse drag |
drag a tiled window to a numbered destination |
| bar widget | the layout on this monitor's workspace; scroll or middle-click cycles |
| right-click bar widget | Settings, opened at Hypertile’s menu bar position: Left, Middle, or Right |
hypertile-ctl list |
the layouts on disk, the one in use starred |
Layouts live one per file in ~/.config/hypr/layouts/<name>.lua. A layout
saved from the overlay joins the SUPER+L cycle (every layout on disk in
name order, then dwindle); one saved with in_cycle = false is skipped by
the cycle and still shown by the overlay. Pressing SUPER+L several times
quickly flashes each name and switches once, to the layout you stop on.
Each workspace remembers its explicit layout in
~/.local/state/hypertile/workspace-rules/. Otherwise it inherits the monitor's
default layout, falling back to general.layout in looknfeel.lua. Choose
Apply to → Follow the display default in the Layouts rail, or run
hypertile-ctl apply monitor-default, to clear a workspace override. An active
scene keeps its required layout until you confirm a replacement.
The gear in the overlay header opens Settings: saving windows for startup, the side of the screen the rail docks on, whether the rail lists the keys, the menu bar position, and the desktop text size. The layout, scene, and display settings stay in their tabs. The installed Hypertile version appears beneath the Settings title.
Right-click Hypertile’s bar widget, or open Settings from the gear, and use the Menu bar cards: Left, Middle, or Right. The checked card shows the current position; changes apply immediately on every screen and survive restarts and updates. On a vertical bar, the choices become Top, Middle, and Bottom.
Use arrow keys to focus a card and Enter or Space to select it. Escape or a click outside closes the popover. Placement is saved by Omarchy independently of your layouts and scenes.
SUPER+Arrow focuses the nearest window in that direction. SUPER+SHIFT+Arrow
moves to the next zone: an empty zone receives the active window, and an
occupied zone swaps windows. Other apps stay in their zones; spacers and zones
a scene marked Empty are skipped. Moving into a collapsed zone reveals the full
layout on that workspace until the layout is reset.
SUPER+ALT+T shows numbered destinations for the active window. Release the
modifiers and type a zone's number, or click a zone, to move there (or swap with
its occupant). Esc cancels; the current zone is highlighted. Numbers match
the layout's fill order, including multiple numbers for a stacked zone.
When a number is also a prefix (for example, 1 and 10), press Enter to select
the shorter number; Backspace corrects input. Spacers and zones a scene marked
Empty are excluded. A zone without a fill number can still be clicked.
Outlines follow each zone's fitted window area, including aspect ratio and
scale, so unused space outside that area is not outlined.
SUPER + left mouse drag also shows those destinations for a tiled window.
The dragged window animates and follows the pointer while the other windows
stay in place. A valid destination under the pointer gets a bright highlight
and thicker outline. Release with the pointer inside another zone to move
there (or swap if occupied). Releasing in the original zone or outside the
outlined zones leaves windows unchanged. Destinations stay on the starting
workspace and monitor. Floating
windows and other layouts keep their normal mouse behavior.
A fullscreen layer draws the viewed layout's zones at true scale over your
windows. Each zone carries a badge with its position in the fill order (a
slot holding several positions shows "5 · 6" and a divider per stacked
window) and chips for its size and constraints; a badge under the rail
moves clear of it. The inspector rail holds the layout's name, the main
actions, and the settings for the current mode, with the rarer ones in
collapsed sections. It docks on the left or the right (Settings) and
remembers that, along with which sections are open, in
~/.local/state/hypertile/overlay.json. A Getting started card shows on
the Layouts tab until you choose Got it.
Each zone shows its width × height in pixels beneath its fill-order number,
updating as you resize the layout. Under the name, the rail says where the
layout is in use and how windows fill it, then offers Use (when the
workspace is on another layout), Edit, and ⋯ for Rename, Duplicate,
Export…, the SUPER+L cycle switch, and Delete. + New on the LAYOUTS
heading starts a blank layout, a copy, or an import.
| Key | Action |
|---|---|
arrows or h j k l, or a click in the rail's list |
browse the layouts on disk; the workspace follows |
SUPER+L, SUPER+SHIFT+L |
browse forward/backward, moving the displayed layout and windows together |
Enter |
use the viewed layout on this workspace and close |
Esc, click outside |
close; the workspace goes back to the layout it had |
Space (hold) |
peek: the overlay fades to hairlines |
e |
edit the viewed layout |
n |
new layout: blank (b), a copy of the viewed one (c), or an import |
F2 |
rename (workspace rules and the default follow) |
d |
delete, after a confirmation |
r |
re-read the layouts and the workspace |
? |
show or hide the keys in the rail |
Browsing switches the workspace for real, without persisting: each step is a compositor-only switch, debounced behind the keys so a held arrow lands once, and the scrim lightens so the windows show through. A layout used as the global or a monitor default cannot be deleted until another default is chosen. Workspaces whose explicit layout is deleted return to inheritance. Apply to, collapsed until you open it, uses the viewed layout on any open workspace, on every workspace of a display, or as the default layout, and keeps the overlay open; it also drops the current workspace's own choice so it follows its display's default. Displays → Workspaces sets a display's default layout and the layouts of workspaces that live on it, open or not.
Use ⋯ → Export… in the Layouts tab to save the selected layout as a JSON
file. On another installation, + New → Import… adds it to the layout list. Imports are
independent copies: if the name already exists, Hypertile adds -2, -3, and
so on. Choose Use to apply the imported layout. Layout files include zone
geometry, fill order, app rules, and layout settings; scenes, open windows,
display settings, and workspace assignments are separate.
Edits preview live on an unmanaged workspace. When a workspace has assigned content, preview stays off: saving an existing layout applies the changes, and saving a new layout asks before using it and replacing those assignments.
| Key | Action |
|---|---|
click, arrows or hjkl, Tab |
select a zone |
c, r |
split the selected zone into columns or rows |
x, Delete, right-click |
delete it; the neighbour absorbs the space |
| drag a divider | resize the two zones it separates |
Shift + arrows |
move the selected zone's edge by 1% of the screen |
s |
toggle between holding nothing (a spacer) and holding windows |
f |
fill order: click zones in order, Backspace undoes, Enter finishes |
u |
undo |
w, Ctrl+S |
save (a new layout asks for a name) |
Esc |
done; with unsaved changes it asks: Discard, Save, or keep editing |
The rail's Zone section names the selected zone (names matter in the file and for app rules, so view mode does not show them), sets its exact size in percent of the screen, and what it Holds: Windows (more windows split it), One window (never split: more overlap it at full size, and it is never an overflow target while another zone exists), or Nothing (a spacer). The split and delete buttons sit on the zone's card; the rail shows them only for zones too small to carry them. The collapsed sections below hold the rest. More options: stack direction, capacity (No limit, or a number of windows), aspect ratio (1:1, 4:3, 3:2, 16:9, 21:9) and scale, so a 1:1 aspect ratio at 70% fits a smaller square inside the zone. Opens here lists the apps pinned to the zone, picked from the windows open now; an app allowed in several zones fills them lowest number first. Appearance sets the gutters (the gap between windows, the gap around the layout, the border) and the window corner radius for the layout's workspaces. Behaviour sets the empty-zone and lone-window policies. A collapsed section's heading lists its settings that differ from the default.
Zones you did not click while setting the fill order follow the clicked ones in tree order. Discarding an unmanaged edit previews the saved layout back onto the workspace; nothing reloads. Discarding managed edits leaves the workspace as it was. Gutters and rounding travel with the layout as workspace and window rules. See engine internals for switch behavior.
Switch to Scenes to choose what each zone holds: Any window (whatever
opens, in fill order), Empty, an open window, or an installed app. The app
list starts short; Show all or a search reaches every app. Click a zone,
press its fill number outside the search field, or use Tab to select it. Type to search; app names can start
with digits. ↑/↓ select a match, Enter assigns it, and Tab moves to the
next zone while keeping the query. Esc clears a query first, then closes;
? shows the keys. Ordinary letters, including hjkl, remain search text.
With no zone selected, ↑/↓ browse saved scenes, Enter uses the selected
scene, and Delete asks before removing its file (Enter confirms, Esc
cancels). Save updates the named scene; Save as… stores another one.
Using a saved scene asks before replacing unsaved scene changes. Retry
rechecks pending content, and Restore previous returns to the arrangement
from before the scene. Clicking outside the zones deselects first; a second
click closes. See Scenes and content for placement, recovery,
and app identity details.
Remote desktop apps and migration
Scenes can launch or reuse installed apps in named zones, including each computer's Remote Desktops launcher. Placement happens once; subsequent window moves and closes stay under your control. The independent Scenes service does not own remote connections or host display settings.
Use the overlay’s Scenes tab or hypertile-ctl scene to assign apps, local
windows, or Empty, then save the arrangement. See scenes and content
for setup, migration from legacy stream sources, and recovery behavior.
Remote connections and host recovery now belong to
Remote Desktops. Upgrade checks
require legacy connections to be disconnected and restored before removing
their old runtime files; saved configuration and journals are preserved.
Choose Displays to arrange or mirror screens, change resolution, refresh rate, scale and rotation, or sleep and wake outputs. The pane opens from the rail's corner. Select a display: its name, Use as, resolution, scale and rotation come first, with Sleep beside the name. Position (exact X and Y) and Workspaces are collapsed. Under Workspaces, Show switches the display's workspace immediately; Start on this workspace saves a starting workspace through Preview/Keep; the display's default layout and the workspaces that live on it are set there too. Forget this display removes a disconnected display's saved settings immediately, without Preview or Keep, and preserves unrelated unsaved edits.
The Display | Wallpaper switch at the top of the inspector shows the selected display's wallpaper: This display or Span displays (an image across the displays you pick), Theme wallpaper or Custom image, and Fill or Fit. Apply wallpaper saves immediately.
In a mirror group, Use this display fits the shared desktop to the selected
monitor while every output retains its own resolution and refresh rate.
Switching saves immediately; Switch back to … returns to the previous
source. The active display renders the desktop and the others show a scaled
copy. hypertile-ctl display use-display next cycles the focused mirror group.
Arrangement and workspace preference changes use a 15-second Keep/Revert
preview with an independent rollback watchdog. Keep changes saves adjusted
monitor fields to ~/.config/hypr/monitors.lua, preserving unrelated and
unchanged automatic settings. Existing monitor configuration is adopted
automatically; setup does not rearrange your screens.
Desktop text size lives in Settings; it applies immediately to every display and is separate from Preview and Keep. Sleep/wake is also immediate and temporary. See Displays and workspace placement for mirroring, configuration ownership, reconnect behavior, and the shared UI/CLI workflow.
An ! badge means session saving needs attention. The tooltip explains why; open the overlay to see unmatched windows or resume saving after a partial restore or freeze. See Session recovery.
The bar widget shows the layout icon and name on each monitor's active
workspace. Clicking opens the overlay; the scroll wheel or a middle-click cycles. It follows workspace
focus, workspaces moving between monitors, and config reloads on its own,
and hypertile-ctl pokes it after every apply, since a workspace rule
write raises no compositor event. omarchy bar move jmartin.hypertile --section center moves it. The shell tracks the plugin by that one bar
entry, so omarchy plugin disable jmartin.hypertile drops the overlay too.
Hypertile saves the desktop after changes and restores supported applications on the next Hyprland session. It restores zone layouts, native window order, pins, runtime sizing, workspaces, floating geometry, and focus. Applications restore their own tabs/documents; terminal commands are not replayed.
Turn this on or off with Settings → Save windows for startup in the
overlay, or hypertile-ctl session enable|disable. The choice takes effect
immediately and persists across reboots. Enabling saves the current desktop.
Use the guarded Omarchy menu actions or hypertile-ctl session logout,
reboot, or shutdown so the snapshot is saved before applications close.
hypertile-ctl session status reports progress and unmatched windows. Apps
with no launch recipe are skipped without holding anything up. A partial
restore (an app that failed to launch or whose window never appeared) protects
the original snapshot until you retry or explicitly accept the current desktop
with hypertile-ctl session resume; a notification says so, and again at
logout while saving is still paused.
hypertile-ctl session save work saves a named session;
hypertile-ctl session restore work returns to it. See session recovery for app recipes,
shutdown integration, storage, and limits.
| Guide | Covers |
|---|---|
| User manual | getting started, daily use, layout recipes, and troubleshooting |
| Scenes | app assignments, saved arrangements, and recovery |
| Displays | monitor settings, mirroring, and workspace placement |
| Session recovery | app recipes, named sessions, and shutdown integration |
| Development guide | working checkout, runtime updates, and verification |
| Engine internals | layout behavior and compositor integration |
| Changelog | release history and unreleased changes |
Hyprland 0.56.2 sizing bug: after a layout switch or save, a window's
content can remain smaller than its tile. hypertile-ctl heal is temporary
recovery. A one-line compositor backport fixes the identified acknowledgment
bookkeeping defect. See the sizing bug and backport guide
for building/installing it, checking it after Omarchy updates, rollback, and
returning to an official package that includes the upstream correction.
Run hypertile-ctl help for command syntax. The CLI can also run directly
from this checkout with HYPERTILE_SRC=$PWD bin/hypertile-ctl help.
hypertile-ctl list [--json] layouts on disk, the one in use starred; --json includes each spec
hypertile-ctl dump <name> layout as JSON {"name":..., "spec":{...}}
hypertile-ctl export <name> [file|-] [--force]
export JSON to a file or stdout; --force allows overwrite
hypertile-ctl import [file|-] [--name NAME] [--no-reload] [--json]
import as a new layout; duplicate names gain -2, -3, ...
hypertile-ctl validate [file|-] check JSON, silent on success
hypertile-ctl save [file|-] [--no-reload]
write layouts/<name>.lua, reload
hypertile-ctl rename <old> <new> [--no-reload]
rename the file; workspace rules and the default follow
hypertile-ctl remove <name> [--force] [--no-reload]
delete the file, reload; refuses global or monitor defaults;
--force clears workspace references
hypertile-ctl preview [file|-] [--workspace N] [--no-apply]
hot-swap in the compositor and re-place the workspace, no disk write
hypertile-ctl apply <name|monitor-default|dwindle|scrolling|master> [--workspace N] [--quiet] [--no-persist]
workspace rule, persisted in ~/.local/state/hypertile/workspace-rules/;
the shell flashes the name (unless --quiet, or the workspace is
not the active one) and the bar widget refreshes; --no-persist
switches the compositor only; monitor-default clears the
workspace override
hypertile-ctl cycle [--reverse] [--workspace N] [--quiet] [--now]
apply the next layout: every saved layout in name order
(in_cycle = false skips one), then dwindle; presses within
200 ms become one switch to the layout landed on (--now
switches at once); while the Layouts overlay is open,
cycle browses there instead (--now bypasses this)
hypertile-ctl heal [--workspace N] manual recovery for a window drawn smaller than its tile
hypertile-ctl current [--json] active workspace id, name, layout; --json adds monitor size,
reserved edges, gaps, border, layout area
hypertile-ctl workspaces [--json] every workspace with monitor, layout, window count
hypertile-ctl windows [--json] open windows (class, title, workspace)
hypertile-ctl default [name|dwindle|scrolling|master] [--no-reload]
show or set the default layout (looknfeel.lua)
hypertile-ctl path [name] layouts dir or a layout's file
Reading a layout back executes its file with a recording stub in place of the engine, so hand-edited files round-trip as long as they are valid Lua.
Share layouts without copying executable Lua:
hypertile-ctl export quad ~/quad.json
hypertile-ctl import ~/quad.json
hypertile-ctl import ~/quad.json --name workImport validates the JSON before saving a new layout and reloading Hyprland;
it does not apply the layout. It clears scene identities so copies can be used
independently. Export refuses an existing destination unless --force is
provided. The overlay's Save dialog asks before replacing an export file.
| Command | Purpose | Reference |
|---|---|---|
hypertile-ctl scene |
save and apply scenes, assign content, retry or restore | Scenes |
hypertile-ctl display |
inspect displays, preview/keep/revert changes, identify, sleep or wake | Displays |
hypertile-ctl session |
check recovery, enable/disable saving, save/restore named sessions, guarded logout | Sessions |
hypertile-ctl scene list
hypertile-ctl display list --json
hypertile-ctl session statusWhile the overlay is open, omarchy-shell hypertile <method> [args] drives
it:
| Method | Effect |
|---|---|
next, prev, view <name> |
browse |
use, apply, applyTo <ws>, applyMonitor <mon>, setDefault |
use the viewed layout (use closes) |
inCycle <bool>, rename <name>, deleteLayout |
layout housekeeping |
importLayout <path>, exportLayout <path> |
import a JSON layout, or export the viewed layout (existing export files are refused) |
edit, newLayout, newBlank |
enter edit mode (copy or blank) |
select <zone>, move <dir>, split <zone> <columns|rows>, remove <zone> |
zones |
nudge <w|h> <delta>, size <zone> <w|h> <fraction>, resize <path> <index> <ratio> |
sizes |
renameZone <zone> <name>, renumber <a,b,c>, zoneProp <zone> <key> <value>, capacity <zone> <n> |
zone settings |
layoutProp <key> <value>, gap <inner|outer> <px>, addRule <class> <zone>, removeRule <i> |
layout settings |
undo, saveAs <name>, discard |
finish an edit |
content <bool> |
show Scenes (true) or Layouts (false); leave edit mode first |
displays, displayState |
open Displays or read its draft, preview, and error state |
displaySelect <index>, displaySet <index> <key> <value> |
select or edit a display in the draft (zero-based index) |
displayPreview, displayKeep, displayRevert |
preview, keep, or revert display changes |
displayIdentify <connector>, displayDiscard |
identify a display, or discard edits and close |
assign <local|empty> |
set the selected zone to local fill or empty |
assignApp <class> |
pin an already-open window of this class to the selected zone; does not launch an app |
scene <action> <name> |
request a scene action on the current workspace; see actions below |
saveSceneAs, saveScene <name>, deleteScene <name> |
open the scene naming field, save directly, or delete directly |
confirmSwitch |
confirm the pending layout or scene replacement |
search <text>, pick |
set the app query, then assign the selected search match |
hover <index>, focusSearch |
preview a search match by zero-based index (-1 clears), or focus the search field |
dock <left|right>, keysHint <bool>, peek <bool>, refresh, close |
the overlay itself |
viewed, draft, state |
read back the viewed name, the draft, or the whole state as JSON |
For content assignments, switch to Scenes and select a zone first. assignApp
accepts a window class; to launch or reuse an installed app, use search and
pick. scene actions include apply, save, and remove with a scene name,
or retry, restore, and dismiss with an empty name argument (""). Use
state to inspect pendingSwitch, scene progress, or errors; confirmSwitch
accepts a pending replacement. deleteScene removes the saved definition
immediately, without the UI's confirmation prompt.
omarchy-shell hypertile content true
omarchy-shell hypertile select left
omarchy-shell hypertile search '1Password'
omarchy-shell hypertile state # check that matches is greater than zero
omarchy-shell hypertile pick
omarchy-shell hypertile stateReplace left with a zone name from your layout. Placement may continue after
the command returns; read state to check its result.
local hypertile = require("hypr.hypertile")
hypertile.layout("ultrawide", {
columns = {
{ name = "left", w = 0.2 },
{ name = "center", w = 0.6 },
{ name = "right", w = 0.2, rows = { { name = "r1" }, { name = "r2", h = 2 } } },
},
-- leaf options: aspect = 1 (w/h), scale = 0.7, spacer = true, never_split = true, stack = "h"
fill = { "center", "right", "left" }, -- where the first unassigned windows go
cycle = { "right", "left", "center" }, -- where the rest go (defaults to fill)
rules = { { class = "^chromium$", slot = "center" }, { tag = "terminal", slot = "right" } },
capacity = { r1 = 1 }, -- overflow spills to the next fill slot
gaps = { inner = 0, outer = 0 }, -- gutters, applied as workspace rules
border = 0, -- border size, same
rounding = 12, -- window corner radius 0..20, as a window rule
empty = "collapse", -- or "keep": empty slots leave a gap;
-- any container node can override with its own `empty`
single = "collapse", -- or "slot": one window stays in its slot
stack = "v", -- or "h": how windows share a slot
in_cycle = false, -- leave out of the SUPER+L cycle (default true)
})Use it from a workspace rule (layout = "lua:ultrawide") or as the global
layout. Runtime commands go through the layout dispatcher:
hl.dsp.layout("pin center") -- pin the active window to a slot
hl.dsp.layout("unpin")
hl.dsp.layout("grow center 0.05") -- adjust a slot's weight
hl.dsp.layout("size center 1.0")
hl.dsp.layout("reset")New installs include welcome: four equal zones filled top-left, top-right,
bottom-left, then bottom-right, with horizontal stacks in the top two zones
and 20-pixel rounding. The bottom zones hold one window each; empty zones
keep their space, and a lone window stays in its slot.
The repository also includes two example layouts for reference and tests; neither is installed automatically:
ultrawide: 20/60/20 columns, fill center, right, left, then cycle.quad: same columns, but the center is four quadrants filled top-left, top-right, bottom-left, bottom-right, then right (stacked), then left (stacked). Every slot keeps its place when empty, so a lone window sits in the top-left quadrant.
omarchy plugin update jmartin.hypertile --yesEnabled plugins apply runtime updates automatically; disabled plugins apply
them when next enabled. Updates that change shell plugin files restart the
shell once after successful setup, including UI-only updates. Unchanged
reloads skip setup and do not restart the shell.
If setup fails, the widget points to ~/.local/state/hypertile/install.log
($XDG_STATE_HOME/hypertile/install.log when set). Resolve the reported issue,
then disable and re-enable Hypertile to retry.
Development links use ./install.sh once and ./dev apply after edits.
Uninstall with:
~/.config/omarchy/plugins/jmartin.hypertile/uninstall.sh
# Choose the archive location instead:
# .../uninstall.sh --archive ~/Backups/my-hypertile-settings
# Or permanently discard settings without an archive:
# .../uninstall.sh --purgeUninstall first copies and verifies an archive in
~/Backups/hypertile-uninstall-<timestamp>/ (or the new directory supplied
with --archive). It then removes layouts, all Hypertile settings, scenes,
state, runtime files and caches, installer-created .hypertile.bak files,
and the installed plugin checkout. Development symlinks are unlinked without
removing their source checkout. The shell restarts to clear cached plugin UI,
so reinstalling another version cannot show the old version's menus.
The archive includes layouts/, settings/, state/, reference copies of
desktop configuration, and the plugin checkout. Its README.txt explains
restoration. To restore only layouts after testing a clean install, copy the
contents of layouts/ into ~/.config/hypr/layouts/ (or
$XDG_CONFIG_HOME/hypr/layouts/) and run hyprctl reload. Existing archive
paths are refused; if archiving fails, uninstall stops before removing files.
--purge explicitly skips archiving; both modes leave a clean installation.
The default layout returns to dwindle if it used Hypertile. Monitor settings
and unrelated desktop customizations remain. If a custom binding still
references hypertile-navigation, uninstall retains runtime files and asks
you to remove that reference before running it again. Active legacy remote
connections must also be restored before uninstalling.
For live development, keep one checkout and link the installed plugin to it:
./dev link # preserves the existing installation, then links this checkout
./install.sh # first-time runtime/config setup
./dev apply # validates and applies changes; restarts affected components
./dev statusAfter setup, edit locally and run ./dev apply. See the
development README for component reloads, layout previews,
backup/recovery paths, and isolated testing. The regular installer also supports
an unlinked source checkout when the destination is a plain plugin copy; it
refuses to overwrite a different Git checkout.
Run the tests from the repository root:
shellcheck install.sh uninstall.sh
python3 test/dev.py && python3 test/upgrade.py # deployment helper: preservation, restarts, failures
python3 test/install.py # installer and uninstaller
python3 test/wayland.py && python3 test/displays.py && python3 test/display_configuration.py && python3 test/display_safety.py && python3 test/display_placement.py && python3 test/display_policy.py && node test/displays.js
# display transactions, failure recovery, assignment policy
python3 test/display_integration.py # opt-in isolated compositor, from a live Wayland session
python3 test/display_integration.py --auto-position-only # disable next to an automatically placed display
python3 test/mirror_outputs_integration.py --fixed # opt-in with the mirror-output compositor fix installed
python3 test/overlay_display_integration.py # real overlay surfaces and keyboard input across mirror-source switches
python3 test/issue_integration.py # live inherited-default and automatic-position regressions
lua test/harness.lua && lua test/loader.lua && lua test/pattern.lua
# engine, bounded Lua patterns, capacity, hot swap
lua test/navigation.lua && node test/tile_picker.js # directional and numbered moves, swaps, picker input
lua test/bridge.lua # bridge and CLI, against a fake hyprctl
python3 test/session.py && python3 test/scene_recovery.py && lua test/session.lua && node test/session.js
# recovery: durable writes, shutdown, restart, identity
python3 test/scenes.py && python3 test/apps.py && lua test/scenes.lua && lua test/swap.lua
node test/content.js && node test/content_keys.js # scenes, app catalog, placement, the Scenes tab
python3 test/browse.py && node test/browse.js # managed layout browsing
node test/wallpaper.js && python3 test/wallpaper.py # wallpaper groups and settings
python3 test/wallpaper_integration.py # opt-in installed Omarchy renderer in isolated compositor
node test/geometry.js # overlay drawing math
node test/editor.js && node test/overlay.js && node test/readability.js
# editor operations (validated by the engine), overlay, contrast
omarchy plugin validate .The CI workflow runs the unit suites and manifest
checks. The isolated compositor integration test and omarchy plugin validate
are local checks requiring the corresponding environment; CI also checks the
migrated Windows display policy in the Remote Desktops repository.
The harness also checks that the ultrawide spec places 1 to 9 windows
where the hand-written provider in test/fixtures/legacy-ultrawide.lua
put them (within 1px on stacked heights, where hypertile rounds edges
instead of sizes to avoid seams). The CLI runs from a checkout without
installing: HYPERTILE_SRC=$PWD bin/hypertile-ctl list.
Source files and architecture
manifest.json the Omarchy plugin manifest (kinds: overlay, bar-widget, service)
plugin/ the shell plugin: Overlay.qml, Rail.qml (inspector), ZoneItem.qml,
Divider.qml, Thumb.qml, Card.qml, Chip.qml, Geometry.js (drawing),
Editor.js (edits); DisplaysPane.qml and Displays.js (display settings);
ContentPane.qml and Content.js (the Scenes tab);
SettingsPanel.qml (the gear); KitButton.qml (the button
every surface uses);
TilePicker.qml and TilePicker.js (numbered tile destinations);
LayoutWidget.qml (bar widget); SessionStatus.qml and Session.js
(session status); Service.qml (automatic setup on enable and
update); Readability.js (text contrast)
hypertile.lua engine: spec -> layout provider (hot-swappable)
hypertile-pattern.lua bounded Lua-pattern compiler and matcher for placement rules
hypertile-bridge.lua bridge: load/serialize/JSON/save/preview/apply
hypertile-json.lua JSON encode/decode (pure Lua)
hypertile-layouts.lua loader: requires every ~/.config/hypr/layouts/*.lua
hypertile-navigation.lua gap-aware focus and swap for SUPER+arrows and SUPER+SHIFT+arrows
hypertile-session.lua compositor adapter: capture and restore window placement
session/service.py session watcher, durable snapshots, app launch and matching
session/scene_recovery.py checkpoint and delivery of scene assignments across restarts
session/upgrade.py preserve daemon state while replacing installed runtime files
scenes/*.py scenes service: saved scenes, the app catalog, one-shot placement
layouts/*.lua starter layout: welcome; reference examples: ultrawide, quad
bin/hypertile-ctl CLI over the bridge
bin/hypertile-session session service entry point (also via hypertile-ctl session)
bin/hypertile-displays display service entry point (also via hypertile-ctl display)
displays/*.py display adapter, recovery watchdog, workspace policy
bin/hypertile-scenes scenes service entry point (also via hypertile-ctl scene)
dev link the checkout, check changes, and reload affected components
install.sh puts the engine, CLI, keybinds, and menu entry in place
uninstall.sh takes them out again
probe.lua live probe (logs everything the API hands a layout)
docs/MANUAL.md user manual: daily use, designing layouts, scenes, sessions, troubleshooting
docs/SCENES.md scenes and content: placement, recovery, app identity, the scene CLI
docs/DISPLAYS.md displays, mirroring, workspace placement, and monitor configuration
docs/SESSIONS.md session recovery: commands, app recipes, shutdown integration, storage
docs/README.md development workflow, testing, and backup/recovery paths
docs/INTERNALS.md what the compositor API does and does not do, what shapes the overlay,
and the stale-window forensics
docs/HYPRLAND-SIZING-BUG.md the 0.56.2 size-ack bug and the local compositor backport
docs/RELEASING.md release and marketplace verification procedure
CHANGELOG.md release notes
test/ engine, bridge, CLI, geometry, and editor tests
The layout editor reads workspace and layout data through hypertile-ctl current --json and
hypertile-ctl list --json, previews through hypertile-ctl preview, and
saves through hypertile-ctl save then apply. Overlay.qml owns the
state, the processes, and the pointer and key handling; Rail.qml is the
inspector, ZoneItem.qml draws a zone, Divider.qml a boundary,
Thumb.qml a layout thumbnail. The chrome uses the shell's Color and
Style tokens and its qs.Ui controls, so it follows the active theme.
Zone math lives in plugin/Geometry.js; edits are pure functions in
plugin/Editor.js.
MIT. See LICENSE.

