Commit Graph
255 Commits
Author SHA1 Message Date
David Heinemeier HanssonandGitHub 35ebe2dfab Merge pull request #6383 from scottjones/menu-search-rank-apps
Rank installed apps above equally matching menu entries
2026-07-26 15:09:04 -07:00
David Heinemeier HanssonandClaude Opus 5 f375113fe3 Find the omarchy-pkgs checkout for PKGBUILD coverage
The three hardcoded candidates are all relative to a sibling of the omarchy
checkout, so a clone anywhere else, such as ~/Work/omacom/omarchy-pkgs, found
nothing. The block then crashed on read_text() and the whole test exited on an
unhandled traceback, which reads as an unrelated failure rather than a missing
checkout.

Add the omacom layouts, an OMARCHY_PKGS_PATH override taking either the
checkout or its pkgbuilds directory, and a named failure listing where it
looked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:53:29 -07:00
David Heinemeier HanssonandClaude Opus 5 425c3ff84d Only check for pending migrations at login
omarchy-update-user-notify.path watched /usr/share/omarchy/migrations, but
pacman writes that directory during every update, including the blessed
omarchy update, which runs omarchy-migrate a step later. The watcher fired a
critical notification for the migrations the update was already applying in
the visible terminal. A watcher cannot tell that apart from a bypassed
pacman -Syu, so the only trigger that never collides with a running update is
a once-per-login check.

The service that already ran at graphical-session.target is now the whole
mechanism, renamed after the command it runs. That is also all the second-user
case needs: markers are per-user, so anyone who did not run the update finds
them missing at their next login.

Login timing means the toast can be sent before the shell has claimed
org.freedesktop.Notifications, so the notifier waits for a live server first.
The wait is omarchy-first-run's, lifted into omarchy-notification-wait rather
than duplicated.

The package keeps omarchy-update-user-notify.service as a symlink onto the new
unit. Existing users hold an absolute wants symlink to the old path, and the
migration that repoints it only runs for users who run an update, which is the
opposite of who the notifier is for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:53:29 -07:00
David Heinemeier HanssonandClaude Opus 5 171b6374c6 Keep the zram fallback until its replacement is installed
zram-generator creates no device at all when nothing configures one, so the
/etc copy is the only thing holding up swap until the vendor drop-in lands.
Removing it early costs a machine its zram entirely, not just its tuning.

The update pipeline installs packages before it runs migrations, so a packaged
machine always has the drop-in by then. A dev checkout does not: omarchy-update-dev
pulls migrations from a release the installed package has never seen, and no
ordering of the pipeline can produce a file that has not been built yet.
Checking for the drop-in is what makes the removal safe rather than well
sequenced.

The tests pinned the drop-in path to a fixture as well. Left at the real path
they would pass or fail on whether the machine running them happened to carry
the packaged copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:37:22 -07:00
David Heinemeier HanssonandClaude Opus 5 187c2ecf7f Size zram the way Fedora does
zram-size = min(ram, 8192) is Fedora's default since F34, where
zram-fraction=1.0 with max-zram-size=8192 has run on desktops for five
years. The previous min(ram / 2, 8192) halved that on every machine
below 16G with nothing behind the fraction; at 16G and above the two
already agree.

A changed size only reaches a running device on the next boot, so the
migration resizes an empty device now and asks for a reboot otherwise.
It asks the generator what the config evaluates to rather than
repeating the expression here, and does neither when the device already
matches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 12:58:02 -07:00
Scott JonesandClaude Opus 5 0c6b07e2c4 Trim ranking comments
Say it once and match mergeAppRows: app rows sort after all menu items,
not interleaved among them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:34:43 -04:00
Scott JonesandClaude Opus 5 ab798d80b2 Rank installed apps above equally matching menu entries
Searching the menu for an installed app buried it: "brave" listed Setup >
Defaults > Browser, Install > Browser and Remove > Browser ahead of the
Brave app itself. All four are exact label matches scoring 0, so the
tiebreak falls to declaration order, and mergeAppRows appends app rows
after every static item.

Bias app rows ahead of menu entries that match equally well. The bias is
smaller than the gap between match tiers, so a menu entry that matches the
query better still sorts first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:25:31 -04:00
David Heinemeier HanssonandClaude Opus 5 2e04f093c1 Ship zram tuning as a vendor drop-in
pacman checks file conflicts during transaction prepare, so the --overwrite
entry added alongside /etc/systemd/zram-generator.conf never got to run: the
copy of omarchy-update-system-pkgs driving the upgrade is the one already on
disk. Every machine carrying archinstall's copy aborted the transaction.

