Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/quickshell-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
name: Lint Quickshell

on:
push:
branches:
- distro/arch-omarchy
paths:
- '**/*.qml'
- '.github/workflows/quickshell-lint.yml'
pull_request:
paths:
- '**/*.qml'
- '.github/workflows/quickshell-lint.yml'

permissions:
contents: read

env:
# Version of the Arch `quickshell` package this lint runs against. The
# Omarchy shell runs on Arch's quickshell (which pulls qt6-declarative, i.e.
# qmllint), so this tracks the Arch package - not Qt and not upstream tags.
# Renovate keeps it in sync via the repology datasource (see the
# customManagers entry in renovate.json); the version check below fails if
# the container's quickshell has drifted from this pin, which is the signal
# to merge the Renovate bump.
QUICKSHELL_VERSION: "0.3.0"

jobs:
qmllint:
runs-on: ubuntu-latest
container: archlinux:latest
steps:
- name: Install quickshell and qmllint
run: |
# Refresh the keyring first (the base image's can lag and break
# signature checks), then install quickshell (pulls qt6-declarative =
# qmllint) and git (needed by actions/checkout in this container).
pacman -Sy --noconfirm --needed archlinux-keyring
pacman -Su --noconfirm --needed quickshell qt6-declarative git

- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

- name: Trust the checkout (container git ownership)
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Verify quickshell matches the pinned version
run: |
installed="$(pacman -Q quickshell | awk '{print $2}' | cut -d- -f1)"
echo "Installed quickshell: $installed (pinned QUICKSHELL_VERSION: $QUICKSHELL_VERSION)"
if [ "$installed" != "$QUICKSHELL_VERSION" ]; then
echo "::error::Arch quickshell is $installed but QUICKSHELL_VERSION is pinned to $QUICKSHELL_VERSION. Merge the Renovate bump so the pin matches the package being linted against." >&2
exit 1
fi

- name: Lint QML
run: |
export PATH="/usr/lib/qt6/bin:$PATH"
mapfile -t qml_files < <(git ls-files '*.qml')
if [ "${#qml_files[@]}" -eq 0 ]; then
echo 'No QML files found.'
exit 0
fi
printf 'Linting %s QML file(s) with %s:\n' "${#qml_files[@]}" "$(qmllint --version)"
printf ' %s\n' "${qml_files[@]}"
# quickshell installs its QML modules under /usr/lib/qt6/qml, so `-I`
# lets qmllint resolve `Quickshell.*`. The Omarchy shell's own `qs.*`
# modules ship in the (unpackaged) shell source, so the import
# category stays disabled; qmllint still fails on real syntax errors.
qmllint -I /usr/lib/qt6/qml --import disable --unqualified disable "${qml_files[@]}"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ dot/.fallow/
scripts/.local/bin/dot
scripts/.local/bin/dot*
zsh/.local/share/zsh/chpwd-recent-dirs
zsh/.local/share/zsh/site-functions/_mise
zsh/.local/share/zsh/site-functions/_op
/session-*.md

68 changes: 68 additions & 0 deletions .opencode/skills/omarchy-shell-quickshell/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: omarchy-shell-quickshell
description: "Customise and reload the Omarchy shell (omarchy-shell) - the Quickshell process behind the bar, notifications, OSD, launcher, and settings - in this dotfiles repo. Use when working with the Omarchy shell or Quickshell: editing omarchy/.config/omarchy/plugins/, the shell.json generator dot/src/lib/omarchyShellConfig.ts, shell.json, BarWidget/WidgetButton or other Quickshell QML, running omarchy plugin or omarchy restart shell, referencing upstream Quickshell, or when a shell or bar change is not showing up."
---

# Omarchy Shell (Quickshell)

## Background / upstream

