Nordsettings
An Omarchy shell plugin that opens Hyprland look-and-feel settings from a gear icon on the top bar.
Change gaps, borders, rounding, blur, opacity, shadows, dimming, tiling layout, floating-window snapping, per-leaf animation speed and named curves, pointer behaviour, and touchpad options from a guided panel that matches the rest of the desktop. Five reversible live profiles, named user snapshots, category diagrams, Basic / Advanced modes, global search, and Undo / Redo history make the larger Hyprland schema approachable.
Compatibility
Nordsettings targets Omarchy's current shell-plugin API and Hyprland 0.55 or newer, where Omarchy's Hyprland configuration is Lua-based. The model and generated configuration are covered by automated tests; the complete panel still requires an Omarchy/Hyprland session for UI testing.
Omarchy 4.0.3
Omarchy 4.0.3 hands installed bar widgets a PluginBarApi facade instead of the
bar itself, and on that facade centerHoverRevealSuppressed is readonly.
Nordsettings now sets the bar's center-hover hint through
setCenterHoverRevealSuppressed() when the host provides it and falls back to
the direct assignment on older hosts. Panel teardown can also no longer abort
controller.hide(), so the full-screen layer-shell overlay is always released
even if a future host rejects part of the close path.
Install
omarchy plugin add https://github.com/ekrist1/nordsettings.git --enable --yes
omarchy bar move io.github.ekrist1.nordsettings --section right
From this folder, without git:
omarchy plugin validate .
mkdir -p ~/.config/omarchy/plugins
rsync -a --delete --delete-excluded --exclude .git --exclude .agents --exclude .codex ./ \
~/.config/omarchy/plugins/io.github.ekrist1.nordsettings/
omarchy restart shell
omarchy plugin enable io.github.ekrist1.nordsettings --yes
omarchy bar move io.github.ekrist1.nordsettings --section right
Re-syncing after an edit currently needs omarchy restart shell, not
omarchy-shell shell rescanPlugins: the latter reloads the plugin entry but
the QML engine keeps its already-compiled Panel.qml, so the running shell
quietly carries on with the old code.
Keyboard shortcut
Add this to ~/.config/hypr/bindings.lua:
o.bind(
"SUPER + SHIFT + C",
"Hyprland settings",
"omarchy-shell shell toggle io.github.ekrist1.nordsettings"
)
Hyprland reloads the file on save. You can also toggle it from a terminal:
omarchy-shell shell toggle io.github.ekrist1.nordsettings
Use
Click the gear on the bar (right section by default). The panel uses five task-oriented categories:
- Overview — five quick profiles, Undo, and Reset
- Windows — geometry, opacity, and the active tiling layout
- Effects — blur, shadows, and inactive-window dimming
- Motion — global animation plus per-leaf speed and named curves
- Input — pointer, keyboard repeat, cursor, and hardware-aware touchpad behavior
Basic mode shows the controls most people are likely to need. Advanced mode reveals the complete schema. Child controls collapse when their parent feature is off; layout-specific controls appear only for the selected layout. Search is always global and includes Advanced settings, even when another category is selected.
The Balanced, Snappy, Calm, Minimal, and Battery Saver profiles are partial starting points. Selecting one applies it live but saves nothing. Review its exact before/after diff, then choose Keep or Revert. Closing the panel also reverts an unaccepted preview. Profiles never change input devices or choose a tiling layout.
j/k or the arrow keys move between rows. h/l nudges the selected slider,
cycles an enum, or flips a toggle. Enter or Space does the same for toggles
and enums. / or q jumps to search — type a name, a description, or a
group ("blur", "gaps", "touchpad") and the list keeps only the matches.
Escape clears the query, then closes. Tab and Shift+Tab move to the next
or previous bar panel.
Sliders apply after a short debounce so dragging does not stall Hyprland.
A footer status reports loading, applying, and saving. If Hyprland or a file
operation fails, controls stop accepting changes and the footer offers Retry;
failed live changes are not written as though they succeeded.
An up-to-20-step Undo / Redo history appears after an ordinary edit; a
continuous slider drag is coalesced into one action. Keeping a profile can also
be undone.
Rows owned by Nordsettings are marked and have an individual reset button. A
row reset removes only that override, reloads Hyprland, and reveals the value
from Omarchy or your hand-written config. Animation controls reset their whole
leaf because Hyprland applies each animation leaf as one record.
A Reset row at the bottom deletes this plugin's overlay and runs
hyprctl reload, which returns those keys to Omarchy defaults plus anything
you still have in looknfeel.lua / input.lua.
How persistence works
Omarchy configures Hyprland in Lua. Live hyprctl keyword is rejected in
that mode, so the plugin applies changes with:
hyprctl eval 'hl.config({ general = { gaps_in = 8 } })'
hyprctl eval 'hl.animation({ leaf = "windows", enabled = true, speed = 4, bezier = "easeOutQuint" })'
Those evals are session-only. To keep them, Nordsettings writes two files:
| File | Role |
|---|---|
~/.local/state/omarchy/nordsettings/overrides.json |
Source of truth for keys this plugin has set |
~/.local/state/omarchy/nordsettings/profiles.json |
Named, complete user profile snapshots |
~/.local/state/omarchy/toggles/hypr/zz-nordsettings.lua |
Generated hl.config / hl.animation Hyprland actually loads |
profiles.json is portable: copy it to the same path on another machine to
share the named snapshots. Settings unsupported by that machine's Hyprland
version or absent hardware are skipped when a profile is previewed.
The toggles directory is already require'd after your user Hyprland files,
so the overlay is picked up on the next reload or login without editing
hyprland.lua. It also survives omarchy refresh hyprland.
All three files are accessed through the bundled StateStore.py helper.
It uses nonblocking, no-follow descriptor opens; validates directory and file
type, owner, permissions, and link count; rejects oversized input before
parsing; and saves through an exclusive same-directory temporary file followed
by fsync and atomic replacement. Unsafe state paths are reported instead of
being followed or overwritten.
Do not edit zz-nordsettings.lua by hand — the plugin overwrites it. Keep
hand-written extras in ~/.config/hypr/looknfeel.lua and
~/.config/hypr/input.lua.
Same key in both places: the overlay file loads last, so a gap you set
here wins over the same key uncommented in looknfeel.lua. Keys this plugin
does not expose (window rules, colours, binds, monitors) stay yours.
Theme files still own border colours. This plugin does not write colour
keys, so omarchy theme set keeps working.
Hyprland's animation API replaces a complete animation leaf at once. The
first time you change a leaf's speed, curve, or enabled state, Nordsettings
therefore snapshots all four fields for that leaf into overrides.json.
This makes the source of truth accurately describe everything the generated
hl.animation call owns. Reset removes that ownership.
If overrides.json is malformed or from an unsupported future version,
Nordsettings leaves it untouched and disables editing. Fix or move the file,
then select Retry; it will not silently replace unreadable state.
Options unavailable in the running Hyprland version are omitted without blocking the rest of the panel. Touchpad and touchscreen-only controls are also hidden when Omarchy does not detect the corresponding hardware.
What this is not
Monitors (use the stock Display panel), keybindings, window / workspace /
layer rules, a custom bezier point editor, autostart, or environment
variables. Animation curves are a named pick from Omarchy's look-and-feel
(default, easeOutQuint, easeInOutCubic, linear, almostLinear,
quick). It is not a second Quickshell process and it is not HyprMod.
Settings
Tune the widget from the bar settings panel, or inline in
~/.config/omarchy/shell.json:
{
"id": "io.github.ekrist1.nordsettings",
"hoverOpen": false
}
hoverOpen opens the panel when the pointer rests on the gear. It is off
by default so a pass across the bar does not drop a large settings card.
Tests
The option schema, hyprctl -j parsing, Lua emission, and JSON overlay
logic live in HyprModel.js as plain functions:
npm test
That also runs tests/source-lint.test.js, a static pass over the QML and
JS, plus tests/state-store.test.js, which plants FIFOs, symlinks, oversized
files, and unsafe destinations against the real helper. Other checks cover
invalid string escapes, unbounded SplitParser use, manifest settings, and the
requirement that live apply use hyprctl eval.
The installed-shell freshness check is deliberately a development diagnostic, not a source test:
npm run check:installed
Full UI coverage still belongs to using the plugin on the bar — QML here
depends on omarchy-shell, Hyprland, and layer-shell.
Architecture
Panel.qml— panel composition and keyboard navigationSettingsBackend.qml— live reads, reversible preview, Undo, retry, reset, and transactional persistenceSettingsRow.qml— reusable header, setting control, and reset-row delegateSettingsFooter.qml— search, keyboard hints, progress, and retry feedbackCategoryTabs.qml— category and Basic / Advanced navigationPresetGallery.qml,PresetCard.qml,PresetPreviewBar.qml— profile selection and reviewUserProfiles.qml— save, rename, preview, and delete named snapshotsHistoryBar.qml— bounded Undo / Redo controlsSettingVisualizer.qml— live category diagramsHyprModel.js,Presets.js— pure schema, profile, diff, parsing, coercion, filtering, and Lua emissionStateStore.py— bounded descriptor-safe reads, atomic writes, and safe removal for plugin state
Remove
omarchy plugin remove io.github.ekrist1.nordsettings
That takes the widget off the bar and removes the plugin folder. Generated overlay files are left in place so your Hyprland look does not jump on remove. Delete them and reload if you want Omarchy defaults back:
rm -f ~/.local/state/omarchy/toggles/hypr/zz-nordsettings.lua \
~/.local/state/omarchy/nordsettings/overrides.json
hyprctl reload
License
MIT