* Let users choose passwordless sudo duration
* Warn in the menu bar when passwordless sudo is active
* Fix sudo indicator hover behavior and repeated authentication
* Disable passwordless sudo from the bar without a terminal
* Shorten passwordless sudo indicator tooltip
* Recover interrupted passwordless sudo duration switches
* Allow sudo grant changes when listings require authentication
* Start passwordless sudo setup with the duration question
The manual still said the panel shows the percentage used and that an idle
account keeps its last known limits, and the README that sign-in trouble is
only text. Rows show the time to reset with the percentage on hover, and a
lapsed sign-in is a Sign-in required link that signs the account in again.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Slack is one of the most popular apps people install from the AUR. It now
ships from the Omarchy package repository as slack-desktop, with native
x86_64 and aarch64 builds. Install it the same way as Bitwarden: one menu
entry that installs the package and then launches Slack. The commercial
apps page of the manual gets a matching section.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each pending user's one-time migration requests a reboot even when hardware setup has already installed the aliases. Let migration completion handle repeat runs and remove the shared marker and its privileged write.
Co-Authored-By: Greptile <165735046+greptile-apps[bot]@users.noreply.github.com>
Keep hardware setup limited to installing the firmware package. The migration owns the pending-reboot marker and preserves prompts for every user migrating before reboot.
Install the separately owned firmware aliases through the package helper so stock firmware updates and stable refreshes retain them. Use a distinct migration to reach machines with the prior migration completed and preserve the reboot prompt until reboot. Restore the generic updater and leave display quirks to the kernel.
Co-Authored-By: Codex GPT-6.1-Sol XHigh <noreply@openai.com>
Honor package exclusions while returning failure when an installation leaves the required Cirrus firmware too old. Print-only and download-only operations remain successful without a repair or reboot request.
Co-Authored-By: GPT-6.1-Sol High <noreply@openai.com>
Co-Authored-By: Greptile <165735046+greptile-apps[bot]@users.noreply.github.com>
Constrain Cirrus firmware in the package transaction so a stable channel refresh cannot remove the required aliases. Recover old firmware independently of completed migrations, and request reboot only after verifying a successful repair. Preserve package exclusions through native pacman handling while keeping interactive conflict recovery answerable.
Co-Authored-By: GPT-6.1-Sol High <noreply@openai.com>
Co-Authored-By: GPT-6.1-Sol XHigh <noreply@openai.com>
* Bound clipboard capture reads
* Bound the watched clipboard read and drop copies cut off at the deadline
In watch mode capture.sh reads the copy from the owner's pipe on stdin, to EOF, with no bound. An owner that stalls without closing its end keeps the callback alive, and wl-paste --watch handles no further clipboard events until it exits. That matches #9443, whose stuck capture.sh had no wl-paste child: --list-types never reads from the owner.
Every read now goes through one bounded reader into a temporary file, and the copy is recorded only when the read finished, so a snapshot that stalls halfway no longer records its first half.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Omarchybot <317366263+omarchybot@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Add Install > Service > Microsoft with Outlook, Office, and Teams web apps
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Prefix the Microsoft web apps with the brand so they sit together in the launcher
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Open the Office web app at the sign-in page
office.com now forwards to the Microsoft 365 marketing site when signed out, while
Outlook and Teams open straight to sign-in. The login path does the same for Office.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Add Microsoft OneDrive to the Microsoft web apps
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Point the Teams web app at teams.cloud.microsoft
Microsoft moved Teams to its cloud.microsoft domain; teams.microsoft.com is the
legacy address now.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Document Microsoft web app customization
Explain individual app selection, organization-specific URLs, and a separate browser profile using existing launcher options. Keep the optional bundle separate from the manual's default apps.
Co-Authored-By: GPT-6 in Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 Medium <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Omarchy Bot <omarchybot@users.noreply.github.com>
Co-authored-by: GPT-6 in Codex <noreply@openai.com>
Recent runtime changes left the aggregate suite expecting a VS Code symlink and fixed extension version, pacman arguments without the end-of-options marker, a one-argument QML text signal, and background functions without preparation state. Align these fixtures with the current interfaces while retaining palette equality, package retry, keyboard action, and missing-background recovery assertions.
Co-authored-by: Omarchybot <317366263+omarchybot@users.noreply.github.com>
* Retry systemd reload after installing fingerprint recovery
A failed reload leaves the migration pending with the drop-in already installed. Reload existing configuration on retry so completion means systemd has applied the stop timeout, while preserving administrator changes.
Co-Authored-By: Codex Medium <317366263+omarchybot@users.noreply.github.com>
* Avoid sudo for an already-applied fingerprint repair
Migration completion belongs to each user, while the systemd repair is machine-wide. Query systemd reload state without elevation and reload only stale configuration, so interrupted repairs still retry and later users can complete without sudo. A failed state query remains a migration failure.
Co-Authored-By: Greptile <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: Omarchybot <317366263+omarchybot@users.noreply.github.com>
Co-authored-by: Greptile <165735046+greptile-apps[bot]@users.noreply.github.com>
* Restart fprintd after resume to clear a claim wedged by suspend
A fingerprint verify still open when the machine suspends leaves fprintd
unable to hand the reader back: the verify dies with "Cannot run while
suspended" and the follow-up ReleaseDevice fails on the still-busy device.
The wedged claim then rejects every lock-screen attempt after resume until
fprintd exits on its own 30-second idle timer -- and the retry loop keeps
it from ever reaching that timer, so the reader stays dead until the user
gives up and types a password.
Install a system-sleep hook that restarts fprintd on resume, dropping the
claim so the reader answers on the first touch. It is installed by
omarchy-setup-security-fingerprint and removed by its teardown, so it is
present exactly when a fingerprint reader is configured. try-restart is a
no-op when fprintd is not running, so a healthy resume pays nothing.
Approach suggested in #7229 and measured by @paracycle: 45 stray PAM
sessions after resume down to 2.
* Pace fingerprint retries and show when the reader is unavailable
The lock screen retried fingerprint auth on a flat 250ms timer with no
sign to the user, so a reader it could not reach -- a claim wedged across
suspend, one held by another client, or a sensor gone from the bus --
spun PAM sessions at four per second behind an icon still inviting
touches that could never unlock.
Pace and report on one signal: whether an attempt reached the reader at
all. pam_fprintd relays a finger prompt only once the claim lands, so an
attempt that ends without prompting never reached the device. Those
advance a streak that backs the retry off exponentially (to a ceiling
above fprintd's 30s idle exit) and, past a few in a row, crosses out the
icon and shows a "Fingerprint reader unavailable" notice. An attempt that
did prompt proves the reader works -- a finger that merely did not match
still reaches it -- so it clears the streak and the loop stays responsive.
User presence (a keypress or touch) collapses a backed-off wait to a
prompt retry, rate-limited so a moving cursor cannot respin the storm. An
attempt that never reaches the reader within a few seconds is aborted and
settled as unreached, so a claim orphaned by the resume restart surfaces
the notice and retries a fresh daemon rather than hanging silently.
The pacing, streak, nudge, and reach-timeout logic live in
FingerprintModel.js with Node coverage; the new Text elements declare
textFormat; lock status reports fingerprintUnavailable.
The attempt state machine tracks the open attempt with fingerprintAuthenticating alone; the first settle closes it, and one PAM attempt raising both onError and onCompleted still folds into the streak exactly once.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Install the fprintd resume hook root-owned and keep it with the PAM file
cp -p carried the checkout's owner and mode into
/usr/lib/systemd/system-sleep/, so under dev-link the root-executed hook
was user-owned, and a tree whose exec bit had been stripped installed a
hook that systemd-sleep silently never ran. Use install -Dm755 -o root
-g root, as the migration that installs the same file already does.
The hook also belongs exactly where the fingerprint PAM file does:
omarchy-apply-lock creates and removes /etc/pam.d/omarchy-lock-fingerprint
on its own, and any apply-lock run after enrollment left PAM without the
hook while its removal branch left a hook behind without PAM. Have
apply-lock install and remove the hook together with the PAM file, and
teach apply-lock-test.sh to redirect the hook into its scratch tree and
assert the hardened run lands it beside the PAM fixtures.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Only treat fingerprint as configured when a print is enrolled
The lock screen and omarchy-apply-lock decided fingerprint was set up with
fprintd-list | grep -qi finger, which also matches "has no fingers enrolled"
and "ListEnrolledFingers failed". A second account on a machine where one
user enrolled, or anyone who ran fprintd-delete, was therefore handed the
fingerprint loop: every attempt bailed before the claim, and with the new
pacing that showed up as a crossed icon and "Fingerprint reader unavailable"
for a reader the account simply has no print on. Match the per-print
" - #N:" lines instead.
apply-lock-test.sh follows: its fprintd-list stubs answer with a real enrolled-print row and its helper patcher matches the new probe line.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Exercise the migration's default hook source in its test
Every case overrode OMARCHY_FPRINTD_RESUME_SRC, so the path the migration
really reads from was never checked, while its -f guard turns a missing
source into a clean exit and a permanent per-user marker. Add a case that
runs against the shipped hook under the repo, and adopt set -euo pipefail
like the sibling tests.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Take the fprintd restart off the thaw and bound its stop timeout
The resume hook ran systemctl try-restart synchronously while user
sessions were still frozen, so its cost landed on the wake path: half a
second when fprintd answers SIGTERM, but a wedged fprintd on a stale
device handle (the reader re-enumerated across the sleep) does not, and
then the desktop stayed frozen for the whole stop timeout -- precisely in
the case the hook exists for.
Enqueue the restart with --no-block instead, as the unmount-fuse hook
already does for the same reason, and ship a drop-in capping fprintd's
TimeoutStopSec at 3s so the restart lands within seconds either way. The
drop-in is numbered 10-stop-timeout.conf, as the other Omarchy system
drop-ins are, so an administrator's override.conf sorts after it and
wins. It is installed and removed wherever the hook is (setup, teardown,
apply-lock, migration), and apply-lock-test.sh redirects it into its
scratch tree alongside the hook.
The hook's comments now say what actually happens on a locked resume --
Omarchy locks before every suspend and the lock screen opens a verify at
once, so the restart is real, not a no-op -- and name the upstream
defects this works around, fprintd#173 and fprintd#216, so the hook and
the drop-in can be retired when upstream fixes them.
Measured by MaxMad75 on an X390 Yoga (S3): 2 of 10 fprintd stops rode out
the timeout to SIGKILL; the 3s cap verified with systemctl show.
Co-authored-by: Omabot <omabot@omarchy.org>
Co-authored-by: MaxMad75 <44462964+MaxMad75@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Codex XHigh <noreply@openai.com>
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Close the status-check and start-failure exits through settle
Two paths left the fingerprint loop stuck or misreporting. A mid-lock status check that found fingerprint unconfigured aborted the PAM context directly; abort() delivers no signal, so fingerprintAuthenticating stayed true and every later attempt and nudge returned on it until the password unlock. And a fingerprintPam.start() that fails synchronously means the PAM file is gone -- a configuration problem, not a reader miss -- yet it fed the reader streak and reported "Fingerprint reader unavailable".
Route the abort through settleFingerprintAttempt like the reach timeout does, drop the pending retry with it, and on a start failure re-check the configuration so the icon disappears instead; a pending retry owns the next attempt when a status check comes back configured.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Pace fingerprint nudges by the pending tier and the cap's idle stretch
The nudge cooldown was a flat 2s, shorter than every backoff step, so a
user moving the mouse at a wedged reader collapsed each wait to 2s --
thirty claims a minute against the cap's 1.5 -- and each claim re-armed
fprintd's 30s idle timer, so the hook-less recovery the cap exists for
never happened while anyone was present.
Grow the cooldown with the pending wait, so presence collapses each
backed-off wait once and repeat nudges are paced by the tier. At the cap
the wait itself is the cure -- it is what lets fprintd idle out and drop
a wedged claim -- so there the idle stretch is measured from the last
settle, not the last nudge: a nudged attempt that hung until the reach
timeout would otherwise eat most of the window, and under continuous
input fprintd would never be left alone long enough to exit.
Wall-clock steps are handled in both directions: a clock stepped back
past the last nudge does not hold a fresh nudge back, and one stepped
back past the last settle counts as no idle time at the cap rather than
as enough. The retry test drives continuous input against attempts that
hang to the reach bound and checks the gap fprintd is left.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Give a slow fingerprint claim time to land before aborting it
The reach bound aborted any attempt that had not prompted within 5s by
SIGKILLing the PAM child mid-Claim. A reader whose device open takes
longer than that (out-of-tree drivers, and any reader right after the
resume hook forces a re-open) could then never prompt: each kill left
fprintd tearing the claim down until the open finished, the 1s retry hit
"already claimed", and three misses later the reader was reported
unavailable for good. Raise the bound to 20s, under GDBus's 25s Claim
timeout and pam_fprintd's 30s verify timeout (whose "Verification timed
out" is a non-error message that would read as reached), and name the
hazard the bound actually covers: a daemon restarted under the verify
fails the attempt promptly, a stuck device open does not.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Detect resume and hold the streak through the restart window
Monotonic timers pause across suspend, so a backed-off wait armed before
the sleep picked up mid-count afterwards: with the streak at the cap the
"Fingerprint reader unavailable" notice stayed up for the remaining wait
after the resume hook had already freed the reader, and misses collected
around the suspend edge carried across it, so a healthy reader could
cross the notice threshold in the first seconds after waking. With the
restart enqueued off the thaw, the loop's first attempts after a wake can
also land on the old daemon while it is being stopped -- up to ~3s when
it ignores SIGTERM -- and three of those would show the notice for a
reader that was merely being restarted underneath.
Notice a resume from any of three signals -- a sleep watch ticking the
wall clock for the whole lock, a retry that fired late, or an unreached
attempt whose settle finds the watch's last tick far in the past (so a
suspend shorter than the reach bound is caught before the tick itself
gets a chance to) -- and open a grace window: the stale streak is
dropped, a pending wait retries the fresh daemon at once, and misses
inside the window hold the streak at the first tier without ever counting
toward the notice. Detection is idempotent within the window, since more
than one timer can notice the same resume.
Pinned by MaxMad75's reading: the window is armed by the resume, not by
the first miss. Verified on his X390 (S3, frozen sessions): six lid-close
cycles, fingerprint-resume at +15ms, streak held, notice never fired.
Co-authored-by: MaxMad75 <44462964+MaxMad75@users.noreply.github.com>
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Only let a definitive probe change whether fingerprint is configured
The status probe collapsed every fprintd-list result into yes or no, so
an unreachable fprintd -- restarting under the resume hook, or failing a
D-Bus activation mid-resume -- read as "not configured": the icon
vanished, the retry loop and the sleep watch stopped, and nothing asked
again for the rest of the lock. One transient miss killed fingerprint
until the next lock, with the password as the only clue. MaxMad75 hit it
on hardware in run 6 of the X390 series; osborng filed the stock repro as
#9453 (mask fprintd, lock, unmask -- fingerprint never returns).
Classify the probe's output instead: an enrolled-print row is yes,
fprintd's explicit no-prints answer (or a missing PAM file or binary) is
no, and anything else is unknown -- the probe could not tell, so nothing
changes and it is retried on the attempt-retry pacing. The unavailable
notice, backoff, and resume detection all sit downstream of this flag;
now only an answer that actually means something can clear it.
Fixes#9453.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Log the fingerprint loop's misses, notice, and recovery as lock events
The reach timeout, an unreached settle, the streak crossing into the
notice, a resume restart, and the recovery all changed lock state without
touching logEvent, so a report of "Fingerprint reader unavailable" left
no omarchy lock line to line up with suspend and resume timestamps in
omarchy-debug-idle output. Log those transitions; reached attempts are
the steady state and stay quiet. A match that unlocks after a run of
misses is the recovery too -- the unlock resets the streak without
settling, so it logs fingerprint-recovered there as well.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Abort an attempt stranded in flight when a resume is detected
A verify that survived into the suspend still prompted comes back to a
daemon the resume hook has already replaced, and the loop's resume
handling deliberately left it alone: the reach timer stopped at the
prompt, so nothing bounded it but pam_fprintd's own ~25s timeout, and
until that ran out the icon invited touches that could not work. Most
visible where user sessions are not frozen across sleep and the lock
races the hook.
Abort the stranded session when the resume is detected and route it
through settle: it lands inside the grace window, so the kill never
counts toward the notice, and the settle arms the fast retry against the
fresh daemon itself.
Suggested by sliekens in review.
Claude-Session: https://claude.ai/code/session_0168egYTXrVBVg16ugszGzQt
* Simplify fingerprint recovery and consolidate enrollment checks
Use the enrolled-entry matcher from #9551 while retaining the lock's tri-state probe recovery and the privileged /usr/bin/fprintd-list call. Unknown enrollment probes must preserve existing PAM and resume recovery rather than deleting the machinery needed to recover. Preserve administrator-owned unnumbered timeout files during migration.
Remove presence-driven retry overrides and their cooldown, clock, and idle-window state: resume has its own fast recovery path, while other errors can follow the bounded automatic backoff. Let the existing sleep watcher detect resume instead of also tracking the age of each retry. Setup now uses apply-lock so PAM and recovery installation have one implementation. Keep the restart, stop bound, unreachable-attempt pacing, unavailable feedback, reach watchdog, and probe rechecks because each handles a distinct failure.
Co-Authored-By: Karl Ahlin <kalle.ahlin@gmail.com>
Co-Authored-By: Codex Medium <noreply@openai.com>
* Preserve failed-enrollment coverage in the setup fixture
The successful-enrollment fixture accepts PAM commands, so failure checks must explicitly reject those commands instead of relying on an unexpected-command error. Log both sed and tee and stub apply-lock for every case so premature authentication setup is detected without reaching live PAM files.
Co-Authored-By: Codex Medium <noreply@openai.com>
* Complete fingerprint recovery and setup reporting
Back off immediate device errors after the verification prompt as well as failed claims, while retaining fast retries for mismatches and normal scan timeouts. Measure from the prompt so a slow claim cannot hide a fast failure. Paced user activity retries preserve the daemon idle window required to clear a wedged claim. Initial probe outages remain visible without inventing enrollment, and setup cannot claim lock-screen success when the PAM configuration was not installed. Exercise the real QML service rather than a copy of its state machine.
Co-Authored-By: GPT-6 <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 Medium <noreply@anthropic.com>
* Avoid competing fingerprint probes and partial setup
Known enrollment is recovered by the PAM retry loop, so failed status probes must not raise a false unavailable notice or interrupt its daemon idle window. Initial unknown enrollment still gets paced probes. Install recovery files before enabling fingerprint PAM so a missing source cannot leave a new partial configuration.
Co-Authored-By: GPT-6 <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 Medium <noreply@anthropic.com>
* Keep the fingerprint error clock at the first prompt
pam_fprintd also sends Verification timed out as an informational message. Updating the prompt timestamp on that message made a normal full scan window look like an immediate device error and caused unnecessary backoff. Record the first prompt of each PAM attempt so later status messages cannot move the error window.
Co-Authored-By: GPT-6 <noreply@openai.com>
---------
Co-authored-by: Omabot <omabot@omarchy.org>
Co-authored-by: MaxMad75 <44462964+MaxMad75@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Codex XHigh <noreply@openai.com>
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Karl Ahlin <kalle.ahlin@gmail.com>
Co-authored-by: Omarchy Bot <omarchybot@users.noreply.github.com>
* Make application transparency opt-in
* Use the transparency helper for Kanagawa
* Address transparency review findings
* Toggle transparency for opaque windows too
The tuning host loaded module-rt with default arguments, so it asked for
realtime through xdg-desktop-portal. A portal started before rtkit was
installed reports a realtime budget of zero. module-rt applies that as a
hard RLIMIT_RTTIME of 0, RTKit still makes the data thread SCHED_RR, and
the kernel kills the host with SIGKILL as soon as audio plays; systemd
restarts it every two seconds and the speakers stay silent until the next
login.
Set rtportal.enabled = false, as the stock pipewire.conf does. The host is
a user service in the same session as the PipeWire daemon and has no
reason to go through the portal.
The host config is only copied by `omarchy-audio-tuning on`, so a
migration replaces an installed copy that is still Omarchy's own with the
old setting, and restarts the host if it runs. It does not call `on`,
which would overwrite a tuning graph another tool wrote under the same
name.
Co-authored-by: Michael de By <contact@michaeldeby.nl>
* Resolve effect_input/effect_output audio filter pairs
User filter chains can name their input and output nodes differently, so the existing input-name prefix check cannot find their downstream sink. Match effect_output.<suffix> exactly for effect_input.<suffix>, avoiding similarly named filters while retaining prefix-compatible custom tunings and the existing EasyEffects fallbacks.
Generated by GPT-6 in Codex. Reviewed by Claude Opus 5.5 Medium.
Co-Authored-By: Claude Opus 5.5 Medium <noreply@anthropic.com>
* Prefer an exact filter output over legacy prefix matches
An earlier stream for a similarly named filter can satisfy the legacy prefix check and select the wrong device before the exact paired output is read. Keep the first prefix candidate as a fallback for effect_input sinks and finish searching for the exact effect_output pair. Preserve first-match behavior for all other sink names.
Generated by GPT-6 in Codex. Reviewed by Claude Opus 5.5 Medium.
Co-Authored-By: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: Omarchy Bot <317366263+omarchybot@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 Medium <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Text labels centered their whole line box, which keeps room for
descenders, so the clock and workspace numbers sat a pixel above the
icons beside them. Center single-line labels on the figure height
instead. The menu logo comes from an icon font and opts out.
The Elsewhen globe also took the wide icon slot while its neighbors in
the center cluster use the status slot, so it drew extra space on both
sides. Give it the status slot like the weather widget.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Apply agent account selection to CLI subprocesses
* Fix account dispatch edge cases and remove redundant shell wrappers
* Use the current account when launching a new agent session
* Instantiate only the current orientation's indicator tree
Indicators.qml built both the horizontal Row and the vertical Column and
toggled them with `visible`, so every indicator existed twice per bar,
and so did every process an indicator spawns: Dictation.qml ran two
`voxtype status --follow` per monitor. On a six-monitor bar that is 72
indicator instances and twelve followers for six visible icons, and each
instance registers a click target and re-syncs the active-indicator model
as its state resolves at startup.
A Loader now instantiates the tree that matches `root.vertical`. Each tree
is wrapped in an Item that keeps the stock explicit implicit-size
expressions, so the root's size still follows the blocks synchronously; a
bare positioner only updates its implicit size on polish, which the
indicator contract test's center-hover check catches.
Measured on a six-monitor, 23-widget bar (three runs each, `omarchy
restart shell`): time from "Configuration Loaded" to "polkit agent
registered" 19.8-20.3s -> 15.5-15.7s, quickshell CPU 29-30s -> 24-25s,
voxtype followers 12 -> 6.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Coalesce plugin API resyncs and key bar object ownership by target
Every WidgetButton registers itself as a bar click target when it is
created. registerClickTarget replaced the clickTargets array, the change
handler ran syncAllPluginBarApiObjects() inline, and that walked every
plugin API times every click target times a linear scan of
pluginObjectOwners in pluginObjectRecord. Startup is a few hundred
registrations, so the cost is quadratic in bar size and multiplied by the
number of monitors: a six-monitor, 23-widget bar spent 16-20 seconds of
pegged QML thread before it was populated, and an 8-second qmlprofiler
capture showed 1.36 million pluginOwnsBarObject calls and 46-71ms per
registration.
- pluginObjectOwners is a Map keyed by target, so pluginObjectRecord,
markPluginObject, unmarkPluginObject and releasePluginObjects are O(1)
per object. Nothing outside Bar.qml read the array.
- The activePopout, clickTargets and layoutConfig change handlers schedule
one resync per event-loop turn through Qt.callLater, the way
onModuleSlotsChanged already defers prunePluginBarApis. bindPluginBarApi
still syncs a brand-new API inline, and requestPluginPopout and
releasePluginPopout sync the owning API inline, so a plugin never reads
a stale API on its own actions.
- A flush serialises the layout once and hands each API its own parsed
copy instead of deep-copying it once per API per sync.
- ModuleSlot's cursorShape read clickTargets for every slot on every
monitor (26,700 evaluations per start). It is now gated on the slot's
HoverHandler, which is the only time the cursor is over it.
Measured on the same bar, launching the shell from a checkout with this
and the indicators change (two runs): "Configuration Loaded" to "polkit
agent registered" 19.8-20.3s -> 0.70s, bar populated 1.7s after launch
(from ~30s), quickshell CPU 29-30s -> 2.0s.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Wait for the old shell to exit before restarting it
omarchy-restart-shell stopped the running shell with `quickshell kill`
under a five-second timeout and launched the replacement as soon as the
loop ended. A six-monitor bar takes 5.4-6.0 seconds to tear down (every
widget button unregisters its click target on destruction, and each
unregistration re-synced every plugin API), so the client timed out while
the shell was still exiting, the fresh instance's no-duplicate check saw
the dying one and quit, and the user was left with no bar and "Omarchy
shell did not become ready after restart".
Give the kill client thirty seconds, then wait, bounded, until
`quickshell list` shows no instance of the session config before
launching. The readiness check also waits on a sixty-second deadline
instead of twenty attempts: a large bar answers ping only after its
plugins have loaded, which on the stock bar was well past the old
twelve-second window.
Verified three consecutive restarts against the stock shell on the
six-monitor machine: each returned 0 in about six seconds with exactly
one instance and no "already running" in the journal.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Say what the bar's ownership Map actually saves
Qt's V4 Map (ESTable::get) finds a key by scanning its keys, so ownership lookups are not O(1). The win is that a registration no longer copies the owner array and rescans it in QML.
Co-Authored-By: Codex Medium <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Omarchybot <317366263+omarchybot@users.noreply.github.com>
Co-authored-by: Codex Medium <noreply@openai.com>
User setup recreates installer-seeded files, so presenting a full rerun as a refresh or an after-login recovery step implies that it safely preserves an existing account. Remove that advice from first-boot provisioning and both user-setup entry points. Setup failures still report the log path, and provisioning behaviour is unchanged.
Co-authored-by: Omarchybot <317366263+omarchybot@users.noreply.github.com>
Co-authored-by: GPT-6 <noreply@openai.com>
* Apply the system keyboard layout to the SDDM greeter
* Extract shared assert_resolved_input helper
* Make the missing vconsole.conf tests actually open nothing
The stub only returned nil for an unset OMARCHY_VCONSOLE, but bash passes it
through as "", so both fallback cases were reading an empty temp file instead
of exercising the failed open.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Keep the Wi-Fi icon steady on OWE transition-mode networks
An OWE transition-mode network pairs an open SSID with a hidden "_owetm_"
twin on the same BSSID. Between scans NetworkManager reports the in-use
access point under the hidden SSID and drops the active profile from the
device's AvailableConnections, which is the only place Quickshell builds
known networks from. No listed network is then connected, so the bar fell
back to the disconnected icon until the next scan brought the open SSID
back, flickering on and off while the link stayed up.
Fall back to the Wi-Fi device's own connected state when no listed network
is connected, and read the in-use access point's strength from nmcli
(without a rescan) while the connected network has none of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Discard stale in-use AP reads and key Wi-Fi checks on the device
Bump a generation whenever the cached in-use access point strength stops
describing the link (leaving Wi-Fi or a device change), and drop any nmcli
read started before it, re-reading immediately instead of a full interval
later.
Key Wi-Fi connectivity checks on the device rather than the SSID, so the
listed network coming and going with each scan on an OWE transition-mode
network no longer schedules a check. A real network switch still passes
through "disconnected".
Add a QML fixture that runs the panel against a mocked device through the
scan churn, a mid-read disconnect, and a final disconnect.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>