6663 Commits
Author SHA1 Message Date
David Heinemeier HanssonandClaude Opus 5.5 3faafba234 Install Hype, the Markdown presentation app, by default (#13455)
Hype joins Omacut, Monologue, Omacalc and Omawrite as an Omacom app in
the base install and the preinstall set, so Remove and Install
Preinstalls cover it too. A migration installs it on existing machines,
unless their owner has removed the preinstalls, and the GUIs chapter of
the manual introduces it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:48:22 +02:00
David Heinemeier HanssonandClaude Opus 5.5 349ecc09a2 Flip Elsewhen between Fahrenheit and Celsius with Alt+T (#13450)
* Flip Elsewhen between Fahrenheit and Celsius with Shift+T

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Flip Elsewhen's temperature units with Alt+T instead of Shift+T

PanelKeyCatcher's textKey now carries the held modifiers, which is how the
panel tells Alt+T from T. Existing handlers take only the text and are
unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:19:31 +02:00
David Heinemeier HanssonandClaude Opus 5.5 ed5837a05b Give the picked Elsewhen city one bright highlight (#13452)
A row lit itself by lifting its own time-of-day fill, so a picked night row
could read darker than the daytime rows around it. The picked city now takes
midday's lifted fill, the brightest, whatever its time of day; hover still
lifts each row from its own.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:19:11 +02:00
David Heinemeier HanssonandClaude Opus 5.5 944fa24fd1 Add Monologue as a default app (#13449)
Monologue is Omarchy's own webcam recorder: pick a camera and microphone
once, press Space to record, and stop straight into a built-in editor
to split, trim and remove clips before saving an MP4.

Ship it in the base packages alongside Omacut, include it in the
preinstall restore/remove lists, and describe it in the manual. A
migration installs it on existing machines unless the user removed the
preinstalls.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:14:28 +02:00
David Heinemeier HanssonandClaude Opus 5.5 25f9795221 Flip Elsewhen's time format with t and line up its weather (#13448)
* Flip Elsewhen between 24-hour and AM/PM time with t

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Line up Elsewhen's weather in one column

The temperature and weather icon sat against each row's time, which is
narrower for "4:06" than "10:06", and shared the name's slack. The time
column now takes the widest time's width in every row, and a spacer holds
the weather against it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:12:04 +02:00
David Heinemeier HanssonandClaude Opus 5.5 773e19e145 Drive Elsewhen from the keyboard and summon it with Super+Ctrl+Alt+E (#13445)
* Navigate Elsewhen from the keyboard and summon it with Super+Ctrl+Alt+E

Up and down walk home and the cities, lighting the picked row and turning
the globe to it. Left and right move the clocks an hour, held until Escape
or close. Escape now clears a shifted time before leaving the globe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reach Add a city from the keyboard and shift time past a day

Down past the last city selects Add a city, where Return or Space opens the
search. The arrow keys no longer stop at twelve hours, and shifts past a day
read in days.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Delete the picked Elsewhen city from the keyboard

Delete (or x) removes the picked city and moves the cursor to the one that
takes its place. The picked row shows its × so the target is plain. Delete
now reaches every panel's deleteRequested, as it already does in the
clipboard and the menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Center bar glyphs on the open-panel underline

The 16px icon canvas in the 27px slot was snapped from 5.5 to 6, and the
glyph's fractional centering correction was snapped too, so glyphs sat up to
0.9px right of the underline, which centers on the slot. Both are now placed
exactly, and the geometry test measures against the slot to within 0.05px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:05:41 +02:00
David Heinemeier HanssonandClaude Opus 5.5 82979b9a18 Remove the IPC socket of each shell a test starts (#13443)
* Answer the Elsewhen panel over the shell's IPC socket

The Elsewhen panel arrived with a plain IpcHandler, so omarchy-shell
reached it only through the slower qs ipc fallback, and the test that
every first-party handler registers as ShellIpc failed on quattro.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove the IPC socket of each shell a test starts

The runtime smoke and screenshot sanity tests start the real shell from a
temporary copy and kill it, which leaves its IPC socket behind in
XDG_RUNTIME_DIR. A session that runs the suite a few times collected
dozens of them.

base-test.sh gains shell_ipc_socket, which derives a shell's socket as
omarchy-shell does, and both tests remove theirs in cleanup. The socket
test derives its fixture's socket through it too, so the helper cannot
drift from omarchy-shell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:57:46 +02:00
David Heinemeier HanssonandClaude Opus 5.5 0066ea216b Keep panel loaders across plugin changes so the OSD loads once (#13439)
Every plugin change handed the panel Instantiator a fresh array, which
rebuilds every panel. The panels were rebuilt four times in the first
700ms of startup, each rebuild starting a new asynchronous load of every
keepLoaded panel while the last was still in flight. Under load two OSDs
could run at once, each registering the osd IPC handler: the runtime
smoke test's intermittent "another handler is registered for target
osd".

The entries now live in a ListModel synced in place. A panel whose
plugin still loads the same way keeps its loader, so startup adds each
panel once, and a full plugin reload still clears the model to rebuild
from fresh code.

Under a six-core busy load the smoke test hit the duplicate in 1 of 5
runs on quattro and in none of 10 with this change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:29:06 +02:00
5929a9b80c Set Elsewhen's here by tapping a city on the globe (#13438)
A city on this machine's zone becomes homeCity, the zone's own city clears
it, and a city on another zone leaves it alone. Ported from omacom/elsewhen#5.

Co-authored-by: Shawn Yeager <shawn@shawnyeager.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:26:00 +02:00
David Heinemeier HanssonandClaude Opus 5.5 e7218e36f2 Move Elsewhen into Omarchy as omarchy.elsewhen (#13429)
* Move Elsewhen into Omarchy as omarchy.elsewhen

The world clock ships in the shell tree instead of its own package. A
migration renames existing bar entries with their settings and removes
the retired package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove Elsewhen's shelved Earth row, overlap band and sky tint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tidy Elsewhen's models, data helper and docs, and drop currency

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Restructure Elsewhen's panel and globe on the shared shell components

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Show Elsewhen's bar globe upright

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the shared city search for Elsewhen's globe jump bar

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep legacy Elsewhen entries single and retry offline geocodes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fetch Elsewhen's weather with curl instead of a Python helper

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:15:00 +02:00
David Heinemeier HanssonandClaude Opus 5.5 5c7cef1e78 Keep Hyprland from sliding the OSD in from the corner (#13436)
The OSD, the reminder flow and the Wi-Fi QR code now keep a surface that
parks as a 1x1 in the corner and grows when shown. They were missing from
the layer rule that turns Hyprland's layer animation off for the shell's
overlays, so Hyprland animated that growth: the OSD flew in from the left.

Add them to the rule, and test that every OverlayWindow's namespace is in
it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:07:27 +02:00
David Heinemeier HanssonandClaude Opus 5.5 c231097df7 Answer omarchy-shell calls over the shell's own socket (#13435)
* Answer omarchy-shell calls over the shell's own socket

Every omarchy-shell call started a qs ipc client, ~45ms of startup for one
IPC call: a theme switch makes two, and every script-driven OSD, toggle
refresh and lock query paid it too.

The shell now serves a socket in XDG_RUNTIME_DIR, named from its config
path and Wayland display as qs ipc selects its instance, and omarchy-shell
tries it first through socat, which starts in ~5ms. First-party handlers
register as ShellIpc, an IpcHandler that qs ipc still reaches, and the
socket calls only the functions a handler declares with their exact
argument count, allowed by name so QObject methods such as destroy() stay
out of reach.

When the shell ran nothing it answers SKIP, and omarchy-shell asks qs ipc
for its exact answer, so errors, third-party plugins and an unreachable
socket behave as before. A call that may have run is never retried: a
timeout or a connection closed without an answer reports the shell as not
responding.

omarchy-shell shell ping takes ~13-18ms instead of ~61ms, and omarchy-osd
reaches the screen in ~36ms instead of ~77ms. Output and exit status match
the qs ipc path across 26 calls, errors and quiet mode included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Only accept whole socket replies and retry only unmade connections

A reply cut off after its OK prefix passed for the whole answer, and an
empty reply with socat failing was retried through qs ipc although the
request might already have been delivered.

An answer now counts only once its record separator arrived. socat's own
errors join the reply, so only its connect error, a socket nothing
listens on, falls back to qs ipc beside an explicit SKIP; anything else
is reported as not responding rather than retried.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:46:00 +02:00
David Heinemeier HanssonandClaude Opus 5.5 616feda103 Add opt-in theme sync to herdr machines (#13430)
* Add a theme-set sample hook that mirrors themes to herdr machines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Mirror theme changes to herdr machines by default

Replaces the sample hook with omarchy-theme-sync, which omarchy-theme-set
starts detached after every theme change, and a theme-sync-off toggle that
stops a machine from both sending and receiving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rename omarchy-theme-sync to omarchy-theme-set-herdr-machines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Label the toggle Herdr Theme Sync and extract its menu guard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Answer the Herdr Theme Sync menu guard from omarchy-theme-set-herdr-machines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sync herdr machines one at a time and drop the dry run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check the theme sync toggle under the lock and stop special-casing this machine

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Make herdr theme sync opt-in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:31:02 +02:00
David Heinemeier HanssonandClaude Opus 5.5 387fcf599a Keep the lock wallpaper decoded so waking shows it with the password field (#13431)
The lock started decoding its wallpaper only once locked, with the cache
off, and first at the view's unsized native resolution. A machine
suspending right after locking froze that decode partway, so waking
showed the password field on a bare background and the wallpaper
popped in after it. On this machine the wallpaper took ~208ms to become
ready, and the suspend followed the lock by 66ms.

The lock service now keeps each screen's lock wallpaper decoded in the
image cache, as the lock view requests it: same URL, the screen's
logical size, PreserveAspectCrop. The view waits for its size and reads
from the cache, so the wallpaper is ready within ~3ms of the lock
starting. The version in the cached URL follows the file's mtime and
size, so a wallpaper overwritten in place still reloads.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:00:08 +02:00
David Heinemeier HanssonandClaude Opus 5.5 d3fb0284a8 Step the internal backlight in the shell instead of a script per keypress (#13427)
Each brightness key ran omarchy-brightness-display: resolving the focused
monitor and backlight device, checking for an Apple display, reading,
writing and reading back through brightnessctl, then omarchy-osd over a
qs IPC client. That is 64-133ms from keypress to OSD depending on load,
on keys that repeat while held.

The brightness up and down keys now dispatch global shortcuts. On an
internal panel the shell reads the level from sysfs, steps it by the
script's rules, writes it with one brightnessctl, and shows the OSD from
the level read back. A press overlapping one still being applied is
dropped, as the script's flock drops it. External and Apple displays
fall back to the script, which drives them over DDC or their own helper.
The absolute and precise brightness keys still run the script.

Across 24 steps from eight starting levels the shell and the script land
on identical levels, and keypress to OSD drops to ~31ms.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 19:34:36 +02:00
David Heinemeier HanssonandClaude Opus 5.5 25ead28f90 Trim launcher overhead for terminals and web apps (#13423)
omarchy-launch-webapp asked xdg-settings for the default browser with
BROWSER still set to omarchy-launch-browser, which sends it down a
slower path to the same answer: ~105ms instead of ~30ms on every web app
launch. Unset it, as omarchy-launch-browser already does.

omarchy-cmd-terminal-cwd found the focused terminal's shell with pgrep,
which scans all of /proc on every new terminal. Read the terminal's
child lists from /proc instead, keeping the newest child as before.

Web app wrapper overhead drops from ~111ms to ~34ms, and a new terminal
appears in ~77ms instead of ~90ms.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 19:03:54 +02:00
David Heinemeier HanssonandClaude Opus 5.5 804749970a Handle volume, media and notification keys in the shell (#13424)
* Step the volume in the shell instead of a script per keypress

Each volume key ran omarchy-audio-output-volume: several pactl calls in
bash, then omarchy-osd delivering the OSD over a qs IPC client, ~165ms
from keypress to OSD, on keys that repeat while held.

The volume up, down and mute keys now dispatch global shortcuts that the
media service handles over PipeWire, stepping, clamping, unmuting and
debouncing by the script's rules and showing the same OSD. It acts only
when the default sink is an ALSA sink, which is its own physical sink.
Any other default, a DSP chain or EasyEffects above all, falls back to
the script, which resolves the physical sink from the live routing on
every press. The precise +1/-1 keys still run the script.

Keypress to OSD drops from ~168ms to ~20ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Run media and notification keys in the shell without an IPC client

The media keys and the notification dismiss, invoke and history keys ran
omarchy-shell, starting a qs client for one argument-free IPC call.

A new ipc shortcut kind names such a call as target.method. The shell
hands it to the service that owns the target, which runs its own
IpcHandler function, so the key behaves exactly as the omarchy-shell
call did. A call missing from the list binds through omarchy-shell.

Dismissing a notification with SUPER+comma clears the popup in ~11ms
instead of ~40ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 19:02:59 +02:00
bf44355a98 Fix: Restore former window fullscreen state after screensaver exit (#3284)
* fix(screensaver): open in own special workspace instead of active

- The screensaver window rule sets fullscreen, and a workspace holds
  only one fullscreen window
- Launching it on the active workspace drops the fullscreen window
  already there, and leaves it windowed after the screensaver quits

* Keep a showing special workspace open through the screensaver

A monitor shows one special workspace at a time, so opening the screensaver on its own replaced a visible scratchpad, and it stayed hidden after the screensaver closed. Open it on the special workspace that is already showing instead; it covers that just as well, and the workspace is still there when it goes.

The test's stubbed event stream follows the one in #10870.

Co-Authored-By: Willem van Ede <37050539+WillemCR@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Hand focus back after the screensaver closes

Hyprland focuses a monitor when its special workspace empties, so on more than one monitor keyboard focus ended up wherever the last screensaver happened to close, rather than where it was. A detached reader on the launcher's event stream waits for the last one to go and focuses the original monitor again; the launcher itself still exits straight away, as the idle service expects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep a remapped screensaver off the regular workspaces

The terminal occasionally maps its window again as it closes, after its special workspace is gone. The launcher's workspace only applies to the first map, so the window landed on the focused monitor's regular workspace, where its fullscreen rule took fullscreen from the window there. On Hyprland 0.56.2 with two monitors this happened in 4 of 45 cycles, each time losing the user's fullscreen. A class rule now sends any later map to a hidden special workspace; the launcher's exec rule still wins on the first map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check for screensavers before waiting on their close

The focus watcher only looked for remaining screensavers after reading a closewindow, so one that never mapped, or closed while the launcher was still waiting on another monitor, left it blocked until some unrelated window closed, and then it pulled focus back to the old monitor. It now checks before each wait, and matches the exact class, passed through the environment so the screensaver's pgrep and pkill never see it in argv.

Co-Authored-By: Codex Medium <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Willem van Ede <37050539+WillemCR@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Codex Medium <noreply@openai.com>
2026-09-27 18:05:20 +02:00
David Heinemeier HanssonandClaude Opus 5.5 48de7823da Keep overlay surfaces mapped so they open sharp (#13419)
The menu, emoji picker, clipboard, OSD, reminder flow and Wi-Fi QR code
mapped a fresh surface on every open. Qt drew its first frames before
Hyprland sent the surface's fractional scale: the pixel ratio stepped
2, 1, then 1.6, so on a 1.6 display each overlay showed blurry for
~350ms before going sharp.

A new OverlayWindow keeps the surface. Hidden, it parks as a 1x1,
input-less layer below windows, off the overlay layer so it never
blocks direct scanout. Showing only resizes and raises it, so the scale
is already settled. Content stays hidden until the surface has grown,
so no 1x1 frame is stretched across the screen, and the window follows
the focused monitor each time it is shown. The image picker's own
fullscreen parking moves onto it too.

Each parked overlay keeps a Qt window alive: at rest the shell holds
~40 MiB more RSS and ~20 MiB more GPU memory.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:03:01 +02:00
David Heinemeier HanssonandClaude Opus 5.5 6762769bbf Open shell menus and panels through Hyprland global shortcuts (#13418)
Every binding that opened a menu or panel ran omarchy-menu or
omarchy-shell, which starts a qs client just to deliver one IPC call:
~60ms before the shell heard about the keypress.

The shell now registers a Hyprland global shortcut for each menu route
and panel in default/omarchy/shortcuts, and o.bind turns { menu = ... }
and { panel = ... } into hl.dsp.global for those, so a keypress spawns
nothing. A route or panel missing from the list binds through the
command as before. The default menu and panel bindings use the new form.

SUPER+SPACE opens the menu in ~31ms instead of ~94ms, measured from a
simulated keypress until the menu layer maps.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:02:01 +02:00
Saurabh SuriClaude Opus 5.5Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>David Heinemeier Hansson
fd961e5300 Speed up the theme switch's browser refresh (#13038)
* Speed up theme switch by backgrounding preload and parallelizing browser refresh

`omarchy theme set` was blocking on `omarchy-theme-switcher --preload` (~1.1s)
and serially refreshing every Chromium-family browser (~1.0-1.5s), making a
picked theme feel slow even though the shell recolored instantly.

- Background `omarchy-theme-switcher --preload` like the background cache
- In `omarchy-theme-set-browser`, skip the privileged policy write and browser
  refreshes when every existing `color.json` already has the requested color
- Refresh only running browsers, and run those refreshes in parallel

Fixes #12627.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* Harden the unchanged-color shortcut in omarchy-theme-set-browser

Review feedback identified that the original shortcut could vacuously skip
when no managed policy directories existed, and that it accepted a color.json
whose ownership or contents the privileged writer would have rewritten.

- Only skip when at least one managed directory exists and every existing
  color.json is a regular, root-owned 0644 file containing the canonical JSON
- Add OMARCHY_BROWSER_POLICY_DIRS as a test-only override so the setter's
  unchanged-color check does not depend on the host's real /etc policy files
- Update browser-policy-sudoers-test.sh to use that override, and make
  theme-set-browser-test.sh behavioral rather than grep-only

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* Find running browsers with one process scan during the policy write

Each browser check ran its own pgrep, and every pgrep rescans /proc,
~40ms apiece. Read the process table once with ps while the policy
writer runs, keeping its stdin so a terminal sudo prompt stays in the
terminal, then refresh the running browsers in parallel.

Chrome's fallback to plain google-chrome never ran: the refresh helper
returned success for a missing browser, so the || branch was dead. Pick
whichever Chrome binary exists up front instead.

The unchanged-color shortcut keeps its root:root 0644 check without the
fallback for hosts lacking stat -c, which Omarchy never runs on. Its
tests stub stat, ps and command discovery, so they neither depend on
nor touch the host's browsers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:24:04 +02:00
f3dbc464fc Decode theme backgrounds earlier, and at screen size (#13408)
* Decode the next theme background while the theme stages

The wipe waited 125-290ms after the transition arrived, decoding the new
wallpaper. Most stock wallpapers are WebP, which Qt decodes at full size
and scales afterwards, so a screen-sized sourceSize does not shorten it.

Start the decode earlier instead. omarchy-theme-set chooses the next
background and snapshots it before rendering templates, then sends a new
background prepare call in the background. The shell loads it into the
hidden incoming frame, so the transition finds it decoded. A prepare that
arrives after its transition is ignored, and one no transition claims is
dropped after five seconds.

The wipe now starts ~255ms after omarchy-theme-set begins instead of
~345-490ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Decode the wallpaper at screen size instead of shipped size

Wallpapers decoded at the resolution they were shipped at: a 5120x2880
stock wallpaper took 5120x2880 of RGBA on a 1920x1200 panel, and a
transition held up to three such frames. Bind sourceSize on the
displayed wallpaper and both transition frames to the screen's physical
size. PreserveAspectCrop treats it as the area to cover, so the image
still fills the screen.

Qt scales a decode up as well as down to cover sourceSize, so the native
size is read from the file header first with magick identify, and a
wallpaper smaller than the screen decodes at its own size. The images
wait for both sizes, so nothing decodes at native size first.

Ported from #8324 onto BackgroundMedia and the prepared incoming frame.
Measured with a 5120x2880 wallpaper, the shell's GPU memory at rest
drops from 264 MiB to ~148 MiB. The size probe delays the reveal by
~30ms, which the earlier prepare still more than covers.

Co-authored-by: Ryan Yogan <ryanyogan@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Ryan Yogan <ryanyogan@gmail.com>
2026-09-27 16:11:36 +02:00
David Heinemeier HanssonandClaude Opus 5.5 7e8d35efb3 Render theme templates in one awk pass (#13406)
A theme switch waits on omarchy-theme-set-templates before the shell
transition starts, and it spent ~670ms spawning processes: a subshell
per color for its rgb form, a grep per template per function family, an
awk per mix, and a sed per template matching every line against
hundreds of patterns.

Helpers now return through REPLY, one grep finds the function tokens
across all templates, and one awk computes the mixes with the same
floating point rounding and renders every template. Output is byte for
byte identical across all shipped themes. Generation takes ~65ms, and
the transition starts ~130ms after omarchy-theme-set begins instead of
~740ms.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:29:34 +02:00
Spencer Bull 731fc8d051 Merge pull request #13350 from omacom/fix/sudo-passwordless-migration-path
Run the passwordless sudo helper from $OMARCHY_PATH in its migration
2026-09-27 08:18:53 -05:00
David Heinemeier HanssonandClaude Opus 5.5 24f1243120 Make the theme picker open instantly (#13403)
* Run menu summon actions in-process

A menu action that only summons another shell plugin spawned bash and a
qs ipc client to ask this same shell to do it, about 60ms of the path.
Call shell.summon directly instead, and fall back to bash when the call
is refused or the action is anything more than a bare summon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Open the theme picker from rows held in the shell

Opening the theme picker ran omarchy-theme-switcher to rebuild its index
and then made a second IPC call, about 170ms before the picker mapped.
The picker now holds the theme rows itself, opens from them at once, and
refreshes them behind the open via omarchy-theme-switcher --print-rows.
It applies the chosen theme with omarchy-theme-set directly, so
omarchy-theme-set no longer preloads the picker.

From the keybinding to the overlay mapped drops from ~245ms to ~83ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep the image picker surface mapped between opens

Each open mapped a fresh surface, which rendered its first frames before
Hyprland sent its fractional scale: the pixel ratio stepped 2, 1, then
1.6, so the picker flashed blurry for ~130ms and re-uploaded every
thumbnail texture. Keep the surface and park it transparent and
input-less on the bottom layer while closed, since anything on the
overlay layer blocks direct scanout for fullscreen apps. It follows the
focused monitor on each open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:10:49 +02:00
David Heinemeier HanssonandClaude Opus 5.5 e558224acf Loosen existing offline installs' exact Node pin (#13400)
5db4a401 fixed the installer, but machines already installed offline
still pin Node to the ISO's exact version, so mise up never updates it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:01:16 +02:00
David Heinemeier Hansson c5b4db77d6 Move AUR more out of sight
Now that every package we offer an installer for no longer needs it
2026-09-26 23:11:07 +02:00
David Heinemeier HanssonandClaude Opus 5.5 8cd727dfdf Stop the update sleep inhibitor without a signal error (#13361)
Stopping stay-awake sends TERM to the held process, which was an exec'd
sleep. systemd-inhibit reports a child killed by a signal as an error, so
every update ended with "'/usr/bin/setpriv' terminated by signal TERM."

The held process now stays a shell that traps TERM, kills its sleep, and
exits cleanly.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:07:58 +02:00
Spencer BullandCodex XHigh 92ef7e9407 Reach the migration's helper through $OMARCHY_PATH rather than by name
By name, the sudo call resolved through secure_path, which puts /usr/local/bin ahead of /usr/bin and falls back to the caller's PATH where no secure_path is set, so an install could run a different helper than the pinned path did. $OMARCHY_PATH/bin is the package's symlink into /usr/bin on an install and the checkout under a dev link, so neither PATH nor secure_path takes part.

Co-Authored-By: Codex XHigh <noreply@openai.com>
2026-09-26 14:49:34 -05:00
Spencer BullandCodex XHigh 2ffae36a18 Resolve the passwordless sudo helper by name in its migration
Pinning /usr/bin ran the packaged helper even where the migration came from somewhere else. Under a dev link the migration is read from the checkout while /usr/bin still holds the last installed package, and a package predating __migrate prints its usage and fails the update. By name, the unprivileged call goes through PATH and the sudo call through secure_path, which is how other migrations reach their helpers and which lands on /usr/bin on an install.

Co-Authored-By: Codex XHigh <noreply@openai.com>
2026-09-26 14:43:00 -05:00
David Heinemeier HanssonandClaude Opus 5.5 e1614f2bdb Ask for the sudo password once per omarchy update (#13323)
* Ask for the sudo password once per omarchy update

Every sudo call in omarchy update prompted, because the no-update wrapper
covered the whole run on top of per-phase revokes, and stay-awake revoked
the timestamp on its own entry and exit. A single update could ask four
times before the snapshot finished (#13319).

Authorize once, right after confirmation, starting from a revoked
timestamp so the prompt always belongs to this update. A background
keepalive refreshes it until the update is done. Prune, snapshot,
stay-awake, keyring, system packages, migrations, orphan removal, service
restarts, the post-update hook, and mise all share that authorization.

AUR builds run third-party PKGBUILD code, so they move to the end and run
cold: the keepalive stops, the timestamp is revoked, and yay and any bare
sudo use the no-update wrapper. The timestamp is revoked again after AUR
and on every exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep the single authorization for passwordless sudo and ttyless inhibition

Authorize by running a command instead of sudo -v. Under the default
verifypw=all, -v prompts even when passwordless sudo is enabled, which
would have added a prompt those users never had.

Inside an update without a terminal, stay-awake now reuses the update's
authorization with a non-interactive sudo instead of asking again through
polkit. It falls back to polkit only if that authorization is gone.

The test sudo refuses a cold non-interactive call, as the real one does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 15:31:40 +02:00
Ryan Hughes 7b336b1b0d Merge pull request #11486 from omacom/fix-remote-neovim-yanks
Migrate remote Neovim providers to reliable clipboard yanks and paste
2026-09-25 14:57:16 -05:00
Ryan Hughes 289f36ab97 Require the corrected Neovim package before clipboard migration 2026-09-25 15:35:10 -04:00
Erik Melton d201fb9564 Merge pull request #9467 from AFOliveira/codex/om-sec-12-update-inhibitor-identity
[codex] OM-SEC-12: Bind update inhibitor cleanup to process identity

Reported-by: Afonso "AFOliveira" Oliveira
2026-09-25 15:56:39 +02:00
Erik Melton b5aa835fb3 Merge pull request #13046 from AFOliveira/fix/color-reference-cycles-20260923
Guard against cyclic theme colour references
2026-09-25 15:54:32 +02:00
David Heinemeier Hansson c599d23a39 Document automatic screenshot saving (#13218) 2026-09-25 07:28:27 -05:00
Spencer Bull c3e67f5d40 Merge pull request #12844 from omacom/hermes-desktop-default-agent
Install Hermes for the default agent as the desktop app's self-updating runtime
2026-09-24 23:41:18 -05:00
Ryan Hughes 5bc098d749 Merge pull request #11690 from Yarlord/fix-hybrid-nvidia-vaapi
Only force NVIDIA VA-API/GLX env when NVIDIA drives the display
2026-09-24 21:52:04 -05:00
Ryan Hughes 28405af27f Request reboot for hybrid NVIDIA fix and streamline tests 2026-09-24 22:49:41 -04:00
5630c6f561 Count Hermes run as a module from an activated venv as Hermes too
With the venv active, a gateway started by hand is `python -m hermes_cli.main gateway run`: the interpreter is bare, the executable resolves outside the runtime, and no runtime path is on the command line, so the removal took it for a stranger and refused over the files it holds rather than closing it. Hermes's own package named with -m is the program.

Co-Authored-By: Codex XHigh <noreply@openai.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 21:41:08 -05:00
14eb9430c3 Close Hermes on removal instead of asking the user to close it
Remove > AI refused whenever anything had Hermes's files open and told the user to close it and try again, and the thing open was Hermes: the agent in the terminal that choosing it as the default agent leaves running, the desktop app, a gateway. Those are the removal's to close. It now stops the gateway unit that upstream's `hermes gateway install` wrote, since that unit starts the runtime about to be deleted and would start it again the moment it was killed, then ends every process whose program lives in that runtime or in the package, and only then refuses over whatever is left, which is somebody else's: an editor on a skill, a shell sitting in ~/.hermes, a writer on the state database. Both are judged by the program, never by a later argument or by the home served, so an editor opened on a runtime file is the user's and a unit running a Hermes kept elsewhere is left alone whatever home it serves; for an interpreter the program is the script it runs, so a gateway started by hand as `python .../hermes gateway run` is found too. The refusal comes before anything is touched, so a run that stops leaves Hermes running as it was. A unit that will not stop still aborts the removal, as dropping the package would strand a live gateway on deleted code.

Co-Authored-By: Codex XHigh <noreply@openai.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 21:35:46 -05:00
bjarneo 93e8cd56b1 Merge pull request #12774 from MicahelE/fix/fastfetch-swap-label
Fix Fastfetch swap label spacing
2026-09-24 22:47:46 +02:00
bjarneo 28ceaae70e Merge pull request #7718 from OldJobobo/feature/remember-theme-wallpapers
Remember the selected wallpaper for each theme
2026-09-23 20:39:46 +02:00
OldJobobo 0385610cfe Merge quattro and preserve per-theme wallpaper memory 2026-09-23 10:41:32 -07:00
bjarneo c737c4975f Merge pull request #10134 from Macho0x/fix/vscode-theme-reload
Make VS Code-family editors reload the Omarchy theme when it changes
2026-09-23 19:04:39 +02:00
bjarneo b76a35d633 Merge pull request #12864 from anandude/fix-catppuccin-colorscheme
Point catppuccin theme at its flavour colorscheme
2026-09-23 18:54:59 +02:00
bjarneo a54668dae3 Merge pull request #9964 from ivanskodje/fix/vscode-theme-invisible-states
Fix invisible VSCode status bar labels and current line
2026-09-23 18:46:48 +02:00
bjarneo 9217abeea2 Merge pull request #10016 from ivanskodje/fix/vscode-theme-invisible-hover-states
Fix invisible hover and active states in the generated VS Code theme
2026-09-23 18:46:00 +02:00
Afonso Oliveira f5ad8a0b9e Stop cyclic shell color references
Resolve color references iteratively and use the caller's fallback when a reference repeats. Preserve normal palette roles, color parsing and gradient first stops.

Reported-by: Aleksej <aleksejhairov@yandex.ru>
2026-09-23 09:19:45 +01:00
Anandakrishnan CV 8d6a738269 Describe mocha as compatible with older plugin checkouts 2026-09-23 10:57:42 +05:30