The three components were three takes on the same shape \u2014 a clickable rectangle with text/icon, a hot/hover state, an optional persistent border, and an optional 'selected' or 'active' highlight. CursorPill was a 30-line PillButton wrapper that added a hovered() signal; ChoiceButton was effectively PillButton with selected: bool painting an accent fill+border. Collapse them into a single qs.Ui.Button. State flags compose independently: hasCursor / hover hot fill active persistent foreground-tint fill selected accent fill + accent border bordered: true persistent 1px idle border (form primaries) focusable: true Tab focus paints the accent ring pressed pressed fill The hovered(bool) signal is now built-in, so CursorPill's wrapper is unnecessary. ButtonGroup wraps a Row+Repeater for the form-style 'pick one of N' pattern; panel-cursor-driven cases still compose Buttons directly in a Row with per-instance hasCursor wiring. Theme tokens move into a new [style] section in shell.toml: border-width = 1 focus-border-width = 3 idle-border-alpha = 0.4 hot-fill-alpha = 0.08 selected-fill-alpha = 0.18 pressed-fill-alpha = 0.22 focus-fill-alpha = 0.22 Style.qml parses these out of the same shell.toml [font] / [bar] already reads, and exposes pre-computed Style.hotFill / selectedFill / pressedFill / idleBorderColor / selectedAccentFill / borderWidth + the existing focusBorder* tokens. Themes that don't ship a [style] section get the previous defaults unchanged. Dev gallery consolidates three sections (PillButton, CursorPill, ChoiceButton) into Button + ButtonGroup, with the cursor model sections renamed accordingly.
First-party plugins
These plugins ship with Omarchy and are loaded by the shell at startup.
They use the same manifest.json contract as third-party plugins; the
only difference is that the shell flags them with __isFirstParty: true
so they cannot be disabled.
User-installed plugins live alongside these conceptually but on disk under
~/.config/omarchy/plugins/<plugin-id>/ rather than in this directory.
| Plugin | id | kinds | activation | entry point |
|---|---|---|---|---|
| Bar | omarchy.bar |
bar |
persistent | bar/Bar.qml |
| Bar settings | omarchy.settings |
panel |
on-demand | settings/SettingsPanel.qml |
| Image picker | omarchy.image-picker |
overlay |
on-demand | image-picker/ImagePicker.qml |
| Emoji picker | omarchy.emoji-picker |
overlay |
on-demand | emoji-picker/EmojiPicker.qml |
| Clipboard mgr | omarchy.clipboard-picker |
overlay |
on-demand | clipboard-picker/ClipboardPicker.qml |
| Omarchy menu | omarchy.menu |
menu |
on-demand | menu/Menu.qml |
| Notifications | omarchy.notifications |
service |
persistent | notifications/Service.qml |
| OSD | omarchy.osd |
panel |
persistent | osd/Osd.qml |
| Polkit agent | omarchy.polkit |
service |
persistent | polkit/PolkitAgent.qml |
Bar
The status bar. Mounted at startup, lives forever. Layout lives in the
top-level bar: subtree of ~/.config/omarchy/shell.json (with the shell
providing shell-defaults.json when the user has
no file). Owns the bar IPC target for refresh hooks fired by indicator
scripts. See bar/README.md for the widget catalogue
and customization schema.
Bar settings
Visual editor for the bar layout. Summoned by
omarchy-shell shell summon omarchy.settings "{}" (which is what
omarchy launch bar settings ultimately calls). Provides:
- bar position and center-anchor controls
- per-section add/move/remove/edit of bar widget entries
- dynamic per-widget settings forms that write inline back to the corresponding shell.json entry
Image picker
Fullscreen image-grid selector overlay. Used by omarchy-menu-images
(wallpaper picker) and omarchy-theme-switcher (theme picker) and any
other caller that wants to present a directory of images with previews.
Two ways to drive it:
- Shell-level summon:
omarchy-shell shell summon omarchy.image-picker '<jsonPayload>'. The payload can carryimageDirs,imageRows,selectedImage,selectionFile,doneFile,showLabels,filterable. Best for in-shell callers that already speak JSON. - Direct IPC target:
omarchy-shell image-selector open <imageDirs> <imageRowsB64> <selectedImage> <selectionFile> <doneFile> <showLabels> <filterable>. Positional args;imageRowsB64is base64-encoded so embedded newlines / tabs survive the bash argv handoff. This is whatomarchy-menu-imagesuses. Colors come from the central shell theme singleton; there is no per-call override surface.
The selection round-trip remains file-based: callers create a
selection_file and done_file (both mktemp), pass the paths, and
poll done_file for existence. The plugin writes the chosen path into
selection_file and touches done_file when it's done. cancel IPC
clears it without writing a selection.
The plugin has keepLoaded: true so the layer-shell window survives
between summons within a single shell session.
Polkit agent
Theme-aware authentication dialog for privileged actions. It uses
Quickshell's native Quickshell.Services.Polkit.PolkitAgent backend and
runs inside the long-lived omarchy-shell process, replacing the old
polkit-gnome-authentication-agent-1 autostart.
Omarchy menu
Quickshell-powered replacement for the legacy Walker-driven omarchy-menu.
The menu UI lives in menu/Menu.qml as a first-party menu plugin and is
summoned through the shell (omarchy-shell shell summon omarchy.menu ...),
so it shares the long-running omarchy-shell process instead of starting a
second Quickshell instance.
The menu definition lives outside the shell host code:
- defaults:
default/omarchy/omarchy-menu.jsonc - user extensions:
~/.config/omarchy/extensions/omarchy-menu.jsonc
The shell parses both JSONC files at startup (with watchChanges: true
so edits take effect without a restart), evaluates when: / checked:
bash expressions in a single batched subprocess, and executes the
selected action: string directly via Quickshell.execDetached. The
long-running shell process keeps the parsed menu in memory, so the
keybind → IPC → visible path costs ~30ms cold.
Coming soon
omarchy.theme-switcher— folds theme switching into the shell.