The weather widgets asked wttr.in to geolocate by IP, which lands on
whatever city the ISP registers its address block under -- Downey when
you're in Malibu. Now the location can be set explicitly:
* Click the location in the weather panel to search via open-meteo
geocoding, pick a suggestion (disambiguated by region), and it sticks.
The X next to the field returns to IP auto-detect, as does committing
an empty search.
* State lives in ~/.local/state/omarchy/settings/weather.json as
{name, latitude, longitude}, owned by the new omarchy-weather-location
command (bare call prints the current location, --set/--clear write).
The panel watches the file, so hand edits apply live.
* Stored coordinates make wttr.in and open-meteo exact. Open-meteo also
now supplies current conditions alongside the daily forecast it
already served, which fills the panel within a second of a location
change instead of waiting out wttr.in's multi-second responses; wttr
results still win once they land. Failed wttr fetches retry instead
of sitting stale until the next refresh cycle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9914513a unified internal monitor detection but left the test grepping
for the old jq expression. Since the suite runner stops on first
failure, every test file after monitor-recovery went silently unrun --
281 assertions were being skipped while the suite looked green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
canonical_widget_id was a pure pass-through, making its reserved-id
check a tautology; require_omarchy_path and choose_value were defined
but never called.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apps like nvim clear the per-pane cursor colour with OSC 112, which made
tmux reset the outer terminal's cursor. Terminals that can't reload their
config (foot) then restore the cursor colour from whatever theme was
active at launch, leaving a mix of old and new cursor colours across
panes. Set the global cursor-colour option as a fallback so cleared panes
land on the current theme instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The trailing slash from the themes/*/ glob put a double slash in the
path comparison, so stock backgrounds never matched git ls-files output
and were all moved into ~/.config/omarchy/backgrounds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With dotglob set, * already matches dotfiles, so the extra dot patterns
made the second ln -s fail and silently skip the legacy hypr shim.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Legacy repos cached omarchy packages under the same name and version as
quattro rebuilds with different bytes, so pacman aborted --noconfirm
transactions with checksum errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Weather wraps its panel in a BarWidget that only exposed togglePanel(),
so it couldn't ride the new shell-routed hotkey path and was left on the
stale-prone per-plugin target. Expose open/close/opened on the wrapper
(open maps to the panel's hotkey path so the center hover reveal stays
suppressed) and switch omarchy-notification-weather to the new syntax.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bar-widget panels (audio, bluetooth, network, power, monitor) used to be
toggled via their own per-plugin IpcHandler targets. Quickshell resolves
duplicate targets first-handler-wins, so after a plugin or bar reload the
stale handler of the destroyed widget instance kept claiming the target
and the hotkeys went dead.
The shell root's IpcHandler lives outside the reload cycle, so summon,
hide, and toggle now go through `omarchy-shell shell toggle <plugin>`
and the shell routes to the live widget instance via the bar's slot
registry. Panel plugins that are also panel/overlay/menu kinds keep
using the panel loader path. Failures to find a live widget are logged
so a widget missing from the bar layout stays diagnosable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The slot registry is about to become the routing table for panel
hotkeys, so it has outgrown its debug-only name.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A previous submap attempt was brittle because the script owned the
keyboard state: if it died between enter and exit, the submap stuck.
This version lets the compositor own both edges. The Lua config binds
plain Return when a layer surface with slurp's "selection" namespace
opens and unbinds it when the last one closes, so the bind exists
exactly while a pick is on screen. slurp dying for any reason (cancel,
crash, pkill, SIGKILL of the picker) fires layer.closed and the bind
evaporates — there is no code path where it leaks.
Return invokes omarchy-capture-region --take-fullscreen, which flags a
marker in XDG_RUNTIME_DIR and dismisses slurp; the picker treats an
empty slurp result with a fresh marker as the focused monitor, reusing
the existing fullscreen path. Since monitor focus follows the cursor,
that is the monitor under the slurp crosshair. The recording flow gets
monitor:NAME from the existing match, so Return during a recording
pick starts a native full-monitor capture.
The signal mode no-ops unless slurp is actually running, and both the
signal protocol and the layer-scoped bind lifecycle are verified live:
bind present only while slurp is open, real picks resolved by the
signal on both the screenshot and recording paths, no leftover binds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both capture scripts carried the same pipeline: workspace rectangles
from hyprctl, a hyprpicker freeze, slurp, and the bare-click snap loop.
The copies had already diverged once (rotated-display handling), so the
pipeline now lives in one hidden helper, omarchy-capture-region-pick,
which prints the picked geometry in slurp format.
The two callers differ only in what they layer on top. The screenshot
needs the freeze to outlive the pick so grim captures frozen content:
--keep-freeze leaves hyprpicker running and prints its PID first, with
the caller owning the kill. The recording prefers native full-monitor
capture: --match-monitor prints monitor:NAME when the geometry exactly
matches a display.
One deliberate fix along the way: the screenshot snap regex rejected
negative coordinates, so bare clicks on monitors positioned left of or
above the origin never snapped. The helper uses the recording variant,
which handles them.
Verified with an 11-case shim harness (snap precedence, rotated-monitor
match, freeze ownership, cancellation, negative coordinates) and the
live end-to-end screenshot sanity test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five scripts each hand-rolled colors.toml parsing plus their own
alias/fallback cascade, and the tables had drifted: theme-osc grew a
divergent color8/color9 chain, tmux and gnome carried private mode
detection, and the preview tool duplicated the lot. The differences
never mattered for shipped themes but were a standing bug factory for
sparse user themes.
omarchy-theme-color is now the single resolver. Its cascade is a
verbatim port of the canonical theme-set-templates logic (semantic
keys, legacy colorN aliases, derived shades, selection/cursor
derivation, mode precedence), exposed as --all, --raw, or single-key
lookup with fallback.
Generated output is byte-identical across all 21 shipped themes on
every surface: 17 template files, OSC byte stream, tmux and gsettings
command logs, and previews. Sparse legacy themes now resolve the same
palette everywhere instead of five slightly different ones.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cmd_replace fed $PLACEMENT_FROM_SECTION/$PLACEMENT_FROM_INDEX to jq
without ever calling parse_placement, so they were always empty
strings that implied flag support which does not exist. Pass literal
empties with a comment stating replace resolves by widget id only.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The screenshot picker swaps monitor dimensions for transform 1/3
displays; the recording copy had drifted and did not, so portrait
monitors got landscape rectangles in the picker. The full-monitor
match had the same blind spot, silently downgrading rotated-monitor
selections to region capture instead of native -w capture.
Both now share the screenshot script's format_geo jq helper, which
also lets the monitor match compare one geometry string instead of
four fields.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four scripts matched internal panels with contains("eDP") while
omarchy-monitor-state used test("^(eDP|LVDS|DSI)-"), so laptops with
LVDS or DSI panels were internal to monitor-state but invisible to the
clamshell, mirror, and external-active logic. All five sites now share
the inclusive anchored pattern.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FILES="$*" space-joined multiple CLI path arguments into one bogus
argument before handing them to LocalSend. Build the file list as an
array from the start, which also collapses the single/multi send
branch into one path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The keymap cache parsed xkbcli compile-keymap into a bash associative
array, serialized that array back into a string, and re-parsed it
inside a second awk's BEGIN block. One awk now reads the compiled
keymap directly, seeded with the fallback table so xkbcli failure
still degrades gracefully. (The xkbcli child must read /dev/null or
it consumes the binding records arriving on stdin.)
Also gone: the never-called lookup_keycode_cached, the DEBUG timing
scaffold, the redundant explicit noops in the Lua mock that the
metatable already provides, the walker-era XML entity escaping that
nothing renders as markup anymore, and the monitor-height computation
in favor of a fixed height like every other menu.
The cache key drops the double sha256 and now includes the active
keymap, so switching XKB layout invalidates cached symbol names
instead of serving stale ones.
Binding records are byte-identical before and after: 215 of 215.
686 lines to 573.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each color lookup spawned a 60-line awk program with a character-by-
character quote-state machine for comment stripping, about 22 spawns
per run counting the dotted-key retries. One pass now tracks the
current [section], normalizes dotted keys to the same path, and emits
key/hex pairs into a single associative array the shell reads once.
The strict value pattern (a lone, optionally quoted and 0x/#-prefixed
six-digit hex, followed by nothing or a comment) makes quote-aware
comment stripping unnecessary. Section form still wins over dotted
form and first valid occurrence still wins, matching the original
search order. Output is byte-identical across fixtures covering both
key forms, comments, prefixes, and fallback paths, plus real themes.
197 lines to 151.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The braille and block renderers were two near-identical awk programs
sharing a verbatim PBM tokenizer, header check, and blank-line trim,
differing only in the per-cell rendering. One awk with a mode variable
now branches where they actually differ.
The option surface served two in-repo callers, both passing constants:
the duplicate positional loop behind --, the undocumented --block and
--braille aliases, and the --flag=value forms all go. The two magick
probes for the alpha range collapse into a single invocation.
Output is byte-identical across images and flag sets for everything
that remains, including both real callers' exact invocations.
353 lines to 243.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The /proc/*/environ scraping, wayland/X11 socket probing, and LANG/DBUS
guessing arrived with the Omarchy 4 live upgrade cutover, whose upgrade
script no longer exists. Every remaining caller runs inside the
graphical session with full env. Keep a five-line guard deriving
HYPRLAND_INSTANCE_SIGNATURE from the newest instance runtime dir so
restarting over ssh still works.
Launching now goes straight to the Lua dispatcher: on quattro, classic
hyprctl dispatch arguments are evaluated as Lua and fail with exit 0,
so the old exec fallback silently did nothing. Check for "ok" output
instead of trusting the exit code, with setsid as the real fallback.
180 lines to 94.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The embedded Perl program parsed instance.lock QString framing and spoke
the Quickshell IPC socket protocol by hand. Quickshell ships this
natively: qs -p <config> ipc call does instance discovery, the call, and
response printing, byte-identical for every in-repo call pattern.
The bash wrapper keeps what qs doesn't do: the -q best-effort mode, the
{} default argument for summon/toggle, and the familiar error wordings.
Two qs quirks are handled: function names like "show" shadow qs's own
CLI subcommands, so arguments are always passed after --, and IPC-level
failures (unknown target/function, bad arguments) are reported on stdout
with exit 0, so those are matched and mapped to stderr with exit 1.
165 lines to 62.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A plugin is now just a git repo cloned into ~/.config/omarchy/plugins/<id>/.
That one idea replaces the entire homegrown package-manager half of the
plugin suite: trusted-source registry, clone cache, catalog scanning,
semver comparison, staging dirs, and timestamped backups — 1,025 lines
across five binaries whose jobs git already does.
Gone:
- omarchy-plugin-source: the trusted-repo registry (sources.json) and its
clone cache under ~/.cache/omarchy/plugin-sources/. The trust decision
now happens once, at add time, with the same unsandboxed-code warning.
- omarchy-plugin-scan + omarchy-plugin-available: the catalog machinery
over cached clones. Discovery belongs on a web page, not in the CLI.
- omarchy-plugin-add: copying folders out of cached source clones with
hand-rolled staging and .bak backups. Replaced by a git clone.
- omarchy-plugin-update: manifest version comparison via sort -V and
re-installs. Replaced by fetch + diff + fast-forward; git is the version
and git is the backup.
- omarchy-plugin-remove and omarchy-plugin-edit as separate binaries:
folded into omarchy-plugin, much slimmer.
The consolidated omarchy-plugin now handles the full lifecycle:
- add <git-url>: warn, clone into a dot-prefixed staging dir (invisible
to the plugin scanner), validate, then move into place named by the
manifest id. Plugins land disabled — enabling is the single consent
moment, replacing the old review-before-copy flow.
- update [id | --all]: fetch origin HEAD, show the diff (delta when
available), confirm, fast-forward. Updates are code the shell will run,
so the result is re-validated and rolled back to ORIG_HEAD if upstream
turned invalid (e.g. smuggled a symlink).
- remove [id]: git checkouts are deleted outright since upstream keeps
the history; hand-made plugin folders still get a backup, and dev
symlinks are just unlinked.
- edit [id]: opens the user plugin directory in a shell.
All commands keep the interactive/unattended split: gum prompts in a
terminal, hard refusal without --yes otherwise, so scripts and agents
never hang on a hidden prompt.
Kept as siblings: omarchy-plugin-catalog (omarchy-bar reads it),
omarchy-plugin-validate (the security boundary, now pruning .git from its
symlink scan since installs are git checkouts), and omarchy-plugin-clone
(local development of built-in widgets, a separate concern).
Trade-offs accepted: one repo = one plugin (no more multi-plugin source
repos), and ref pinning or branch switching is no longer a CLI feature —
an installed plugin is a plain checkout, so that is ordinary git in the
plugin directory.
None of the removed machinery ever shipped: it existed only on this
branch, so there is no migration. The net effect is 11 scripts / 2,040
lines down to 4 scripts / 1,080 lines, and one less concept for users to
learn — everyone already knows what a git repo is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the three-layer process management (trap cleanup, PPID-polling
watchdog subshell, /proc-scanning reaper) with kernel-level lifetime
binding: each wl-paste watcher is spawned under setpriv --pdeathsig TERM,
so it dies with the shell no matter how the shell exits.
- Delete watch.sh: the shell now spawns both watchers directly as
declarative Process commands instead of a wrapper script with traps
and a 1s polling loop
- Delete init.sh: reaping stale watchers from a crashed pre-pdeathsig
shell is a single pkill at startup
- Collapse capture.sh's dual stream/snapshot modes: wl-paste forwards
arguments to its watch command, so the mime rides along as $1 instead
of an env var, letting both modes share one emit_image function
- Turn the six-branch image mime chain into a loop
Net: two fewer files, three fewer runtime processes, and the watch-mode
capture tests now exercise the same argument protocol the shell uses.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
omarchy-plugin mixed plugin lifecycle, clone/edit, and bar-layout mutation.
The bar layout was mutated by two separate engines (an embedded Python
mutate_bar here and jq in omarchy-config-shell-bar), each with its own
manifest walker.
- omarchy-plugin is now a ~220-line router; clone and edit move to
omarchy-plugin-clone and omarchy-plugin-edit, matching the existing
plugin-{source,add,update,remove,available,validate,scan} sibling pattern
- omarchy-config-shell-bar is replaced by omarchy-bar, the single bar engine
(show/layout/list/options/use/reset/add/move/remove/set/replace/position/
transparent/settings) with one jq pipeline; the Python mutate_bar and its
python3 dependency are gone
- omarchy-plugin-catalog is the shared manifest scanner used by omarchy-bar
(widget/option enumeration, add validation) and omarchy-plugin-clone
(source enumeration), replacing three find|jq re-implementations
- service install/remove and refresh-shell call omarchy-bar; docs and tests
updated to the new command surface