zram-generator.conf(5) reserves /etc for the local admin and has vendors ship
snippets under /usr/lib/systemd/zram-generator.conf.d/, where drop-ins outrank
the main config file. Nothing collides, and the tuning wins whether or not
archinstall's file is still around.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LjeyQZsNBxqyYy9z8KaKm7
2026-07-25 14:01:38 -07:00
David Heinemeier HanssonandClaude Opus 5 9ddcec272d Lock the screen before suspend instead of racing logind for it
Closing the lid could suspend the machine with the session still exposed.
omarchy-sleep-lock held a delay inhibitor and waited for Quickshell to report
the session secure, but a delay inhibitor is a timer rather than a promise:
logind suspends once InhibitDelayMaxSec expires, locked or not. The default is
five seconds, and closing the lid also reconfigures displays -- exactly when
the lock service is waiting for the screen set to settle before it can secure.

The race is now off the critical path. switch:on:Lid Switch runs
omarchy-system-lid-close, which requests the lock the moment the lid closes,
before logind has decided to suspend at all, so the inhibitor window usually
finds the session already secure. A docked lid close does not suspend
(HandleLidSwitchDocked defaults to ignore) and must not lock either, since that
is clamshell mode still in use on the external display, so the handler gates on
the same closed-and-undocked pair logind itself keys on.

Suspends that never touch the lid -- idle timeout, the system menu, low battery
-- still arrive through the inhibitor, so that path gets room to work too. The
shipped drop-in raises InhibitDelayMaxSec to 15s, and the helper derives its
budget from logind's live InhibitDelayMaxUSec rather than assuming the drop-in
landed: a machine that has not reloaded logind yet, or that carries its own
override, gets a budget that fits what logind will actually allow. It leaves
logind a fifth of its own window and caps at 12s, so a hand-raised window
cannot strand a closed laptop awake in a bag.

The wait itself had three defects. Its deadline arithmetic read EPOCHREALTIME
assuming a period, so under any comma-decimal locale the subtraction parsed as
bash's comma operator and silently voided the deadline, leaving only the
attempt counter to stop it. The lock request shared the status polls' timeout
and exited on first failure, so a shell 300ms slow meant suspending unlocked;
it now has its own budget and is simply retried, since asking again is
idempotent. And a refusal the shell reports on stdout with a zero exit --
missing-pam -- read as success, burning the whole window on a lock that could
never happen.

Every call is bounded by what is left of the budget rather than by an estimate
of what the step should cost, so the deadline holds on hardware slower than
anything the constants were fitted to.

Failure is still possible and it used to be silent. It now writes to the
journal and raises a critical notification, which lands on the screen the user
unlocks into.

Incidentally, monitor-recovery-test asserted lid probing against
omarchy-hw-clamshell after that logic moved to omarchy-hw-laptop-closed. It
aborted the file under set -e, skipping the nine assertions behind it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:35:17 -07:00
David Heinemeier Hansson 57923f0378 Support positional section when moving bar widgets 2026-07-25 11:14:04 -07:00
David Heinemeier Hansson fc4caf3c60 Extract notification center bar widget 2026-07-25 10:59:09 -07:00
David Heinemeier HanssonandClaude Opus 5 e880cf77cc Alert on tmux output while its terminal is unfocused
tmux only raises an activity flag for windows that are not currently
selected, so a long-running command in the window you left selected --
the common case, since you switch away by moving your Hyprland focus
elsewhere rather than by selecting another tmux window -- finished
silently and the indicator never lit.

Windows now also count as waiting when they are selected in an attached
client, every client showing them is unfocused, and their window_activity
is newer than an @omarchy_unfocused_activity watermark. The watermark is
stamped by a new `track` subcommand wired to client-focus-in/out and the
existing select-window hooks, so it records where attention last was.
The hooks pass #{window_id} and #{window_activity} as arguments, which
tmux expands when the hook fires; run-shell -b would otherwise let output
arriving during the handoff be swallowed by the new watermark. The focus
hooks take index 100 to leave a user's own bindings alone.

That state has no hook of its own, so it needs polling to be noticed.
The probe therefore moves out of the indicator and into a service plugin,
alongside nightlight and battery. A bar surface exists per monitor and
each one instantiates every indicator twice, once per block, so a timer
on the indicator meant a shell-out per instance per tick -- four probe
processes every three seconds on a two-monitor machine, forever. One
service polls for the whole shell instead.

Sharing the state also fixes what per-instance polling would have papered
over: each indicator used to own its own count, so a timer-driven update
only refreshed the bar it ran on and left the other monitors stale.
Refreshes still arrive over the existing indicator broadcast, which now
coalesces into a single run no matter how many bars relay it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018CC9kSQv8ZEogaxDoeKBzL
2026-07-25 07:19:02 -07:00
David Heinemeier HanssonandClaude Opus 5 ec1553d720 Let tmux alerts actually reach the bar
The tmux alert hooks fire omarchy-shell through run-shell, and run-shell
builds its own environment rather than passing the server's along, so the
hook ran without WAYLAND_DISPLAY. qs matches instances by display, so every
refresh from a bell died at "omarchy-shell is not running", which -q then
swallowed by design.

