gum_env.lua is fed into the compositor environment via hl.env at login,
but that environment is captured once and is not refreshed on a theme
switch. Terminals launched through uwsm-app inherit the caller's
environment (scopes don't read systemctl --user set-environment), so a
floating terminal would show the login-time theme's gum colors.
Extract the export logic into a sourced omarchy-restart-gum helper and
source it from the presentation terminal launcher so gum widgets match
the active theme.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
system-config-printer ships /etc/xdg/autostart/print-applet.desktop, so
installing it for the printer GUI also starts a ~24 MiB Python tray applet
in every session whose only job is print-job notifications. Override it
with a Hidden=true entry in config/autostart (same mechanism already used
for fcitx5 and limine-snapper-notify), so the applet no longer autostarts.
Printing and the system-config-printer GUI are unaffected.
Verified: with the override in place, systemd's xdg-autostart generator no
longer produces app-print\x2dapplet@autostart.service.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guards the dock-gating in omarchy-hyprland-monitor-watch: the poll runs
only when omarchy-hw-laptop and an external monitor are both present, via
sync_poll_state, with the event loop fed by process substitution so it
can start/stop the poll. Behaviourally simulated (stubbed hw checks +
synthetic monitor events): the poll runs only while docked and stops on
undock; a desktop or undocked laptop never polls.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The media service ran a 500ms repeat timer calling syncPlayingOrder() for
the whole session — two CPU wakeups a second even with nothing playing.
syncPlayingOrder only depends on the set of MPRIS players and each
player's isPlaying state, both of which the Mpris service already signals.
Replace the timer with onPlayersChanged (players appearing/disappearing)
plus an Instantiator that connects isPlayingChanged for each live player,
and a Component.onCompleted for the initial sync.
Verified with a test MPRIS player: play/pause and player add/remove all
update the active player with no periodic timer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reconciliation poll (added in "Fix clamshell display recovery") is a
backstop for lid transitions the Hyprland switch binds can miss across
suspend/resume. But the internal panel is only ever disabled while the
laptop is docked — lid shut with an external monitor active — so the poll
has nothing to reconcile when undocked. Running it 24/7 on every laptop
wakes the CPU every 2s for no reason, which is exactly the case where
battery matters most.
Start the poll when an external monitor is present and stop it when the
last one goes away (driven by the socat monitor-add/remove watch), gated
on omarchy-hw-laptop. Undocked laptops and desktops now never poll; a
docked laptop keeps the 2s recovery poll. Switch the event reader from a
pipe to process substitution so it runs in the main shell and can manage
the poll's lifetime.
Desktop verified: no poll runs even with external monitors attached. The
docked-laptop start/stop path needs a check on real laptop hardware.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
omarchy-hyprland-monitor-watch ran poll_clamshell_state() every 2s for
the whole session, and each pass forks omarchy-hyprland-monitor-clamshell
plus its hyprctl/jq/monitor-* children. Clamshell (lid) handling can only
ever apply to a machine with a lid, so on desktops and VMs this was pure
churn — the single largest remaining source of idle process wakeups
(~3 forks/sec on an otherwise idle desktop).
Gate the poll behind a new omarchy-hw-laptop helper (lid button, or a
laptop DMI chassis type as a fallback). The event-driven socat watch on
Hyprland monitor add/remove is unchanged, so monitor hotplug still
reconciles everywhere; only the periodic lid poll is now laptop-only.
Verified on a desktop: steady-state omarchy-hyprland-* forks drop from
~3/sec to zero, socat watch still present.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pill was populated by polling omarchy-network-status (previously
every 3s, then 10s). Derive kind/signalStrength directly from the
Quickshell.Networking service instead: wired-connected → ethernet
(preferred, matching the default-route device), else a connected Wi-Fi
network → wifi with its signal, else disconnected. The service's
properties are change-notified, so the pill updates on connect/
disconnect and signal changes with no polling and no subprocess.
Removes the last periodic poll from the bar; the label/frequency pill
fields were write-only and are dropped.
Verified live: pill tracks ethernet → disconnected → ethernet in real
time as the wired device goes down and up.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each `bash -lc` starts a login shell that re-sources the profile
(mise activation, /etc/profile.d) on every invocation — ~16 forks per
call versus ~2 for `bash -c` — which taxes every menu/panel/launcher
action the shell shells out for. The session already exports PATH and
env to the shell, so omarchy commands resolve fine under `bash -c`.
Switch the internal/omarchy-owned spawns (theme+background switches,
brightness, monitor scaling, DNS, lock/fingerprint, keyboard-layout
probe, voxtype status, and the `:`/printf state-file writes) to
`bash -c`. Leave `bash -lc` on the sites that run user-configurable
commands (custom bar-widget exec, menu provider/guard scripts,
launcher scan commands, configurable idle/screensaver command), where
a user's command may rely on their login environment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The monitor panel polled `omarchy-monitor-state` every 5s (26 forks per
call) and the network panel polled `omarchy-network-status` every 3s,
both with `running: true` rather than gating like the sibling panels.
Monitor: gate the poll on `opened` (brightness/scale are only shown in
the open panel, which already refreshes on open) and drive the bar glyph
from Quickshell.screens instead of the polled display list, so monitor
count stays correct without polling.
Network: the bar pill needs live-ish state, so keep polling but at 10s
instead of 3s. (Deriving the pill from the native Networking service
would remove the poll entirely; left as a follow-up since it needs Wi-Fi
state testing.)
Idle fork rate drops ~20/s to ~14/s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The indicators widget broadcast a refresh every 2s, fanning out to four
status subprocesses (nightlight, idle, reminder, screen-recording), and
NightLight/StayAwake each ran an additional 5s poll. On an idle desktop
this was the dominant source of process churn (~33 of ~53 forks/sec in a
VM). The state-changing commands already push `omarchy.indicators
refresh` over IPC, so the polling was redundant.
Drop the 2s broadcast and the two 5s timers; indicators now refresh at
startup and on the IPC push. Also fix three callers that pushed to the
wrong target `Indicators` instead of `omarchy.indicators` (screen
recording, notification silencing, and the omarchy-shell help example) —
those pushes silently failed and only appeared to work because the poll
masked them.
Idle fork rate drops ~53/s to ~20/s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The background plugin ran a 100ms Timer that spawned a readlink
subprocess ~10x/sec for the life of the shell, purely to notice
wallpaper changes. Wallpaper updates already arrive over IPC
(omarchy-theme-bg-set calls `background set`, omarchy-theme-set calls
`background themeTransition`), and the initial wallpaper is read once
in Component.onCompleted, so the poll was redundant.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
omarchy-update-user-notify.path used PathExistsGlob= on the packaged
migrations directory. That directive is level-triggered: systemd re-checks
it every time the triggered unit deactivates and fires again while the glob
still matches. Since applied migrations stay on disk forever (state lives in
~/.local/state/omarchy/migrations), the glob always matches, so the oneshot
notifier re-triggered itself in a tight loop (~26-66 starts/sec) — burning
about a core and flooding the journal for the whole session.
The loop existed since the unit was introduced, but the default start-rate
limit killed it after 5 iterations, taking the .path unit down with
'unit-start-limit-hit'. That symptom was reported as #6174 and fixed
yesterday by setting StartLimitIntervalSec=0 — which removed the only brake
and turned the capped hiccup into an unbounded busy-loop.
Fix the actual cause instead:
* Drop PathExistsGlob= from the .path unit, keeping the edge-triggered
PathModified= watch for updates that land mid-session.
* Revert the StartLimitIntervalSec=0 override; with the level trigger gone
there is no self-re-fire to trip the limit, and the default limit is a
useful backstop again.
* Preserve the once-per-login pending check the glob used to provide by
giving the service its own WantedBy=graphical-session.target, enabled at
first-run alongside the other user units.
* Add a migration that daemon-reloads, revives a rate-limit-killed .path,
restarts the watcher, and enables the login-time notifier on existing
installs.
Verified with transient path/service units: the old config runs the service
200 times in 3 seconds; the new config runs it zero times while idle and
exactly once when a new migration file lands.
Thanks to @HANCORE-Linux for finding and diagnosing the problem.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tree is wholly package-owned, but files can land there unowned
(in-place extension work, script-written files), and pacman then aborts
the entire upgrade on the file conflict — as happened when copy-url's
service worker was renamed to background-2.js. The conflict check runs
before any hooks or scriptlets, so the package itself can't recover;
the update command has to allow the overwrite.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds shift:both_capslock alongside compose:caps so pressing both Shift
keys together toggles real Caps Lock. Caps Lock alone remains the
Compose key, and single-Shift behavior is unchanged. Provides an escape
hatch when Caps Lock gets stuck on (e.g. after an fcitx5 desync).
Closes#5981
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The lock service arms a 5s blank timer whenever the screen locks, and
input at the lock screen re-arms it. Closing the lid sprays pointer
noise over the lock surface, so the timer was routinely armed right
before suspend, froze mid-countdown, and fired moments after resume --
blanking the freshly woken unlock screen under the user.
Guard the timer with a wall-clock check: if far more time elapsed than
the interval, the countdown slept through a suspend, so take a fresh
run-up instead of blanking. This also blanks the lock screen 5s after
an untouched resume.
Two accomplices made the flash worse and hid the real bug:
- The clamshell watcher's 2s poll fired an unconditional global DPMS
enable whenever no external monitor was active, relighting any blank
within 2 seconds (lock-screen blanking never stuck on undocked
laptops) and racing the resume modeset. Recovery now only wakes
displays when it actually re-enables one.
- Every keystroke at the lock screen dispatched a redundant DPMS
enable via omarchy-system-wake, forcing extra modesets in the
fragile just-resumed DRM state. Brightness "on" now skips the
dispatch when every active display is already lit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The colorset is the Monokai Pro Ristretto palette, so the generated
themes keep all the accent colors. Brightened muted to Monokai Pro's
actual comment color, since comments sit on that key.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The stable Brave Origin's process is named plain "brave" (the wrapper
execs /opt/brave-origin-bin/brave), so pgrep -x brave-origin never
matches, and pgrep -f brave-origin also matches the installer's own
floating terminal - which made omarchy-install-browser launch a
headless browser and hang on first install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Brave now publishes brave-origin-bin on AUR, so use it instead of
brave-origin-beta-bin. The stable wrapper also parses flags files
correctly, so Brave Origin can use the normal chromium flags like
the other Chromium-based browsers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tuned muted, dark_foreground, and lighter_background to the tiers the
explicit themes used, which also fixes muted falling back to pure white
in the generated terminal configs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports #6092 from master: the video-app opacity opt-out only matched
Chrome/Chromium on the default profile, so Brave, Edge, and non-default
profiles kept the translucent default-opacity styling.
Co-Authored-By: Zeus-Deus <github.commits@widow.cc>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Chromium-based browsers auto-detect their os_crypt backend at launch. On
Hyprland the xdg-desktop-portal Secret backend has no provider, so the
autodetect can fall back to the 'basic' (v10) store, making previously
v11-encrypted cookies and saved passwords undecryptable — the user is
silently logged out of everything. Pin gnome-libsecret (the stock
omarchy keyring) so the backend is deterministic, matching what we
already do for VSCode.
Migration covers chromium, brave, chrome, and edge flags confs for
existing installs.
Co-authored-by: Niklas Tscheppe <ntscheppe@drgt.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* emoji panel: handle CTRL+BACKSPACE to clear filter
* shell: handle CTRL+BACKSPACE to clear filter in all search overlays
* Indentation fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* menu filters: align keyboard editing with Qt standard shortcuts
* menu filters: extract shared filter-editing helpers
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* potential fix for pull request findings
* Only treat filter edit keys as edits when they change the text
Backspace/Ctrl+U on an empty filter no longer calls setFilter(""),
which was resetting the list selection back to the top. This also lets
the menu's empty-filter Backspace fall through to goBack() with any
modifier held, as it did before the Util extraction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
With initial workspace tracking disabled, windows naturally open on the active workspace. Remove the explicit Hyprland workspace dispatch and let shell actions, shell restarts, and presentation terminals launch directly.
Set misc:initial_workspace_tracking = 0 so new windows appear on the currently
active workspace rather than the one their process was first launched on.
Without this, file-picker dialogs drawn by xdg-desktop-portal-gtk (browser
Open/Save, GTK app choosers, etc.) always open on the workspace where the portal
process started at login (typically workspace 1) instead of following the app
that requested them. So a Save dialog triggered from a browser on workspace 2
pops up on workspace 1, out of view.
Trade-off: an app launched on one workspace that switches away before its window
maps will now open on the new active workspace. In practice the dialog-follows-app
behavior is the more intuitive default.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Long passwords used to overflow the input field and clip with no
feedback that typing was still registering. Scale the dot size and
letter spacing down as the password grows, like macOS, so every
keystroke stays visible.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The skill still described pre-Quattro .conf files and workflows, sending
agents to inactive config files and stale commands. Now covers the Lua
config layout, o.bind/hl.unbind/hl.monitor forms, .d hook directories
with omarchy hook install, plugin cloning, and corrected command routes.
Distilled from #6310.
Co-Authored-By: ssupt <149246609+ssupt@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
omarchy-restart-shell now kills by config dir via quickshell kill and
relaunches with --no-duplicate, polling kill until every instance is
gone since kill returns without waiting and -n silently exits if one
remains. The locked-session refusal reads Hyprland monitor state alone,
which covers a hung shell holding the lock; the redundant shell IPC
probe, availability guards, and omarchy-shell path indirection are gone.
Shell IPC calls now time out (2s default, OMARCHY_SHELL_IPC_TIMEOUT to
override) so probing an unresponsive shell fails fast instead of
hanging the caller.
omarchy-hyprland-launch checks dispatch output for "ok" rather than the
exit code, which is 0 even on Lua errors, so a failed dispatch falls
through to the bash -lc fallback instead of silently launching nothing.
The Quattro upgrade cutover delegates to omarchy-restart-shell instead
of carrying its own copy of the lock check, pkill, and readiness loop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restore the key translation the awk parser used to do (C-M-S-Left →
CTRL + ALT + SHIFT + LEFT, PPage → PAGE UP, etc) as a display pass over
the tmux list-keys output, along with the PREFIX header line and the
COPY MODE table label.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Use tmux to list keybindings
* Annotate tmux keybindings
* List unannotated bindings too and let the isolated server exit on its own
list-keys -aN falls back to the raw command when a binding has no note,
so user-added bindings in an existing tmux.conf still show up. Dropping
exit-empty off means the throwaway server exits when the client
disconnects instead of relying solely on the trap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Explicit profile selections from the power panel or menu are now saved
under an ac/battery key in ~/.local/state/omarchy/powerprofiles and
reapplied on boot and on plug/unplug. Only successful, user-made
selections are persisted, so the performance/balanced defaults still
apply when nothing has been chosen.
All entry points key off the same power signal, UPower's OnBattery:
the shell reads it natively and omarchy-powerprofiles-set autodetect
queries it via busctl. This replaces the sysfs online checks, which
disagreed with the shell on desktops without power_supply entries and
lagged behind plug events on some USB-C laptops.
Plug/unplug switching moves from the udev rule into the shell's battery
service, which already receives UPower's debounced state changes, so
the udev rule and its settle-sleep workaround are gone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hyprland can report a scale set to 3 as something like 3.0000000000000004.
The previous directional comparison treated that as above the 3 preset, so
Super+Alt+Slash (scale down) would stay at 3 instead of reaching 2.
Snap the reported scale to the nearest preset first, then step up/down.
Add a regression test for the floating-point case.
Bundling vconsole.conf makes Plymouth apply the user's layout at the LUKS
prompt, but for layouts whose letter keys aren't Latin (Hebrew, Greek,
Cyrillic, Arabic, ...) that makes the necessarily-Latin passphrase
untypeable and locks the user out of their machine.
mkinitcpio sources its conf.d drop-ins as shell, so omarchy_hooks.conf now
checks XKBLAYOUT at every rebuild and only bundles the file for Latin
layouts; non-Latin layouts keep the US fallback the passphrase was set
under. A migration strips the unconditional line and rebuilds the UKI on
affected installs.
Closes#6229.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>