* Pick window transparency from Appearance > Window Transparency
Transparent, the default, keeps today's windows. Opaque turns the windows
Omarchy makes transparent solid. Glassy is the frosted glass Starship
(#14726): its blur behind every see-through window and shell surface, and a
see-through, blurred background in the theme's terminal files, since foot
turns compositor blur down and a Hyprland opacity rule fades text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Leave window transparency to Appearance when animations are off
Turning animations off no longer makes windows opaque. VM installs start
with opaque windows instead, and a migration keeps them opaque for anyone
who already has animations off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add a Glassy bar background, under Menu Bar > Bar Background
Menu Bar > Bar Background picks Opaque Bar, Glassy Bar or Clear Bar, and
Indicators becomes Bar Indicators to match. Glassy keeps the theme
background faintly and Hyprland blurs what is behind it, with the same blur
as Glassy windows.
omarchy bar background opaque|glassy|none|cycle sets it and prints it when
given nothing. omarchy bar transparent true|false|toggle stays for
compatibility, as none and opaque. Double-clicking the bar cycles through
the three backgrounds the same way, from clear to glassy to opaque.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Stop when a saved corner choice can't be removed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Make btop follow glassy windows
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Let the workspace layout test's Lua assertions fail the suite
Lua 5.5's standalone interpreter exits 0 when the script that raised the error came in on stdin, and exits 1 for the same error in a script file. The block here was piped in through a heredoc, so every assertion in it reported as passing however wrong the value was: changing the expected layout to one the toggle never writes still left the file green.
* Add a floating mode to the workspace layout cycle
Super + L cycled a workspace between dwindle and scrolling. It now carries on into floating, where every window on the workspace floats.
Floating cannot be a third `layout` on the workspace rule. Hyprland has no layout by that name and accepts one anyway: `hl.workspace_rule({ layout = "floating" })` returns ok and silently leaves the workspace on dwindle, exactly as a misspelt name does. So the mode floats the windows themselves, through the same `float` dispatcher SUPER + T uses.
Ending the mode has to tile the windows the mode floated and only those, so each one is tagged with `omarchy-mode-floated`. A window already floating when the mode reaches it -- an app with a float rule of its own, a dialog, one popped out with SUPER + T -- is left unclaimed and is still floating when the mode ends. The tag lives on the window rather than in a table here: it dies with the window, follows it between workspaces, and survives the config reload that plugging in a monitor performs, none of which a table on this side would do. A pinned window is left alone entirely, because tiling one unpins it as well.
Windows arrive on a workspace three ways and a window rule only covers one of them, so the mode watches events: window.open for what opens there, window.move_to_workspace for what is carried in, and the same event for what is carried back out, which gives up the floating the mode gave it rather than staying floating on a workspace that tiles. Every tiled window is claimed before any of them is floated, because floating one member of a group floats the rest, and claiming as it went would find those already floating and leave them stranded.
The mode is read back from the saved file rather than from the compositor. Hyprland reports a workspace's tiled layout and floating is not one, so a floating workspace still answers dwindle or scrolling; trusting that answer would skip a step of the cycle. A file saved before floating existed names a layout rather than a mode and falls through to what Hyprland reports, so the cycle keeps working and the next press rewrites it.
Two faults in the old script had to go first, because saving the mode makes both persistent. Named workspaces report a negative id, which passed the `^-?[0-9]+$` guard, and Hyprland reads a leading "-" as a selector relative to the current workspace: pressing Super + L on a named workspace reconfigured workspace 1 instead, and would now save a file that did it again at every login. And the saved state was written under $HOME/.local/state while the Hyprland side reads XDG_STATE_HOME, which also had to reach package.path: the loader lists that directory but loads the files by module name, so a state home anywhere else listed the saved layouts and then loaded same-named ones from the home directory.
Saving is checked too. The mode reaching the compositor and the mode reaching disk are separate things that can fail separately, and a save that failed silently would leave the next press reading a file that never got written, cycling between two modes and never reaching the third.
The guard against named workspaces stops new files being written, but an earlier version already wrote them, and they go on being loaded at every login. A migration removes the saved layouts named for a negative id.
The keyword fallback is gone. `hyprctl keyword` refuses a Lua configuration outright -- "keyword can't work with non-legacy parsers. Use eval." -- and exits 0 while doing so, so as a fallback it neither worked nor reported that it had not. A failed apply now says so and saves nothing, rather than announcing a layout the screen does not show and restoring it at the next login.
Co-Authored-By: Codex XHigh <noreply@openai.com>
* Add titlebars, edge snapping, and a window Shelf to floating workspaces
Floating workspaces become a traditional desktop that works with every
theme. Windows get themed titlebars from the omarchy-hyprland-titlebars
plugin, which is loaded only while something floats. Dragging a titlebar
to the left or right edge snaps the window to that half, and dragging it
to the top or double-clicking maximizes it. The window floating mode
places is sized, centered, and cascaded, and floating workspaces focus
on click.
Super + M, or the minus button, sets a window aside on the Shelf, which
works from any workspace and restores the window onto the current one
in that workspace's layout. Float All Workspaces makes floating the
default while Super + L still sets a single workspace's layout.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Bring resize on border to tiling too
* Fix floating mode cleanup, fullscreen windows, and failed Shelf notes
Turning Float All Workspaces off with no workspace layouts left behind
started nothing to give the floated windows back. A window fullscreen
when its workspace began floating now floats once it leaves fullscreen.
A window whose Shelf note cannot be saved stays where it is, and the
titlebars read the theme from where omarchy-theme-set writes it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Place the Shelf on the bar once, instead of on every minimize
Putting it back on each minimize kept a Shelf the user moved where they
put it, but re-added one they had removed. A migration places it once for
existing bars; Super + Alt + M opens the Shelf either way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Raise titlebars off the window background
A titlebar in the theme's background color ran into the window under it.
It takes the theme's lighter background, the raised surface every theme
defines, and the selection color on themes that make the two the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drop minimizing and edge snapping from floating workspaces
A floating workspace is now titlebars with maximize and close, and nothing
else. The Shelf (its bar widget, panel, commands, notes, Super + M and
Super + Alt + M) goes with the minimize button, and the titlebar plugin's
edge snapping is left off, along with the shell's snap preview.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Stop describing the titlebar package as snapping
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Name web app windows in their launchers
Chromium names a web app window after its URL, and titles it with the page
title or just the domain. StartupWMClass ties the window to its launcher,
so the titlebar plugin can show the launcher's Name instead: WhatsApp
rather than web.whatsapp.com. New web apps, the shipped ones, and existing
launchers (through a migration) all carry it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drop the titlebar package comment from the base list
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Show the workspace layout in the bar and cycle it on click
The focused workspace's mark is a square for dwindle, a triangle for
scrolling, and a circle for floating, and clicking it cycles the layout
as Super + L does. Both layout toggles ask the bar to read the new mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Show scrolling as a circle and floating as a triangle
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move the look of the desktop to Trigger > Appearance
Menu Bar, Battery Percentage, Workspace Layout, Window Gaps, Animations,
and 1-Window Ratio change how the desktop looks rather than switching a
behavior, so they leave Trigger > Toggle for their own menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Choose a default workspace layout instead of floating everything
Trigger > Appearance > Workspace Layout sets the layout of every workspace
without one of its own: Tiling, Scrolling, or Floating. A tiled default
is Hyprland's own layout, and a floating one is the floating mode. It
replaces Float All Workspaces, which also overrode and set aside the
layouts workspaces had been given with Super + L.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Title the workspace layout menu Default Workspace Layout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Use the animation icon for Appearance > Animations
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Open clipboard history from Trigger
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Leave edge resizing to looknfeel on floating workspaces
looknfeel now resizes from borders everywhere, so floating workspaces
only need to switch pointer focus to click.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Convert saved workspace layout rules to modes
Released versions saved a workspace's layout as a plain workspace rule,
which names no mode, so a floating default would override a workspace
set to scrolling, and the bar and Super + L would misread it. A migration
rewrites those files as modes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cycle workspace layouts backwards with Super + Alt + L
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test the Trigger > Appearance split and Clipboard History placement
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Decode escaped web app URLs before naming their windows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Describe the workspace widget's layout mark and click
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Let a workspace cycled onto the default follow it again
Once Super + L gave a workspace a layout of its own, it kept it for good,
so a later change of the default never reached it. Cycling onto the
default layout now forgets the workspace's own instead of saving it.
Without a saved default, the default is Hyprland's own layout, which
the user's looknfeel may have set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Codex XHigh <noreply@openai.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Providers never returned JSON rows: they are shell-defined row sources
emitting tab-delimited lines, and extensions cannot declare new names.
bar.shellQuote moved to Util.qml, and the UpperCamelCase widget id
migration no longer exists.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The network and bluetooth panels stopped launching them; the module
catalogue still said otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add agent usage collectors that write display-ready data files
One omarchy-agent-usage-scan-<agent> collector per AI coding agent prints a
complete display-ready usage record — identity, tier, status, rate limits,
and today/week/all-time stats. omarchy-agent-usage-update runs every
collector it finds and writes the records atomically to
~/.local/state/omarchy/agents/usage/, so anything that displays usage only
ever reads JSON from there.
The Claude collector absorbs what the shell previously did in-process:
transcript scanning, the stats-cache/history fallback, credentials parsing,
and the OAuth limits probe, now with a probe throttle and last-good limits
kept across network failures. The Codex collector is the existing scanner
reshaped to the shared record contract.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Redo the model-usage plugin as omarchy.agents watching usage data files
The panel is now strictly a display. It discovers the JSON records that
omarchy-agent-usage-update maintains under
~/.local/state/omarchy/agents/usage/, watches them for changes, and draws
whatever appears — so adding an agent means shipping a collector, never
touching the panel. Marks resolve by convention (assets/<id>.svg with an
optional -light twin), the limits meters read a generic limits array, and
the per-provider QML adapters and in-plugin scanner scripts are gone.
Cross-device sync aggregation stays in the shell and keeps the snapshot
field names older versions wrote, so mixed-version fleets still merge in
both directions.
With the provider fan-out gone, the widget takes its real name: the plugin
id becomes omarchy.agents. A migration renames it wherever a user's config
mentions it — layout entries keep their settings and position, a disabled
widget stays disabled — then primes the data files once and drops the old
scanner cache. The migration test also drops a stale assertion that expected
migrations to restart the shell themselves, which c992cdff moved to
omarchy update.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Address Codex review: synced-only tabs, limits retry, history fallback
Three data-availability gaps from review. An agent whose records only exist
in synced snapshots — a collector installed on just one machine — now gets
its tab by unioning the synced aggregate into the provider list, with rate
limits blank since those never travel. A Claude limits probe that reaches no
server at all writes retryAdvised into its record, and the shell honors it
with one 30-second retry instead of waiting out the full refresh interval,
restoring the old boot-before-DHCP behavior. And a machine with only
history.jsonl — no transcripts, no stats-cache — still reports today's
prompt and session counts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Address second Codex pass: history-only visibility, targeted retries
Today's prompt and session counts now count toward an agent's presence in
the bar, so a machine whose only Claude source is history.jsonl shows up
without waiting for limits. And the 30-second limits retry passes the
advising agent ids to the updater, so an outage at one provider no longer
puts every other collector on a retry treadmill.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Drop omarchy-cmd-present jq guards from the agents migrations
jq ships in the default package set, which makes it a runtime invariant per
AGENTS.md — call it directly. The migration tests lose their now-unused
omarchy-cmd-present stubs with it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Drop the scan infix from the collector command names
Collectors are omarchy-agent-usage-<agent>; the updater skips its own name
when globbing them, and the update test proves it with a decoy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Keep the credential store out of the printed usage record
The Claude collector now reads .credentials.json once into three scalars —
the access token, its expiry, and the plan label — instead of passing the
parsed store around. The token reaches nothing but the Authorization header
of the limits probe, and only the plan label may travel into the record,
which is what CodeQL's clear-text-logging alert on the record print was
unable to see when the whole dict flowed through.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Clicking the clock reveals a month grid with ISO week numbers, a year
progress meter, and month stepping. Right click walks the common label
formats and writes the chosen one back to shell.json, so the bar shows
what the config stores. The week start toggles from the grid's "W"
heading and persists as weekStartDay, defaulting to the locale's own
first day.
Rich popup widgets live in their own plugin directories, so the clock
moves out of bar/widgets/ into panels/clock/. The id is unchanged, so
existing layouts and centerAnchor keep working.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Position the bar by dragging (or click-and-holding) empty bar space
toward a screen edge, with a ghost slab previewing the target edge.
With drag for position and double-click for transparency, the bar
config panel, its inline gear button, and the omarchy-launch-bar-settings
CLI are no longer needed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The omarchy-bar command had grown three overlapping ways to inspect the
bar (show/layout/list/options, plus selected/active/available/widgets
aliases) and mixed layout mutation in with bar-level settings. Untangle
it into two focused commands:
- omarchy-bar keeps only the bar-level settings that write shell.json:
use, reset, position, transparent, and settings. reset now delegates
to `use omarchy.bar` rather than duplicating the del(.bar.id) write.
- omarchy-bar-plugin owns all layout mutation: add, move, remove, set,
and replace, with the placement flags and jq resolve/anchor helpers.
`omarchy bar plugin ...` routes here via the dispatcher.
Drop the inspection commands entirely: the layout is visible on the bar,
the config is shell.json, and widget/option ids come from
`omarchy plugin list`. Nothing consumed the show output programmatically
except tests. This also removes omarchy-bar-position, whose jq write was
a duplicate of `omarchy bar position`.
Strip environment-invariant guards (require_command, require_omarchy_path)
that defended against jq or OMARCHY_PATH being absent — neither happens on
a real system. Keep the user-input validation (--section/--index) and the
atomic shell.json write.
Update callers (service install/remove, refresh-shell, plugin-clone,
plugin enable), keybindings, the menu, tests, and docs to the new split.
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
Clock and daytime were near-duplicates with different click semantics
(clock toggles between formats on left-click and opens the timezone
selector on right-click; daytime was non-pressable with a hover tooltip).
Standardize on clock, since it's the more capable of the two.
- Delete widgets/daytime.qml and its FirstPartyWidgets entry.
- Migrate shell-defaults.json, shell.qml's builtin fallback, Bar.qml's
fallback bar config, and the settings panel's builtin defaults so the
center anchor and center-section entry both reference "clock".
- Rename the daytimeSettings form to clockSettings and rephrase
"Tooltip format" → "Alternate format (click to toggle)" so the field
label matches clock's actual behavior.
- Update the gallery dropdown options and bar/README docs.