That left the indicator with no way to turn itself on. Its five second poll
only runs while the indicator is already active, so an alert raised after
startup was invisible until the shell happened to restart with the window
still flagged. The icon appeared roughly never.

Recovering the display from the socket in XDG_RUNTIME_DIR fixes it in the
one place that needs it, and covers any other caller reaching the shell from
a stripped environment rather than just the tmux hooks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 07:03:58 -07:00
David Heinemeier HanssonandClaude Opus 5 c6ad61448f Stop the launcher from listing an app more than once
The apps list rebuilt its rows by writing into the maps held by the menu's
items and itemOrder var properties. Writing into an object owned by a QML var
property is not reliable: the same row object written into a plain JS object
always lands, but written through the property it occasionally arrives with the
key created and the value undefined. One write per rescan was lost, on a
different app each time.

A lost write left an id in itemOrder with no item behind it. The old purge only
deleted app rows it could find in items, so the orphan survived the next merge,
the add loop appended a second row for the same app, and the list grew by one --
permanently, and again on every later rescan. Touching a single desktop file
fires around a dozen merges, because the entry model emits valuesChanged per
insert and removal while it reconciles, so duplicates piled up quickly: nine
YouTube rows on the reporting machine, and Alacritty doubled before that.

The bookkeeping moves into MenuModel as two pure functions that build fresh maps
for the caller to assign in one shot, so the fragile write disappears. They also
make the merge self-healing rather than merely correct-when-nothing-is-lost: an
id with no item is dropped instead of carried forward, and an id is listed once
even when two desktop entries claim it -- so no single dropped write can compound
into a duplicate row again. The bash-backed providers behind the font and power
profile lists had the same latent bug and get the same treatment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJQJfHXXApUk6En8EZisHg
2026-07-24 19:42:07 -07:00
David Heinemeier Hansson be781574d1 Let the invitation terminals actually open
Clicking the Voxtype or fingerprint invitation did nothing. The launcher
execs setsid, which forks because the transient unit's main process is
already a process group leader, so the unit exits within milliseconds and
systemd's default control-group kill took the still-starting terminal down
with it. Run those units with KillMode=process instead.