- The Omarchy shell is a single long-running **Quickshell** process (`omarchy-shell`) that hosts the top bar, notification daemon, on-screen display, launcher, and settings panel. Restarting "the shell" restarts all of them together.
- **Quickshell** is the upstream framework: a QML/Qt6 toolkit for Wayland desktop shells (layer-shell surfaces via `WlrLayershell`, an IPC bus, and live QML reload). Upstream is self-hosted Forgejo at `git.outfoxxed.me/quickshell/quickshell`; the opencode `quickshell` reference clones the official GitHub mirror `quickshell-mirror/quickshell`. Use it for QML types, layer-shell, `IpcHandler`, reloadable config, and the `qs` CLI.
- Omarchy's shell source lives in `~/.local/share/omarchy/shell/` - **READ-ONLY** (reading is useful: base classes `BarWidget`/`WidgetButton` in `shell/Ui/`, the bar host in `shell/plugins/bar/Bar.qml`). Edits are lost on `omarchy update`.
- This work targets the `omarchy-shell` head of `basecamp/omarchy#5856`. A stable Omarchy checkout may still contain Waybar and Walker, so verify its branch before treating it as the source for Omarchy 4 shell behaviour.

## Source of truth (never edit live)

- Custom shell content ships as **user plugins**. Edit the stow source `omarchy/.config/omarchy/plugins/<id>/` (stows to `~/.config/omarchy/plugins/<id>/`).
- `~/.config/omarchy/shell.json` is **generated, not hand-edited**. It is rendered by `dot` from `dot/src/lib/omarchyShellConfig.ts` (`mergeOmarchyShellConfig`), starting from Omarchy's default and inserting personal modules. Edit the generator, rebuild `dot`, then `dot stow` regenerates the file. The live file is mode `0600` and tracked by neither dotfiles repo.

## Plugin layout

A plugin is a folder with `manifest.json` + entry-point QML:

- `manifest.json`: `schemaVersion: 1`, required `id`, `name`, `version`, non-empty `kinds`, and `entryPoints` mapping each kind to a safe relative QML path. A bar widget also has `barWidget` metadata such as `displayName`, `category`, and `allowMultiple`. Third-party ids must be namespaced and cannot start with `omarchy.`.
- Entry QML: a bar widget extends `BarWidget` (`import qs.Commons`, `import qs.Ui`), sets `moduleName` to the plugin id, reads per-instance `shell.json` settings via `setting(name, fallback)`, and uses `WidgetButton` for clickable text cells.

Stow gotcha: `~/.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 (symlink -> dotfiles source).

## Reload matrix (run after a change)

| Change | Action |
| --- | --- |
| `shell.json` layout/settings, existing modules only | Hot-reloads on save - nothing to run |
| New plugin added or user plugin QML edited | `omarchy plugin rescan` |
| Omarchy's first-party shell QML edited, or rescan cannot recover | `omarchy restart shell` |

`omarchy plugin rescan` calls `rescanPlugins`, which unloads user plugin instances, clears the QML component cache, rescans manifests, and reloads enabled plugins. `reloadConfig` only reloads `shell.json`. A full `omarchy restart shell` refuses to run while the session is locked, then stops Quickshell and launches a detached replacement.

## The XCB trap (critical)

`omarchy-restart-shell` inherits the caller's environment. If `QT_QPA_PLATFORM=xcb`, Quickshell starts under XWayland, `WlrLayershell` cannot attach, and the shell renders as a **floating window** instead of bar/overlay surfaces (no error). This only matters when doing a full restart; plugin rescans stay in the running Wayland process.

- Interactive shells in this repo set `QT_QPA_PLATFORM="wayland;xcb"` (`zsh/.zshrc`) so a direct `omarchy restart shell` works. A plain `xcb` reintroduces the floating-window bug.
- From any non-interactive context (SSH, `systemd`, `cron`, an agent shell) force Wayland and make sure the session is reachable:

```bash
QT_QPA_PLATFORM=wayland omarchy restart shell # needs WAYLAND_DISPLAY + XDG_RUNTIME_DIR
```

- `dot update` bakes this in: it reloads the shell **only when the generated `shell.json` changed**, forcing `QT_QPA_PLATFORM=wayland` on the restart (`reloadOmarchyShell` in `dot/src/commands/Update.ts`). Standalone `dot stow` does not reload.

## Lint (required after every final QML change)

After every final QML edit, lint the files you touched with the **Qt6** `qmllint`. On Arch the Qt6 binary is `/usr/lib/qt6/bin/qmllint` - the bare `/usr/bin/qmllint` may be an older Qt5 build that rejects these flags. `-I /usr/lib/qt6/qml` lets it resolve the installed `Quickshell.*` modules:

```bash
/usr/lib/qt6/bin/qmllint -I /usr/lib/qt6/qml --import disable --unqualified disable <file>.qml
```

The shell's own `qs.*` modules ship in the (unpackaged) shell source, so the `import`/`unqualified` categories stay disabled as noise. This is a syntax-focused gate: unresolved Omarchy types can still produce warnings, while parse errors fail with a non-zero exit. This mirrors `.github/workflows/quickshell-lint.yml`, which lints inside an Arch container against the `quickshell` package pinned by `QUICKSHELL_VERSION` (kept in sync with the Arch package by Renovate via repology, and asserted to match). Lint must exit successfully before a QML change is considered done.

## Verify

- `omarchy plugin list` - is the plugin registered and enabled?
- `omarchy-shell shell debugBarGeometry` - per-module `x`/`width`/`visible`; confirm a widget renders or collapses.
- `omarchy plugin validate <folder>` rejects symlinked plugin folders - **expected** for stowed plugins, not a real error.
- `grim -g "0,0 360x32" out.png` - visual check of the bar's left edge; after a restart confirm `hyprctl layers | grep omarchy-bar`.
- After editing the generator: `mise run dot:check`.
8 changes: 5 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,17 @@ Keep shared cross-project agent behaviour in the global `~/.config/opencode/AGEN

