Skip to content

Shell (Quickshell)

Omarchy 4 replaces Waybar with a single long-running Quickshell 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.

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 and 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.

dot stow regenerates shell.json for the active host, starting from Omarchy’s default and adding personal modules around the stock ones (“add, not remove”). 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, middle-click opens timezone settings, and right-click has no action.
  • 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 and adds VOC and dining-room temperature rows. Each host puts its climate status and Low/High fan controls at the top of the Air conditioner subsection.

Secondary outputs keep the core menu, timmo.workspaces, timmo.clock, and built-in system widgets; Twitch, Git, command cells, and the Home Assistant widget 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 for the JSON commands behind the bar and dashboard.

The generated config starts from Omarchy Quattro’s shipped shell.json and modifies that layout rather than replacing it wholesale.

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.

SettingStock QuattroDesktopLaptop
Screensaver2.5 minutes30 minutes2.5 minutes
Lock5 minutes60 minutes5 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.

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.

PluginKindWhat it does
timmo.clockbar-widgetKeeps the stock clock and calendar behaviour with compact 6px cell padding.
timmo.commandbar-widgetRuns 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-assistantservice, bar-widgetSummarises 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.gitservice, bar-widgetCombines repository state and filtered GitHub notifications in one widget and native panel.
timmo.stream-commandbar-widgetRuns 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.twitchservice, bar-widgetShows live Twitch state and opens an attached panel for channels and notification controls.
timmo.workspacesbar-widgetWorkspace 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 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.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 separately groups climate status, fan mode, target editing, and enabled controls under one Air conditioner subheading without repeating that name on each row. The climate status row shows the HVAC state and labelled fan mode separated by a bullet, opens Home Assistant more-info, and puts Low (1) and High (2) fan actions at the top of the subsection; 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. Laptop also has 23.8 °C and Off (36 °C) presets; desktop instead has an enabled toggle immediately below the target. Desktop also starts Controls with 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 and are omitted on laptop. 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.

ChangeAction
shell.json layout or settings, existing modules onlyHot-reloads on save, nothing to run
New plugin addedomarchy-shell shell rescanPlugins, then the hot-reload picks it up
User plugin QML editedHot-reloads on save, nothing to run
Omarchy’s first-party shell QML edited, or hot-reload failsomarchy 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.