The invitation tests had also drifted from the two-unit design and were
failing; teach the systemd-run mock to run the command it is given.
2026-07-24 18:57:51 -07:00
David Heinemeier HanssonandClaude Opus 5 248659de5a Give every OSD card exact padding on all four sides
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LfqzqpU1T6pQRsmpbGxBs5
2026-07-24 16:48:14 -07:00
David Heinemeier HanssonandClaude Opus 5 528a4343a9 Add omarchy-hyprland-monitor-laptop for internal display lookup
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-24 12:28:55 -07:00
David Heinemeier HanssonandGitHub 1d4077287b Merge pull request #6353 from zainsyedz/fix-clamshell-internal-monitor-config
Respect internal monitor config in clamshell recovery
2026-07-24 10:31:00 -07:00
David Heinemeier HanssonandGitHub a62e95d2e1 Merge pull request #6367 from khpark43/fix-bar-plugin-json-values
Fix bar plugin JSON value validation
2026-07-24 10:24:34 -07:00
Kyunghyun Park 876d6bcd23 Fix bar plugin JSON value validation 2026-07-25 01:33:30 +09:00
David Heinemeier Hansson 5a046035d5 Fix shell restart across dev path changes 2026-07-24 09:31:09 -07:00
David Heinemeier HanssonandClaude Fable 5 2c8639ffbd Persist workspace layout toggles across Hyprland reloads
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:03:43 -07:00
David Heinemeier Hansson 202296324a Keep display scaling options consistent 2026-07-23 20:37:25 -07:00
David Heinemeier HanssonandClaude Fable 5 00eae9a5ef Invite fingerprint setup after the first system update
The setup installs packages (libfprint-git, fprintd, usbutils), which
isn't reliable on a fresh install before the databases have been
synced. Defer the invitation with a post-update hook, like Voxtype.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 19:25:06 -07:00
David Heinemeier Hansson 86f616cfb3 Add emoji picker to Trigger menu 2026-07-23 19:23:50 -07:00
David Heinemeier Hansson 9b09a3c142 Clear update inhibitors before reboot prompt 2026-07-23 18:58:55 -07:00
David Heinemeier HanssonandClaude Fable 5 a0e6d65ba8 Keep universal clipboard shortcuts working in terminals
Sending the shortcut through wtype's virtual keyboard let the physically
held SUPER merge into the injected chord, so terminals saw
SUPER+CTRL+Insert instead of CTRL+Insert, and injected CTRL+V could even
trigger the SUPER+CTRL+V clipboard manager bind. Send via send_key_state
with no window target instead: explicit mods go straight to the focused
surface, layer-shell panels included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnMDbwJXQA3fvW8o8Pwr97
2026-07-23 18:08:31 -07:00
David Heinemeier Hansson 63955e787f Hide unavailable display scale options 2026-07-23 18:06:19 -07:00
David Heinemeier Hansson 58ec29f20a Move direct boot command to setup 2026-07-23 17:43:36 -07:00
David Heinemeier HanssonandClaude Fable 5 5b130c1063 Make tmux alert parsing and focus more robust
Session names can contain pipes but never colons, so split fields on
colons instead. Remember refreshes that arrive while the indicator is
already polling, and jump to the most recently used tmux client rather
than an arbitrary one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:40:47 -07:00
David Heinemeier HanssonandClaude Opus 4.8 a382be2645 Indicate and jump to tmux panes waiting for attention
Show a bar indicator whenever tmux has flagged a window, the same state
that highlights the tab, and jump to it on click or with Super + Ctrl + J.
Tmux hooks push the state to the shell, so nothing polls while no pane is
waiting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 17:40:47 -07:00
David Heinemeier Hansson 6d7268eb65 Support clipboard shortcuts in shell panels 2026-07-23 17:12:39 -07:00
David Heinemeier Hansson c844277749 Activate dev checkout before updates 2026-07-23 17:05:29 -07:00
David Heinemeier Hansson ed9e68957f Match display scale highlights to clean scales 2026-07-23 16:48:29 -07:00
David Heinemeier Hansson 2d3d1e2a3c Only offer webcam recording when available 2026-07-23 16:33:12 -07:00
David Heinemeier Hansson 7798ed34b1 Keep apps menu alphabetically sorted 2026-07-23 16:32:31 -07:00
David Heinemeier Hansson 8e549c27d0 Update active dev checkout with system 2026-07-23 15:28:32 -07:00
David Heinemeier Hansson 7a9947a732 Combine the Omarchy menu and the launcher
Now that we can deep search, they don't need to be different
2026-07-23 15:04:22 -07:00
David Heinemeier Hansson 347997871e Defer channel switch reboot until after update 2026-07-23 14:19:24 -07:00
David Heinemeier Hansson f911851319 Link dev channel to home checkout 2026-07-23 14:01:36 -07:00
David Heinemeier HanssonandClaude Opus 4.8 540e411edf Show fingerprint on lock screen and polkit, gated by lid state
Bring the fingerprint affordance to the Quickshell lock screen and polkit
dialog, matching what hyprlock did on master.

Lock screen: render the md-fingerprint glyph inside the password field's
right edge when a sensor is enrolled, reserving space so long passwords
never run under it.

Polkit dialog: show one method at a time. When a sensor is enrolled and
the reader is reachable, the dialog is just the centered fingerprint icon
(square card); the moment PAM asks for a password it switches to the
password field. Detects pam_fprintd anywhere in the auth stack now that a
gate can precede it.

Lid awareness: a closed lid means the reader is unreachable, so both
surfaces fall back to the password. polkit gets a pam_exec clamshell gate
(auth [success=1 default=ignore] before pam_fprintd) so a shut lid drops
straight to the password prompt instead of blocking on the reader for the
pam_fprintd timeout; the lock screen hides the icon and skips scanning.

The gate points at the fixed /usr/bin path the package always provides so
it survives switching between package installs and dev-link. A migration
adds the gate for existing fingerprint setups.

New helper omarchy-hw-laptop-closed (pure lid state); omarchy-hw-clamshell
now composes it with the external-monitor check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 13:51:07 -07:00
39ca9135c4 Fix headless bar widget test skip (#6338)
Co-authored-by: almondsun <maramirezes@outlook.com>
2026-07-23 07:53:57 -07:00
Zain Syed e7fc47a44a Respect internal monitor config in clamshell recovery 2026-07-23 13:53:49 +05:30
David Heinemeier Hansson 1b6ab15331 Fix command detection in Hyprland Lua 2026-07-22 21:48:30 -07:00
David Heinemeier Hansson fc6168af86 Load Voxtype hotkeys when installed 2026-07-22 21:39:15 -07:00
David Heinemeier Hansson 7361b48461 Refresh shell test fixtures 2026-07-22 17:31:14 -07:00
David Heinemeier Hansson 43e95217f2 Reload weather location when opening panel 2026-07-22 17:31:14 -07:00
David Heinemeier Hansson 475211a5d4 Make Copy URL use native clipboard host 2026-07-22 17:25:28 -07:00
David Heinemeier Hansson 60a550a646 Add --fit OSD option and use it for system shutdown/reboot/logout 2026-07-22 16:33:54 -07:00
David Heinemeier Hansson 36329cee7a Launch 1Password installer when missing 2026-07-22 16:10:45 -07:00