## Repo-Specific Skills

- The repo-relevant skills are `effect` (Effect v4 code in `dot/`) and `opentui` (OpenTUI code in `dot/`); both self-document their triggers.
- The repo-relevant skills are `effect` (Effect v4 code in `dot/`), `opentui` (OpenTUI code in `dot/`), and `omarchy-shell-quickshell` (Omarchy shell / Quickshell work in this repo); all self-document their triggers.

## Omarchy Host Overrides

- Hyprland config is a stowed dotfiles package (`hypr/.config/hypr/`, conf-only), not a tracked Omarchy repo.
- `waybar` and `uwsm` are single-branch Omarchy repos expected on `main`.
- Hyprland config is a stowed dotfiles package (`hypr/.config/hypr/`), not a tracked Omarchy repo.
- `uwsm` is a single-branch Omarchy repo expected on `main`.
- `ghostty` is a stowed package (`ghostty/.config/ghostty/`) with `config.$OMARCHY_HOST` overrides loaded by `ghostty-host-config`.
- `bootstrap` is expected on `distro/omarchy`.
- Hypr host-specific overrides live under `~/.config/hypr/hosts/$OMARCHY_HOST`, selected by the runtime `~/.config/hypr/host` symlink.
- `dot stow` lays down the Hypr package with `--no-folding` and creates/repairs `~/.config/hypr/host`; `dot doctor` checks the host link and flags any leftover legacy `omarchy-hypr` clone.
- The Hypr package alone is stowed non-destructively: `dot stow` and `dot install` skip the usual unstow-then-restow for `hypr` so its symlinks (notably `hyprland.lua`) never vanish mid-stow, then reload Hyprland afterwards. This stops Hyprland's live-config autoreload from catching a missing config and dropping into emergency mode. Keep this behaviour if you touch the stow loop in `dot/src/commands/{Stow,Install}.ts`.
- A machine still on the retired `~/.config/hypr` `omarchy-hypr` clone halts `dot update` until the clone is backed up and re-stowed.
- If this host override layout changes, update the docs site (`docs/src/content/docs/`), `README.md`, `AGENTS.md`, and skill documentation together so repo instructions stay consistent.

