---
title: Shell (Quickshell)
description: The Omarchy 4 Quickshell shell, its generated shell.json, and the custom bar plugins.
---

Omarchy 4 replaces Waybar with a single long-running [Quickshell](https://quickshell.outfoxxed.me) process, `omarchy-shell`. That one process hosts the bar, the notification daemon, the on-screen display, the launcher, and the settings panel. Restarting "the shell" restarts all of them together.

These dotfiles do not fork the shell. They extend it through supported user configuration: a generated `shell.json` that lays out the bar, a stowed `shell.toml` style override, and a small set of user plugins that the bar loads as extra widgets.

## Source of truth

Four files drive the shell customisation, and none is hand-edited live:

- **`~/.config/omarchy/shell.json`** is generated, not stowed. `dot` renders it from Omarchy's shipped default and inserts the personal modules. The generator is `dot/src/lib/omarchyShellConfig.ts` (`mergeOmarchyShellConfig`). The live file is mode `0600` and tracked by neither dotfiles repo.
- **`~/.config/omarchy/shell.toml`** is stowed from `omarchy/.config/omarchy/shell.toml`. It keeps the shell-wide 12px type scale while setting the compact bar surface to 12px.
- **`~/.config/omarchy/extensions/omarchy-menu.jsonc`** is stowed from `omarchy/.config/omarchy/extensions/omarchy-menu.jsonc`. It adds **Dotfiles** (`dot update`) and **Topgrade** (`topgrade`) to the stock **Update** submenu without replacing the upstream menu.
- **Bar plugins** live under `omarchy/.config/omarchy/plugins/<id>/` in this repo, with machine-specific plugins in the matching private-overlay path. Both stow to `~/.config/omarchy/plugins/<id>/`. Each plugin is a `manifest.json` plus an entry-point QML file.

To change the bar, edit the generator (then rebuild `dot`) or edit a plugin's QML, never the live `shell.json`.

:::caution[Omarchy's shell source is read-only]
The shell itself lives in `~/.local/share/omarchy/shell/`. Reading it is useful (the `BarWidget` / `WidgetButton` base classes live there), but edits are lost on `omarchy update`. Customisation belongs in plugins and the generated config.
:::

## Generated `shell.json`

`dot stow` regenerates `shell.json` for the active [host](/omarchy/host-overrides/), starting from Omarchy's default and applying the declarative plugin layout in `omarchy-plugins.json`. The merge is idempotent: it only rewrites the file when the rendered content changes.

Per-host differences:

- **Clock format**: compact `HH:mm d MMM`, based on the final pre-Quattro Waybar clock without its weekday or ordinal day suffix. The `timmo.clock` clone reduces the stock clock's 8.75px cell padding to 6px. Left-click opens the calendar and world clocks, middle-click opens timezone settings, and right-click has no action. The popup shows Pacific, Mountain, Central, Eastern, and Local time with a `-24` to `+24` hour slider that shifts every clock together while dragging. Right-click the slider to return to now; closing the popup also resets it. `SUPER+CTRL+T` toggles the same popup.
- **Idle timers**: screensaver at 2.5 minutes and lock at 5 minutes on `laptop`; screensaver at 30 minutes and lock at 60 minutes on every other host.
- **Home Assistant dashboard**: desktop uses the office temperature and CO₂ sensors and adds office curtain controls; laptop uses the living-room sensors, adds VOC and dining-room temperature rows, and provides left, middle, and right living-room blind tilt controls. Each host puts its Low/High fan controls inline with the climate status at the top of the Air conditioner subsection.
- **System Bridge and Notes Capture**: `timmo.system-bridge` sits directly after Home Assistant, followed by `timmo.notes-capture`.

Secondary outputs keep the core menu, `timmo.workspaces`, `timmo.clock`, and built-in system widgets; Twitch, Git, command cells, Home Assistant, System Bridge, and Notes Capture collapse without starting per-output pollers or loading their panels.

The personal status widgets read from bar-agnostic scripts, `dot` JSON output, and Home Assistant. Command cells and the Home Assistant aggregate render at 10px; stock-sized custom icons, the clock, and workspaces render at 11px. See [Bar Integrations](/bar-integrations/) for the JSON commands behind the bar and dashboard.

## Stock Quattro comparison

The generated config starts from Omarchy Quattro's shipped `shell.json` and modifies that layout rather than replacing it wholesale. `omarchy-plugins.json` owns custom widget placement, replacements, shared settings, host-specific settings, and stock widget removals. TypeScript validates and applies those declarations without embedding personal widget settings.

`timmo.workspaces` replaces the stock workspace widget, shows only workspaces that currently exist, displays the focused workspace number at full opacity, and dims the others. The Home Assistant dashboard replaces the stock weather widget; its outdoor row opens the same Met Office weather entity and hourly forecast. The stock `omarchy.agents` widget is intentionally removed without replacement.

The Home Assistant plugin uses one bar widget whose width follows its visible content. It shows each currently visible row's original compact icon and value with its configured colour, or the Home Assistant icon when every row is quiet. In a Call is icon-only while active. Conditional rows appear only while active, warning, or critical; regular readings remain visible whenever their source output is available, except outdoor temperature, which appears only above 25 °C. The desktop air-conditioner target appears while its enabled input is on; the laptop target appears below 26 °C. It is blue while the matching climate entity is cooling, faded blue otherwise, and hidden while that climate entity is unavailable. Hovering does not reveal extra states. Clicking the widget opens the complete dashboard panel, whose heading includes the Home Assistant logo and whose rows include quiet and unavailable states. Activating a row that opens a link closes the panel first.

The stock alternate clock format, opaque bar, config version, and plugin list are also preserved. The normal clock format uses the compact pre-Quattro layout.

### Host overrides

| Setting | Stock Quattro | Desktop | Laptop |
| --- | --- | --- | --- |
| Screensaver | 2.5 minutes | 30 minutes | 2.5 minutes |
| Lock | 5 minutes | 60 minutes | 5 minutes |

Home Assistant entity IDs vary by host. The laptop adds the VOC and dining-room temperature rows; the desktop omits them. The background doorbell watcher uses the active workspace on both hosts.

The session keeps its normal Qt scale for applications, while the `~/.config/hypr/bin/quickshell` wrapper resets `QT_SCALE_FACTOR` to `1` only for Omarchy shell launches. Wayland output scaling still handles the shell's HiDPI rendering, avoiding an additional Qt multiplier across the bar, notifications, and popup plugins.

## Custom plugins

A plugin is a folder with `manifest.json` (schema version 1, an `id` like `timmo.<name>`, its `kinds`, and entry-point QML) plus the QML itself. A bar widget extends `BarWidget`, reads per-instance settings from `shell.json` via `setting(name, fallback)`, and uses `WidgetButton` for clickable cells.

| Plugin | Kind | What it does |
| --- | --- | --- |
| `timmo.clock` | bar-widget | Keeps the stock clock and calendar behaviour, adds five world clocks with a `-24` to `+24` hour slider, and uses compact 6px cell padding. It is published from [`timmo001/omarchy-clock`](https://github.com/timmo001/omarchy-clock). |
| `timmo.command` | bar-widget | Runs a shell command on an interval and renders its status-bar JSON (`text` / `tooltip` / `class`) with compact 6px horizontal cell margins. The Waybar `custom/*` equivalent. |
| `timmo.home-assistant` (private overlay) | service, bar-widget | Summarises active HA schedule, status, NAS, and environment rows in one widget and adds a native dashboard panel while keeping the doorbell watcher alive in the background. |
| `timmo.git` | service, bar-widget | Combines repository state and filtered GitHub notifications in one widget and native panel. |
| `timmo.momentumctl` | service, panel | Shows MOMENTUM 4 battery state and controls ANC, transparency, anti-wind, and headset behaviour. |
| `timmo.notes-capture` | bar-widget | Captures multiline text directly through the local Notes processor with Automatic or searchable repository targeting. |
| `timmo.system-bridge` | service, bar-widget | Streams local CPU, memory, root disk, fan, GPU, temperature, system, and optional battery health into a compact widget and read-only panel. |
| `timmo.stream-command` | bar-widget | Runs a long-running command that streams status-bar JSON lines and renders the latest line with compact 6px horizontal cell margins (for watchers like `ha-watch-singleton`). |
| `timmo.twitch` | service, bar-widget | Shows live Twitch state and opens an attached panel for channels and notification controls. |
| `timmo.workspaces` | bar-widget | Workspace numbers without persistent workspaces: only existing workspaces show, the focused one at full opacity and the rest dimmed. |

`timmo.command` and `timmo.stream-command` both support `classColors` (class-name to colour), `hideClasses`, `hiddenText`, `onClick` / `onClickRight`, and `revealOnHover`, so the generator can style and wire every cell without bespoke QML per module. Shell-launched web apps run through the reusable `launch-floating-webapp` command, which places only the new window at mobile size in the monitor's bottom-right corner. Normal launches of the same sites remain tiled. TUI click targets use the existing `TUI.float` app id.

`timmo.twitch` is published from [`timmo001/twitch-notifications`](https://github.com/timmo001/twitch-notifications/tree/main/omarchy-plugin) into the generated private `timmo001/omarchy-twitch-notifications` repository, which dotfiles pins as a managed submodule. It keeps one polling service for the whole shell and shares it across bar instances. Left click opens its channel panel, middle click rechecks notifications, and right click restarts the notifier. The active state stays hidden until the bar is hovered; live and unavailable states remain visible.

`timmo.git` polls `dot git-diff --bar-json` and `dot git-notifications --bar-json` once per minute through one shell service. Each source appears only while its count is above zero, so a clean source contributes neither an icon nor a count. Important notifications are red, ordinary changes or unread notifications amber, pull-only repositories green, private-only dirt blue, and unavailable state grey. When both sources are clear, the widget collapses and reveals both bare icons dimmed while hovering the bar or while its panel is open. Left click opens the panel with actions, changed repository rows, and notification rows; right click refreshes both sources. Activating a changed repository opens `dot git-diff` directly in lazygit for that repository; quitting lazygit resumes the selected diff TUI. Other actions open the full Changed TUI, Other TUI, filtered notifications TUI, or a notification URL.

`timmo.momentumctl` is pinned from [`timmo001/omarchy-momentumctl`](https://github.com/timmo001/omarchy-momentumctl) as a managed submodule and uses the mise-managed `momentumctl` CLI. `CTRL+ALT+M` toggles its standalone panel for ANC, adaptive noise control, transparency, anti-wind mode, Smart Pause, on-head detection, auto-answer, and Comfort Call. The panel refreshes headset state when opened and serialises writes because each command opens a Bluetooth RFCOMM session.

`timmo.system-bridge` is published from [`timmo001/system-bridge`](https://github.com/timmo001/system-bridge/tree/dev/omarchy-plugin) into the generated [`timmo001/omarchy-system-bridge`](https://github.com/timmo001/omarchy-system-bridge) repository, which dotfiles pins as a managed submodule. It requires the `system-bridge-git` package and keeps one `system-bridge client data watch` process loaded for the shell. It subscribes to CPU, memory, disks, sensors, GPUs, system, and battery updates, accepts `DATA_UPDATE` NDJSON one module at a time, and retains the last valid module values when an individual line is malformed. The widget becomes online only after a valid update, restarts the watcher five seconds after it exits, and terminates a watcher that produces no valid update for 150 seconds so the normal exit path can restart it. CPU and RAM percentages use the normal foreground; loading and offline states are grey. CPU or RAM at 90%, a reported high sensor temperature, stale data, a pending reboot, or a newer System Bridge version turns the widget amber, while a reported critical sensor temperature turns it urgent. The tooltip is limited to status, CPU, memory, load, and CPU temperature. Its lazy, read-only panel presents CPU, memory, load, temperatures, root filesystem usage, fan speeds, available GPU load/memory/power/temperature metrics, uptime, and a pending reboot as a scrollable, keyboard-filterable list with primary labels and secondary values. `CTRL+ALT+S` toggles it. It supports Escape, panel switching, and the `timmo.system-bridge` IPC target's `open`, `close`, `show`, `hide`, and `toggle` methods.

`timmo.notes-capture` is published from [`timmo001/notes`](https://github.com/timmo001/notes/tree/dev/omarchy-plugin) into the generated [`timmo001/omarchy-notes-capture`](https://github.com/timmo001/omarchy-notes-capture) repository, which dotfiles pins as a managed submodule. It opens a keyboard-first multiline capture panel on the primary output. `SUPER+CTRL+ALT+C` toggles it, Ctrl+Enter submits to `notes capture` through the private `notes-capture-local` adapter, and Escape closes it. Send sits directly below the editor. Each submission clears the editor immediately and joins a sequential background queue, so another note can be entered while processing continues; the panel reports active, queued, completed, and failed states. The Clear heading action resets the editor, repository search, repository selection, and visible status. The searchable picker puts Dotfiles, Skills, Notes, Context, and Workflows first, then keeps the remaining notification-enabled private repositories in configuration order; Automatic leaves repository selection to the Notes agent. The panel checks the local OpenCode service every 15 seconds and disables Send while it is unavailable. Unsaved editor text is cached and restored when the panel reloads. Processing failures raise a desktop notification and save the failed submission separately under the user's cache directory. It never opens or submits to the hosted web capture. Its IPC target is `timmo.notes-capture` with `open`, `close`, `show`, `hide`, and `toggle` methods.

`timmo.home-assistant` keeps the HA pollers and singleton streams in one shell service. `Config.qml` owns the desktop and laptop entity mappings, commands, actions, aggregate show conditions, labels, icons, colours, and panel sizing; the other QML files only run and render that configuration. The host-specific target-temperature reading keeps its desktop/laptop visibility gates and cooling colour in the bar, but is omitted from the Environment section because the Controls section already provides its editor. The Controls section starts with Air conditioner and separately groups climate status, target editing, and enabled controls without repeating that name on each row. The climate status row shows `Cool` while cooling, followed by its labelled fan mode and target temperature separated by bullets, opens Home Assistant more-info, and puts Low (`1`) and High (`2`) fan actions inline; the row and actions are omitted while the host's climate entity is unavailable. The target editor has decrement and increment buttons using the helper's configured step, while its label and the desktop enabled-control label open their Home Assistant more-info windows. Laptop includes a second row of presets ordered 22.6 °C, 22.8 °C, 23.2 °C, 23.4 °C, 23.6 °C, 23.8 °C, 24.2 °C, 24.4 °C, and Off (36 °C). A sofa-labelled Blinds subsection follows with left, middle, and right living-room rows showing their state and current tilt position and using `cover.set_cover_tilt_position` presets ordered 100%, 80%, 60%, 40%, 20%, and 0%. Desktop instead has an enabled toggle below the target, followed by an Office curtains status row and a compact left-to-right grid for the Close, 10%, 20%, 30%, and 60% presets on `cover.curtain`; these panel-only actions do not affect the bar. Clicking Calendar or a sensor opens its existing floating full view; Time check and In a call retain their direct toggle actions. The doorbell stream stays loaded without a visible row and continues opening the camera popup on an active transition.

Twitch, Git, and Home Assistant share the same quick-filter controls. Typing filters their visible actions and rows. Up and Down move through matches, Enter activates one, Backspace edits the filter, Ctrl+Backspace removes a word, Ctrl+U clears it, Ctrl+R refreshes, and Escape clears an active filter before closing the panel.

:::note[New plugins need a stow]
`~/.config/omarchy/plugins/` is a real directory with per-plugin symlinks. A brand-new plugin needs `dot stow` to create its symlink before the shell sees it; editing an existing plugin's files is already live.
:::

### Managed third-party plugins

`omarchy plugin add` keeps Omarchy's normal review and enable prompts, then asks where a bar widget belongs. After choosing left, centre, or right, choose an existing neighbour and whether the new widget goes before or after it. The lifecycle hook imports the validated checkout as a submodule under `omarchy/.config/omarchy/plugins/`, records its placement in `omarchy-plugins.json`, and runs `dot stow`.

The parent repository pins the exact plugin SHA. `.gitmodules` records the upstream URL and branch or tag, normalising GitHub SSH URLs to HTTPS so external build systems can clone every submodule, and Renovate proposes newer pins. `omarchy plugin update` moves managed pins after validation; `omarchy plugin remove` removes the submodule and placement declaration. After an interactive add or remove, choose **No** (the default), **Commit**, or **Commit and push**. Non-interactive runs leave ordinary unstaged dotfiles changes. Unmanaged plugins retain Omarchy's stock add, update, and remove behaviour.

Other devices receive managed plugins through the repository's recursive submodule checkout. `dot update` and `dot stow` then restore the live plugin symlink and generated layout.

## Reloading the shell

| Change | Action |
| --- | --- |
| `shell.json` layout or settings, existing modules only | Hot-reloads on save, nothing to run |
| New plugin added | `omarchy-shell shell rescanPlugins`, then the hot-reload picks it up |
| User plugin QML edited | Hot-reloads on save, nothing to run |
| Omarchy's first-party shell QML edited, or hot-reload fails | `omarchy restart shell` (full restart) |

`dot update` bakes this in: it regenerates `shell.json` and reloads the running shell **only when the rendered config changed**. A standalone `dot stow` regenerates the file but does not reload.

:::caution[Force Wayland on restart]
`omarchy restart shell` inherits the caller's environment. If `QT_QPA_PLATFORM=xcb`, Quickshell starts under XWayland, the layer-shell surface cannot attach, and the bar renders as a floating window with no error. Interactive shells here set `QT_QPA_PLATFORM="wayland;xcb"`, and `dot update` forces `QT_QPA_PLATFORM=wayland` on its reload. From any non-interactive context (SSH, systemd, an agent shell), force Wayland explicitly:

```bash
QT_QPA_PLATFORM=wayland omarchy restart shell
```

:::
