OmaShuffle
A fresh Omarchy theme every time you boot — or every sunrise and sunset.
Pick the themes you like once. From then on, every real boot deals you the next one from a shuffled deck — no repeats until you've seen them all. Turn on Day & Night and it switches by time of day instead: a light theme while the sun's up, a dark one once it's down.
<img src="https://img.shields.io/badge/Omarchy-4.x-a855f7?style=flat-square" alt="Omarchy 4.x"> <img src="https://img.shields.io/badge/kind-overlay-22d3ee?style=flat-square" alt="overlay plugin"> <img src="https://img.shields.io/badge/network-none-16a34a?style=flat-square" alt="no network"> <img src="https://img.shields.io/badge/license-MIT-64748b?style=flat-square" alt="MIT"><br><br>
<img src="screenshots/picker.png" alt="The OmaShuffle picker: a grid of every installed theme with palette swatches, some ticked into the rotation; the header carries Boot shuffle, Day & Night and Schedule controls" width="860"> </div>Is this for you?
- You installed six themes, loved the third one for a week, and now you're bored again.
- You keep
omarchy theme set-ing around at 1am instead of sleeping. - You genuinely can't decide, and honestly you'd rather your computer just surprised you.
- You like your desk to feel a little different on a Monday than it did on Friday.
If any of that landed: this is for you. OmaShuffle turns "ugh, which theme today" into a decision you already made, once, in a nice grid.
How it works
You open the picker and tick the themes you want in the rotation. That's the whole setup.
After that, every real boot OmaShuffle quietly applies the next theme in the deck. The deck is a shuffled bag: every theme you picked comes up exactly once, then the bag reshuffles and the theme you're currently wearing is pushed to the back so you don't see it twice in a row.
"Real boot" means a genuine power-on or reboot — the kind where you had to type
your disk-unlock passphrase. It reads /proc/sys/kernel/random/boot_id, a value
the kernel regenerates on every boot and keeps stable within one. So:
| This happens | Theme switches? |
|---|---|
| You reboot / power on | yes |
omarchy restart shell, or the shell reloads |
no |
| You log out and back in | no |
| Suspend / resume, closing the lid | no |
Nothing here is a one-way door. It's a normal omarchy theme set under the
hood — grab any theme by hand from the picker (or the usual omarchy theme
command) whenever the deck deals you something you're not in the mood for.
Day & Night
An independent, opt-in (off by default) alternative to boot shuffle: instead of one theme per boot, OmaShuffle switches by time of day. Turn it on from the Schedule tab.
<img src="screenshots/schedule.png" alt="The OmaShuffle Schedule tab: a Switch themes by time of day toggle, a manual latitude/longitude location, and three slots (Day, Afternoon, Evening) each with a light/dark mode, a sunrise/sunset anchor, and a minute offset" width="760">- Slots. Two by default — Day (a light theme, from sunrise) and Night (a dark theme, from sunset) — each with its own no-repeat deck, so "night" isn't the same theme every night. Add up to six total; each new slot is explicitly tagged light or dark, anchored to sunrise or sunset, with its own minute offset from that event.
- Location. Reused from Omarchy's own weather location
(
omarchy-weather-location) when it has coordinates — no setup needed. If none is set, the tab asks for latitude/longitude directly; the actual sunrise/sunset math is pure arithmetic, so this never makes a network call. - Switching. Instant, no notification — the exception is a slot whose
mode has no matching themes in your rotation, which notifies once and
leaves the current theme alone rather than switching to nothing useful.
It switches only when a slot boundary is actually crossed (checked roughly
once a minute, and once at startup to catch a boundary you were off/asleep
for). A plain shell restart mid-slot changes nothing — a theme you set by
hand, with the picker or
omarchy theme, is never undone. - No location, no switching. Until Omarchy knows your coordinates (or you enter them in the tab), Day & Night stays completely inert even when it's toggled on.
- Manual picks from the grid pause Day & Night until the next boundary — it doesn't fight you — and that pause survives a shell restart.
- Shuffle now / Reshuffle deck act on whichever slot is currently active instead of the (dormant, while Day & Night is on) boot-shuffle deck.
Turning Day & Night on doesn't touch boot shuffle's own toggle or deck; it just takes over deciding the applied theme until you turn it back off.
Requirements
- Omarchy 4 with its Quickshell-based shell
python3— the only dependency beyond Omarchy itself. Used once at startup to read this plugin's own small state file safely (see Notes for reviewers). It's present on a normal Omarchy install.
No network access. No elevated privileges. No package management.
Install
omarchy plugin add https://github.com/Deunnis/OmaShuffle.git --enable
omarchy restart shell
That's it — it starts working from the next boot. Until you pick at least one theme it does nothing but show a one-line reminder on boot.
Opening the picker
Out of the box: Super + Space. Enabling OmaShuffle installs a launcher
entry (~/.local/share/applications/omashuffle.desktop), so the picker shows up
in the Omarchy menu and app launcher straight away — search OmaShuffle or
theme shuffle. Disabling or removing the plugin deletes that entry again;
only a file carrying the X-OmaShuffle-Managed=true marker is ever touched.
Other ways, if you want them:
A keybind (fastest). Add one to ~/.config/hypr/bindings.lua, on any combo
that's free for you:
o.bind("SUPER + SHIFT + S", "Theme shuffle", "omarchy-shell shell toggle io.github.omashuffle")
A dedicated menu row. The launcher entry lands under Apps; if you'd
rather have a row somewhere specific, the gear → Omarchy menu → Add menu
entry adds a Style → Theme Shuffle row (remove it from the same place).
By hand, drop this into ~/.config/omarchy/extensions/omarchy-menu.jsonc:
"style.omashuffle": {"icon":"","label":"Theme Shuffle","aliases":["shuffle"],"action":"omarchy-shell shell toggle io.github.omashuffle"},
IPC, for scripts: omarchy-shell shell toggle io.github.omashuffle
Using the picker
<img src="screenshots/settings.png" alt="The OmaShuffle settings pane: boot-shuffle and notification toggles, transparency / corner / outline sliders, and Add / Remove menu entry buttons" width="640">| Action | What it does |
|---|---|
| Click a theme (or Enter) | apply it right now |
| Right-click a theme (or Space) | add / remove it from the rotation |
| Arrow keys | move the highlight |
| Shuffle now | jump straight to the next deck theme |
| Reshuffle deck | throw out the current order and draw a fresh shuffle |
| Select all / none | bulk-edit the rotation |
| All dark / All light | add every dark (or light) theme to the rotation in one click |
| Boot shuffle: on / off | the master switch for the once-per-boot behaviour |
| Day & Night: on / off | the master switch for time-of-day switching (see below) |
| Schedule | open the Day & Night tab — slots, offsets, location |
| Filter themes… | narrow the grid by name |
| gear icon | notifications, card transparency / corners / outline, menu entry |
Esc |
close (or leave settings) |
The header always tells you what you're wearing now and what's queued for next boot. The strip along the bottom is your recent history.
Where it keeps things
Everything lives in one file:
~/.local/state/omarchy/io.github.omashuffle/state.json
pool— the theme slugs you put in the rotationdeck— the remaining shuffled order for the current bag; when it empties, a fresh shuffle ofpoolis drawnhistory— the last 40 themes OmaShuffle applied, with timestampslastBootId— theboot_idat the last switch, i.e. the "was this a real boot" markerschedule— Day & Night: on/off, location, its slots (each with its own deck), and the manual-pick pause- a few UI prefs (notifications, card transparency / corners / outline)
Delete the file to start over; disable the plugin to stop entirely.
Notes for reviewers
- No network. No privileged calls. The plugin runs
omarchy theme set,omarchy-shell,omarchy-notification-send, and its own twopython3helper scripts inbin/. It makes no network requests, needs no elevated privileges, and manages no packages or services. Service.qmlwrites one file:~/.local/share/applications/omashuffle.desktop, a launcher entry pointing atomarchy-shell shell toggle io.github.omashuffle. All the file work is delegated tobin/omashuffle-desktop-entry, which holds itself to the same discipline asbin/omashuffle-menu-entry: it reads the bundledomashuffle.desktoptemplate and the existing destination with the boundedO_RDONLY | O_NOFOLLOW | O_NONBLOCK+S_ISREGread below, proceeds only when the destination is absent or already carries theX-OmaShuffle-Managed=truemarker (a symlink, FIFO, directory or foreign file all read as "not ours" and are left alone), writes the icon-substituted contents to a freshmkstempO_EXCLinode in the verified target directory, andos.replaces that over the destination — which replaces a symlink at the path rather than following it, so nothing is ever written through one. On disable/remove itunlinks the file only when it is a regular file carrying the marker. This mirrors how Omaland registers its launcher entry.- Theme slugs are validated in
ThemeDeck.js(^[a-z0-9][a-z0-9._-]*$, no/, no.., length-capped) before they are ever passed toomarchy theme set, on top of that command's own checks. - Every file this plugin reads is opened as a bounded, regular, non-symlink
file —
state.jsonandcurrent/theme.name(both under~/.local/state) and the menu file are opened once withO_RDONLY | O_NOFOLLOW | O_NONBLOCK,fstat-checked on that same descriptor to requireS_ISREG, and read up to a fixed byte cap, so nothing is bounded by what the path claims and a planted FIFO or symlink is refused.state.jsonwrites go throughFileViewwithatomicWrites. bin/omashuffle-scan-themesenumerates the two standard theme directories and pulls hex values (and themode = "dark"|"light"key, for the All dark / All light buttons and the Day & Night slots) out of eachcolors.tomlwith the bounded descriptor-safe read above. It caps theme count, slug length, per-file bytes and total output, runs under an outertimeout, and never executes anything a theme ships. A theme (which may be an installed third-party repo) cannot make it block or grow the shell.bin/omashuffle-menu-entryis only ever run when you press Add menu entry / Remove menu entry in the settings pane — the plugin never edits the menu file on its own. It reads~/.config/omarchy/extensions/omarchy-menu.jsoncwith the bounded descriptor-safe read, builds the edit in a freshO_EXCLtemp inode inside that directory (verified to be a real directory you own), thenos.replaces it over the target without following a symlink there. It only ever touches a row it owns, and bails without writing if the file layout is unfamiliar.python3is the only dependency beyond Omarchy — the threebin/scripts and the in-QML descriptor-safe readers.- Day & Night adds one more read,
~/.local/state/omarchy/settings/weather.json(written by Omarchy's ownomarchy-weather-location, read the same bounded/descriptor-safe way asstate.json) as the default source of latitude/longitude for sunrise/sunset, and does no I/O beyond that: the sun position math inSunTimes.jsis local arithmetic, never a network call. Manual coordinates, if you enter them instead, stay instate.jsonlike everything else.
Not included (by design)
- No theme creation or editing — OmaShuffle only rotates themes that already
exist. Use
omarchy themeor a theme-designer plugin for that. - No wallpaper-only shuffle — it switches whole themes via
omarchy theme set. - No background blur behind the picker card (transparency / corners / outline only, for now).
Remove
omarchy plugin remove io.github.omashuffle
omarchy restart shell
Your last theme stays applied. The launcher entry
(~/.local/share/applications/omashuffle.desktop) is removed automatically on
disable/remove. If you also added the dedicated menu row, remove it from the
settings pane (Remove menu entry) or delete the style.omashuffle line from
~/.config/omarchy/extensions/omarchy-menu.jsonc yourself. Delete
~/.local/state/omarchy/io.github.omashuffle/ to clear the rotation and history.
Development
omarchy plugin validate ~/.config/omarchy/plugins/io.github.omashuffle
python3 -m py_compile bin/omashuffle-menu-entry bin/omashuffle-scan-themes bin/omashuffle-desktop-entry
node --check <(tail -n +2 ThemeDeck.js) # strip the .pragma line
node --check <(tail -n +2 SunTimes.js) # same, for the sun-position math
omarchy restart shell # plugin QML is cached by URL
ThemeDeck.js and SunTimes.js are pure logic with no QML or I/O
dependencies — the easy place to reason about shuffling, the no-immediate-
repeat rule, slug validation, and sunrise/sunset math.
License
MIT — see LICENSE.