Expand All @@ -87,6 +88,7 @@ Keep shared cross-project agent behaviour in the global `~/.config/opencode/AGEN
- Keep command and flag metadata in `dot/src/cli/spec.ts`; help and completion generation consume that registry.
- When changing `dot` commands, subcommands, aliases, or flags, run `dot completions` for each supported shell after rebuilding so the stowed completion files stay in sync.
- The `docs/` command reference (`docs/src/content/docs/dot/commands.md`) is generated from `dot/src/cli/spec.ts`. After changing commands, regenerate it with `mise run docs:gen:cli` (alongside shell completions) and commit the result.
- The Omarchy bar `shell.json` is generated by `dot/src/lib/omarchyShellConfig.ts`; the `dot update` loop reloads the running shell only when the rendered `shell.json` changes and forces `QT_QPA_PLATFORM=wayland` on the restart so the layer-shell bar attaches. Keep this behaviour if you touch `reloadOmarchyShell` in `dot/src/commands/Update.ts` or the shell-config return value. See the `omarchy-shell-quickshell` skill.

## Documentation Site

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ My public [Omarchy](https://omarchy.org) dotfiles, managed with GNU Stow and the

- Stow-based dotfiles rooted at `~/.config/dotfiles`, applied with the `dot` command
- A single compiled binary at `scripts/.local/bin/dot` (Bun + Effect v4 + OpenTUI) with a TUI dashboard and a full CLI
- Git/GitHub tooling: diff, log, status, workflow runs, and a notification inbox across managed repos, with Waybar modules
- Managed Omarchy repos (`bootstrap`, `waybar`, `uwsm`) and stowed Hyprland/Ghostty config
- Git/GitHub tooling: diff, log, status, workflow runs, and a notification inbox across managed repos, surfaced in the Omarchy Quickshell status bar
- Managed Omarchy repos (`bootstrap`, `uwsm`) and stowed Hyprland/Ghostty config
- Optional private overlay from `~/.config/dotfiles-private`
- Shared OpenCode agents, commands, skills, and plugins, published to [`timmo001/opencode-config`](https://github.com/timmo001/opencode-config)

Expand Down
5 changes: 3 additions & 2 deletions agents/.agents/skills/dotfiles-stow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,13 @@ When editing the public dotfiles repo (`~/.config/dotfiles`), treat a docs updat

## Omarchy Host Override Documentation Rules

- Hyprland config is a stowed dotfiles package (`hypr/.config/hypr/`, conf-only), not a tracked Omarchy repo.
- `waybar` and `uwsm` are single-branch Omarchy repos expected on `main`.
- Hyprland config is a stowed dotfiles package (`hypr/.config/hypr/`), not a tracked Omarchy repo.
- `uwsm` is a single-branch Omarchy repo expected on `main`.
- `ghostty` is a stowed package (`ghostty/.config/ghostty/`) with host overrides loaded by `ghostty-host-config`.
- `bootstrap` is expected on `distro/omarchy`.
- Hypr host-specific overrides live under `~/.config/hypr/hosts/$OMARCHY_HOST`, selected by the runtime `~/.config/hypr/host` symlink.
- `dot stow` lays down the Hypr package with `--no-folding` and creates/repairs `~/.config/hypr/host`; `dot doctor` checks it and flags any leftover legacy `omarchy-hypr` clone.
- The Hypr package alone is stowed non-destructively: `dot stow` and `dot install` skip the usual unstow-then-restow for `hypr` so its symlinks (notably `hyprland.lua`) never vanish mid-stow, then reload Hyprland afterwards, so Hyprland's autoreload never catches a missing config and trips emergency mode.
- When changing host override layout or guidance, update the relevant `README.md`, `AGENTS.md`, and skill documentation together.
- Repos that use host-specific overrides should have their own `README.md` and `AGENTS.md` that explicitly state the arrangement and the requirement to keep related documentation in sync when it changes.

Expand Down
4 changes: 2 additions & 2 deletions agents/.agents/skills/safe-process-signals/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Omarchy's restart helpers use `pkill -x <name>` which matches the process name o

```bash
# Best: exact process name (what omarchy-restart-* uses)
pkill -x "waybar"
pkill -x "quickshell"

# Acceptable: bracket trick when -f is needed
pkill -f "[n]ode.*server.js"
Expand All @@ -66,7 +66,7 @@ pkill -f "node.*server.js"
When you need to restart an Omarchy-managed app, prefer the built-in helper:

```bash
omarchy restart waybar
omarchy restart shell
omarchy restart terminal
omarchy restart-app <name> [args...]
```
Expand Down
7 changes: 6 additions & 1 deletion docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ export default defineConfig({
},
{
label: 'Omarchy & Hyprland',
items: [{ autogenerate: { directory: 'omarchy' } }],
items: [
{ label: 'Overview', slug: 'omarchy' },
{ label: 'Host Overrides', slug: 'omarchy/host-overrides' },
{ label: 'Shell (Quickshell)', slug: 'omarchy/shell' },
{ label: 'Controls', slug: 'omarchy/controls' },
],
},
{ label: 'Bar Integrations', slug: 'bar-integrations' },
{ label: 'Cleanup', slug: 'cleanup' },
Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/bar-integrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const commandCards = [

`--bar-json` is a shared status-bar contract rather than a single command. Tools emit one-line `--bar-json` output (with `text`, `tooltip`, and `class` fields) meant to be polled by a status bar module through its own short-lived cache. Left click opens the relevant TUI; right click refreshes the cache.

The JSON format is bar-agnostic: it works with [Waybar](https://github.com/Alexays/Waybar), [Quickshell](https://quickshell.outfoxxed.me), or any bar that can run a command and parse JSON. My own setup uses Waybar, so the bundled modules and `dot doctor` checks target Waybar, but the commands themselves do not depend on it.
The JSON format is bar-agnostic: it works with [Quickshell](https://quickshell.outfoxxed.me), [Waybar](https://github.com/Alexays/Waybar), or any bar that can run a command and parse JSON. My own setup uses the Omarchy 4 Quickshell bar, which runs these commands through `timmo.command` widgets, but the commands themselves do not depend on any particular bar. See [Shell (Quickshell)](/omarchy/shell/) for how that bar is configured.

## dot commands with bar output

Expand All @@ -37,4 +37,4 @@ Which repos and which activity reach the bar is controlled by the private `dot-g

## Health checks

`dot doctor` verifies the active status-bar module wiring for `git-workflows` and `git-notifications`, alongside `dot-git.yml` and the absence of legacy `git-workflow-watch` leftovers.
`dot doctor` verifies `dot-git.yml`, GitHub notification API access, and the absence of legacy `git-workflow-watch` leftovers.
3 changes: 1 addition & 2 deletions docs/src/content/docs/cleanup.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ After the stowed links and system config are removed, delete cloned repos and ge

```bash
rm -rf ~/.config/dotfiles-private
rm -rf ~/.config/bootstrap ~/.config/waybar ~/.config/uwsm
rm -rf ~/.config/bootstrap ~/.config/uwsm
rm -rf ~/.local/state/dot ~/.cache/dot
```

Expand All @@ -138,7 +138,6 @@ Private package repos and other private Git clones are configured by the private
If you removed Omarchy config directories that `dot init` replaces with managed repos, or stowed config directories that Omarchy should own again, refresh the stock Omarchy defaults afterwards.

```bash
omarchy refresh waybar
omarchy refresh shell
omarchy refresh hyprland
omarchy refresh config ghostty/config
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/configuration/private-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ sources:
Each `command` must behave like a status-bar poll, not a long-running watcher:

- Print one JSON object on the first line of stdout.
- Use the same `--bar-json` shape as Waybar modules: `text`, `tooltip`, and `class` fields. See [Bar Integrations](/bar-integrations/).
- Use the shared `--bar-json` status-bar shape: `text`, `tooltip`, and `class` fields. See [Bar Integrations](/bar-integrations/).
- Finish within eight seconds. `dot dashboard` kills overdue commands with `SIGTERM`.
- Avoid unbounded stream helpers. Commands containing `ha-watch-singleton`, `singleton-stream`, or `doorbell` are rejected as unsafe.

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/configuration/private-git.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ The `notifications.bar.ignore_bot_activity` key controls status-bar bot noise. A
## Requirements

- `dot git-notifications` requires `gh` authenticated with a classic token carrying the `notifications` or `repo` scope.
- `dot doctor` verifies `dot-git.yml`, the active status-bar module wiring, and the absence of legacy `git-workflow-watch` leftovers.
- `dot doctor` verifies `dot-git.yml` and the absence of legacy `git-workflow-watch` leftovers.
Loading