Consolidate nested READMEs into docs/; trim drift
This commit is contained in:
@@ -120,7 +120,7 @@ Plugin contract:
|
|||||||
`~/.config/omarchy/plugins/<id>/` (third-party).
|
`~/.config/omarchy/plugins/<id>/` (third-party).
|
||||||
- Every plugin ships a `manifest.json` declaring `id`, `kinds`,
|
- Every plugin ships a `manifest.json` declaring `id`, `kinds`,
|
||||||
`activation`, and `entryPoints`. The full schema is in
|
`activation`, and `entryPoints`. The full schema is in
|
||||||
`default/quickshell/omarchy-shell/README.md`.
|
[`docs/omarchy-shell.md`](docs/omarchy-shell.md).
|
||||||
- Entry-point QML files are `Item`s (not `ShellRoot`), and accept the
|
- Entry-point QML files are `Item`s (not `ShellRoot`), and accept the
|
||||||
shell-injected properties `omarchyPath`, `shell`, `manifest`, and
|
shell-injected properties `omarchyPath`, `shell`, `manifest`, and
|
||||||
`pluginRegistry` / `barWidgetRegistry` as appropriate.
|
`pluginRegistry` / `barWidgetRegistry` as appropriate.
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ Omarchy is a beautiful, modern & opinionated Linux distribution by DHH.
|
|||||||
|
|
||||||
Read more at [omarchy.org](https://omarchy.org).
|
Read more at [omarchy.org](https://omarchy.org).
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
- [omarchy-shell](docs/omarchy-shell.md) — shell host, plugin manifest, IPC, `shell.json`, custom bar modules
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Omarchy is released under the [MIT License](https://opensource.org/licenses/MIT).
|
Omarchy is released under the [MIT License](https://opensource.org/licenses/MIT).
|
||||||
|
|||||||
@@ -1,217 +0,0 @@
|
|||||||
# Omarchy shell
|
|
||||||
|
|
||||||
`omarchy-shell` is a single long-running [Quickshell](https://quickshell.org/)
|
|
||||||
instance that hosts the Omarchy desktop. Hyprland autostarts one shell per
|
|
||||||
session; everything else — the bar, the bar settings UI, the background
|
|
||||||
switcher, future panels and overlays — runs **inside** the shell as a
|
|
||||||
plugin.
|
|
||||||
|
|
||||||
Hosting everything inside one shell means:
|
|
||||||
|
|
||||||
- shared services and singletons live once, not once per process
|
|
||||||
- summoning a panel is an IPC call into a process that is already running,
|
|
||||||
not a fresh `quickshell -p ...` cold start
|
|
||||||
- third-party plugins can be loaded from disk without changing any source
|
|
||||||
code in Omarchy itself
|
|
||||||
|
|
||||||
The runtime layout in this branch:
|
|
||||||
|
|
||||||
```
|
|
||||||
default/quickshell/omarchy-shell/
|
|
||||||
shell.qml entry point (ShellRoot)
|
|
||||||
shell-defaults.json canonical out-of-the-box config
|
|
||||||
services/
|
|
||||||
PluginRegistry.qml discovers, validates plugins, looks up enabled state in shell.json
|
|
||||||
BarWidgetRegistry.qml unified registry for bar widgets (1p + 3p)
|
|
||||||
ui/
|
|
||||||
settings/
|
|
||||||
DynamicSettingsForm.qml renders plugin-declared schemas
|
|
||||||
plugins/
|
|
||||||
bar/ first-party plugins (see plugins/README.md)
|
|
||||||
settings/
|
|
||||||
image-picker/
|
|
||||||
menu/
|
|
||||||
notifications/
|
|
||||||
osd/
|
|
||||||
polkit/
|
|
||||||
```
|
|
||||||
|
|
||||||
The plugin discovery path is documented in [plugins/README.md](plugins/README.md).
|
|
||||||
|
|
||||||
## Plugin manifest
|
|
||||||
|
|
||||||
Every plugin ships a `manifest.json` describing what it is and how the
|
|
||||||
shell should load it. Minimal example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"id": "my.org.cool-clock",
|
|
||||||
"name": "Cool clock",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"author": "You",
|
|
||||||
"description": "A clock that does cool things",
|
|
||||||
"kinds": ["bar-widget"],
|
|
||||||
"activation": "on-demand",
|
|
||||||
"entryPoints": { "barWidget": "Widget.qml" },
|
|
||||||
"barWidget": {
|
|
||||||
"displayName": "Cool clock",
|
|
||||||
"category": "Time",
|
|
||||||
"allowMultiple": false,
|
|
||||||
"defaults": { "format": "HH:mm" },
|
|
||||||
"schema": [
|
|
||||||
{ "key": "format", "type": "string", "label": "Format" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Supported `kinds`:
|
|
||||||
|
|
||||||
| Kind | What it is |
|
|
||||||
|--------------|--------------------------------------------------------------|
|
|
||||||
| `bar-widget` | A component that the bar can drop into a section |
|
|
||||||
| `panel` | A persistent or summoned floating window (e.g. bar settings) |
|
|
||||||
| `overlay` | A fullscreen overlay (e.g. background switcher) |
|
|
||||||
| `menu` | A summoned menu surface |
|
|
||||||
| `service` | A headless singleton, no UI |
|
|
||||||
| `bar` | Reserved for the first-party bar host (`omarchy.bar`). Third-party plugins should ship `bar-widget`s; they do not replace the host bar. |
|
|
||||||
|
|
||||||
`activation` is either `persistent` (loaded on startup, never unloaded) or
|
|
||||||
`on-demand` (loaded by `shell summon <id>` and unloaded by `shell hide`).
|
|
||||||
Plugins that need to outlive a single summon can set `keepLoaded: true`
|
|
||||||
(e.g. the image picker keeps its overlay window mounted between
|
|
||||||
summons).
|
|
||||||
|
|
||||||
The full schema lives in `services/PluginRegistry.qml`.
|
|
||||||
|
|
||||||
## Installing a third-party plugin
|
|
||||||
|
|
||||||
1. Drop the plugin into `~/.config/omarchy/plugins/<plugin-id>/`.
|
|
||||||
The directory must contain a `manifest.json` plus the QML files
|
|
||||||
referenced from its `entryPoints`.
|
|
||||||
2. `omarchy-shell-ipc shell rescanPlugins`.
|
|
||||||
3. Enable the plugin with `omarchy-shell-ipc shell setPluginEnabled <id> true`.
|
|
||||||
4. If it's a `bar-widget`, add it to a layout section from the bar editor.
|
|
||||||
|
|
||||||
First-party plugins under `default/quickshell/omarchy-shell/plugins/`
|
|
||||||
are discovered the same way and cannot be disabled.
|
|
||||||
|
|
||||||
## IPC contract
|
|
||||||
|
|
||||||
The shell exposes a single `shell` IPC target plus whatever extra targets
|
|
||||||
individual plugins register (e.g. the bar's `bar` target for refresh
|
|
||||||
hooks, the image picker's `image-selector` target). `omarchy-menu` uses the
|
|
||||||
shell target to summon the first-party `omarchy.menu` plugin instead of
|
|
||||||
running a separate Quickshell instance.
|
|
||||||
|
|
||||||
| Method | Returns | Effect |
|
|
||||||
|------------------------------------------|---------|-------------------------------------------------------|
|
|
||||||
| `ping` | `ok` | health check |
|
|
||||||
| `summon <id> <payloadJson>` | `ok` / `unknown` | load + open a panel/overlay plugin |
|
|
||||||
| `hide <id>` | — | close a previously-summoned plugin |
|
|
||||||
| `toggle <id> <payloadJson>` | — | summon if closed, hide if open |
|
|
||||||
| `rescanPlugins` | — | re-walk plugin dirs and pick up new/changed manifests |
|
|
||||||
| `setPluginEnabled <id> <enabled>` | — | flip the persisted enabled bit (see note) |
|
|
||||||
| `listPlugins` | JSON | every discovered plugin (id, name, kinds, enabled) |
|
|
||||||
|
|
||||||
Direct invocation:
|
|
||||||
|
|
||||||
```
|
|
||||||
quickshell ipc -p $OMARCHY_PATH/default/quickshell/omarchy-shell call shell ping
|
|
||||||
```
|
|
||||||
|
|
||||||
Hyprland starts the shell through `omarchy-restart-shell` on boot.
|
|
||||||
Use `omarchy-restart-shell` to reload the long-running shell process.
|
|
||||||
|
|
||||||
A convenience wrapper, [`omarchy-shell-ipc`](../../../bin/omarchy-shell-ipc),
|
|
||||||
starts the shell if it is not already running, then forwards a `call`. It
|
|
||||||
is the canonical way for other Omarchy CLIs to talk to the shell.
|
|
||||||
|
|
||||||
```
|
|
||||||
omarchy-shell-ipc shell ping
|
|
||||||
omarchy-shell-ipc shell summon omarchy.settings "{}"
|
|
||||||
omarchy-shell-ipc shell listPlugins
|
|
||||||
omarchy-shell-ipc shell rescanPlugins
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note on `setPluginEnabled`:** the `enabled` argument is a string. Only the
|
|
||||||
literal `"true"` enables the plugin; every other value (including `"True"`,
|
|
||||||
`"1"`, `"yes"`, or omitted) disables it. This keeps the IPC surface
|
|
||||||
type-stable across QML's `string`-only IPC arguments.
|
|
||||||
|
|
||||||
## Persisted state
|
|
||||||
|
|
||||||
There is one user config file. Everything that distinguishes your
|
|
||||||
customization from the shipped defaults lives in it.
|
|
||||||
|
|
||||||
| Path | Owner | Purpose |
|
|
||||||
|-----------------------------------|----------------|--------------------------------------------------------|
|
|
||||||
| `~/.config/omarchy/shell.json` | the shell | full layout + per-entry settings + enabled plugin list |
|
|
||||||
| `~/.config/omarchy/plugins/<id>/` | user | drop-in third-party plugin source files |
|
|
||||||
|
|
||||||
The `shell-defaults.json` bundled with the shell describes the
|
|
||||||
fresh-install state. When the user has no `shell.json`, the shell uses
|
|
||||||
the defaults verbatim. Once the user customizes anything, `shell.json`
|
|
||||||
becomes the authoritative file — we do **not** deep-merge defaults back
|
|
||||||
in. Pressing **Reset bar to defaults** in `omarchy launch bar settings`
|
|
||||||
rewrites the `bar` subtree from the current `shell-defaults.json`.
|
|
||||||
|
|
||||||
### shell.json shape
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"bar": {
|
|
||||||
"position": "top",
|
|
||||||
"transparent": false,
|
|
||||||
"centerAnchor": "calendar",
|
|
||||||
"fontFamily": "JetBrainsMono Nerd Font",
|
|
||||||
"layout": {
|
|
||||||
"left": [ { "id": "omarchy" }, { "id": "workspaces" } ],
|
|
||||||
"center": [ { "id": "calendar", "format": "HH:mm" } ],
|
|
||||||
"right": [
|
|
||||||
{ "id": "audioPanel" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
{ "id": "omarchy.settings" },
|
|
||||||
{ "id": "omarchy.image-picker" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Storage rules
|
|
||||||
|
|
||||||
1. **Every plugin instance is one entry.** Either in `bar.layout.<section>`
|
|
||||||
for bar widgets, or in `plugins[]` for panels, overlays, services,
|
|
||||||
menus, and anything else non-bar.
|
|
||||||
2. **Settings are inline on the entry.** No `config:` sub-object, no
|
|
||||||
separate per-plugin settings file, no merge layers. The fields on each
|
|
||||||
entry are the values the plugin sees.
|
|
||||||
3. **Enabled ⇔ present.** A plugin is enabled iff its id appears somewhere
|
|
||||||
in shell.json. For bar widgets, the bar settings UI adds/removes layout
|
|
||||||
entries; other plugin kinds are enabled with the shell IPC.
|
|
||||||
4. **Multiple instances** are allowed when a manifest sets
|
|
||||||
`allowMultiple: true`. Each instance is independent — e.g. two clocks
|
|
||||||
in different timezones are just two `{"id":"calendar", "timezone": ...}`
|
|
||||||
entries with their own values.
|
|
||||||
5. **`version: 1` is required** at the top level. The shell will fall back
|
|
||||||
to defaults rather than load an unknown version.
|
|
||||||
|
|
||||||
## Implementation history
|
|
||||||
|
|
||||||
Built up in phases on this branch:
|
|
||||||
|
|
||||||
- Phase 1 — `omarchy-shell phase 1: host the existing bar in a single shell`
|
|
||||||
- Phase 2 — `omarchy-shell phase 2: plugin registry and bar widget registry`
|
|
||||||
- Phase 3 — `omarchy-shell phase 3: fold bar-settings into the shell as a panel plugin`
|
|
||||||
- Phase 4 — `omarchy-shell phase 4: absorb background-switcher as a plugin`
|
|
||||||
- Phase 5 — `omarchy-shell phase 5: docs, cleanup, and migration crumbs`
|
|
||||||
- Phase 6 — `omarchy-shell phase 6: reviewer cleanup (path traversal, collision, races)`
|
|
||||||
- Phase 7 — `omarchy-shell phase 7: replace socket with IpcHandler, rename to image-picker`
|
|
||||||
- Phase 8a — `omarchy-shell phase 8a: unified shell.json with inline plugin settings`
|
|
||||||
|
|
||||||
Shared services and Pipewire/UPower/Hyprland consolidation are explicitly
|
|
||||||
out of scope here and deferred to a follow-up after a review pass.
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
# 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` |
|
|
||||||
| Dev gallery | `omarchy.dev-gallery` | `panel` | on-demand | `dev-gallery/GalleryPanel.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`](../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`](bar/README.md) for the widget catalogue
|
|
||||||
and customization schema.
|
|
||||||
|
|
||||||
## Bar settings
|
|
||||||
|
|
||||||
Visual editor for the bar layout. Summoned by
|
|
||||||
`omarchy-shell-ipc 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-ipc shell summon omarchy.image-picker '<jsonPayload>'`.
|
|
||||||
The payload can carry `imageDirs`, `imageRows`, `selectedImage`,
|
|
||||||
`selectionFile`, `doneFile`, `showLabels`, `filterable`. Best for
|
|
||||||
in-shell callers that already speak JSON.
|
|
||||||
- Direct IPC target: `omarchy-shell-ipc image-selector open <imageDirs> <imageRowsB64> <selectedImage> <selectionFile> <doneFile> <showLabels> <filterable>`.
|
|
||||||
Positional args; `imageRowsB64` is base64-encoded so embedded newlines /
|
|
||||||
tabs survive the bash argv handoff. This is what `omarchy-menu-images`
|
|
||||||
uses. 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-ipc 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.
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
# Omarchy bar
|
|
||||||
|
|
||||||
This is the Quickshell implementation of the Omarchy status bar. It is
|
|
||||||
shipped as a first-party plugin of [`omarchy-shell`](../../README.md), the
|
|
||||||
long-running shell host. The bar is mounted at startup and lives inside
|
|
||||||
the shell for its whole session.
|
|
||||||
|
|
||||||
- `manifest.json` declares the plugin (`id: omarchy.bar`, `kind: bar`, `activation: persistent`) and points at `Bar.qml` as the entry point.
|
|
||||||
- `Bar.qml` is Omarchy-owned bar engine code, loaded by the omarchy-shell host. Users should not edit it directly.
|
|
||||||
- `widgets/` holds first-party widgets — modular, interactive components shipped with Omarchy.
|
|
||||||
- `common/` holds shared QML helpers (buttons, sliders, popup cards).
|
|
||||||
- The bar receives its config from the host shell as a `barConfig` property; the host loads it from `~/.config/omarchy/shell.json` (or `shell-defaults.json` when the user has no file).
|
|
||||||
- `omarchy-style-bar-position` updates only the user shell.json file.
|
|
||||||
|
|
||||||
## Customizing
|
|
||||||
|
|
||||||
The bar config lives under the `bar:` key of [`~/.config/omarchy/shell.json`](../../README.md#shelljson-shape). Out of the box the shell uses [`shell-defaults.json`](../../shell-defaults.json). Once you customize anything via `omarchy launch bar settings` or by editing shell.json directly, your file is canonical — there is no deep-merge.
|
|
||||||
|
|
||||||
Launch the visual editor with `omarchy launch bar settings` (or run `omarchy-launch-bar-settings`) to reorder widgets, add/remove them, and tweak per-widget options without editing JSON by hand. You can also right-click empty space to the left or right of the centered clock to open it; double-left-click the same empty space to toggle bar transparency.
|
|
||||||
|
|
||||||
Example `shell.json` (bar subtree only shown):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"bar": {
|
|
||||||
"position": "top",
|
|
||||||
"transparent": false,
|
|
||||||
"centerAnchor": "calendar",
|
|
||||||
"layout": {
|
|
||||||
"left": [
|
|
||||||
{ "id": "omarchy" },
|
|
||||||
{ "id": "spacer", "size": 12 },
|
|
||||||
{ "id": "workspaces" }
|
|
||||||
],
|
|
||||||
"center": [
|
|
||||||
{ "id": "media" },
|
|
||||||
{ "id": "calendar", "format": "HH:mm" }
|
|
||||||
],
|
|
||||||
"right": [
|
|
||||||
{ "id": "audioPanel" },
|
|
||||||
{ "id": "battery" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`centerAnchor` pins one center module to the exact horizontal/vertical center and flanks others around it. Set to an empty string to disable anchoring (the center list is centered as a group).
|
|
||||||
|
|
||||||
## Module catalogue
|
|
||||||
|
|
||||||
### First-party interactive widgets (in `widgets/`)
|
|
||||||
|
|
||||||
| Name | What it does | Interactions |
|
|
||||||
|---|---|---|
|
|
||||||
| `media` | MPRIS now-playing — scrolling track + artist, cover-art popup | left = play/pause · middle = next · scroll = prev/next · right = popup |
|
|
||||||
| `audioPanel` | Volume icon + popup with master slider, output-device picker, per-app mixer | left = popup · right = mute · middle = audio TUI · scroll = volume |
|
|
||||||
| `networkPanel` | Wi-Fi/Ethernet icon + popup with Wi-Fi scan, signal, connect, DNS provider selection | left = popup · right = nmtui |
|
|
||||||
| `bluetoothPanel` | Bluetooth icon + popup with device list, connect/disconnect, battery | left = popup · right = toggle radio · middle = bluetoothctl TUI |
|
|
||||||
| `calendar` | Clock + popup with month-grid calendar | left = popup · right = tz selector |
|
|
||||||
| `notificationCenter` | Bell with badge + popup with recent notifications, DND toggle | left = popup · right = toggle DND |
|
|
||||||
| `systemStats` | Inline CPU + memory sparklines, popup with detail | left = popup · right = terminal |
|
|
||||||
| `weatherFlyout` | Weather icon + popup with forecast | left = popup · right = full notification |
|
|
||||||
| `idleInhibitor` | Coffee-cup that toggles `omarchy-toggle-idle` | left = toggle |
|
|
||||||
| `microphone` | Mic icon + scroll volume | left = mute toggle · middle = audio TUI · scroll = source volume |
|
|
||||||
|
|
||||||
### Built-in legacy modules (in `shell.qml`)
|
|
||||||
|
|
||||||
`omarchy`, `workspaces`, `clock`, `weather`, `update`, `voxtype`, `screenRecording`, `idle`, `notifications`, `tray`, `bluetooth`, `network`, `audio`, `cpu`, `battery`.
|
|
||||||
|
|
||||||
These remain available — set them in `layout` to use them instead of the richer widget versions.
|
|
||||||
|
|
||||||
## Orientation
|
|
||||||
|
|
||||||
All widgets work in `top`, `bottom`, `left`, and `right` positions. Popups anchor on the side opposite the bar edge, sliding into the workspace. Vertical bars use 28px width; widgets that show text fall back to compact icon-only forms (e.g. `media` hides its scrolling label).
|
|
||||||
|
|
||||||
## Custom user modules
|
|
||||||
|
|
||||||
The schema accepts arbitrary module ids that you provide. Set `type` to `command` for shell-driven output or `qml` for a custom QML widget. Both still go under `bar.layout.<section>` in `shell.json`.
|
|
||||||
|
|
||||||
Command module:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"bar": {
|
|
||||||
"layout": {
|
|
||||||
"right": [
|
|
||||||
{ "id": "tray" },
|
|
||||||
{ "id": "vpn", "type": "command", "exec": "~/.config/omarchy/bar/scripts/vpn-status", "interval": 5, "tooltip": "VPN", "onClick": "nm-connection-editor" },
|
|
||||||
{ "id": "audioPanel" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The command may print plain text or Waybar-style JSON, for example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"text":"","tooltip":"Work VPN","class":"active"}
|
|
||||||
```
|
|
||||||
|
|
||||||
QML module:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"bar": {
|
|
||||||
"layout": {
|
|
||||||
"right": [
|
|
||||||
{ "id": "gpu", "type": "qml" },
|
|
||||||
{ "id": "audioPanel" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then create `~/.config/omarchy/bar/modules/gpu.qml`. If you want to store it elsewhere, add a `source` path.
|
|
||||||
|
|
||||||
Custom QML modules should be an `Item` with `implicitWidth` and `implicitHeight`. They may optionally define these properties, which the bar fills after loading:
|
|
||||||
|
|
||||||
```qml
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
Item {
|
|
||||||
property var bar
|
|
||||||
property string moduleName
|
|
||||||
property var settings
|
|
||||||
|
|
||||||
implicitWidth: 28
|
|
||||||
implicitHeight: bar ? bar.barSize : 26
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "GPU"
|
|
||||||
color: bar ? bar.foreground : "white"
|
|
||||||
font.family: bar ? bar.fontFamily : "monospace"
|
|
||||||
font.pixelSize: 12
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: if (bar) bar.run("omarchy-launch-or-focus-tui btop")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bar properties available to widgets
|
|
||||||
|
|
||||||
Widgets receive `bar` (the shell root), `moduleName` (string), and `settings` (object) injected at load time. The bar exposes:
|
|
||||||
|
|
||||||
- `bar.foreground`, `bar.background`, `bar.urgent` — theme colors (live-updated)
|
|
||||||
- `bar.fontFamily` — current monospace family
|
|
||||||
- `bar.position` — `"top" | "bottom" | "left" | "right"`
|
|
||||||
- `bar.vertical` — boolean shortcut
|
|
||||||
- `bar.barSize` — 26 horizontal / 28 vertical
|
|
||||||
- `bar.run(command)` — fire-and-forget bash exec
|
|
||||||
- `bar.shellQuote(value)` — safe shell-quote a string
|
|
||||||
- `bar.showTooltip(target, text)` / `bar.hideTooltip(target)` — shared tooltip popup
|
|
||||||
- `bar.requestPopout(owner)` / `bar.releasePopout(owner)` — one-popup-at-a-time coordinator
|
|
||||||
|
|
||||||
First-party widgets live in `widgets/<name>.qml` and are picked up by the
|
|
||||||
shell's `BarWidgetRegistry` at startup; reference one by `id` in any
|
|
||||||
layout list.
|
|
||||||
|
|
||||||
Third-party widgets ship as separate plugins under
|
|
||||||
`~/.config/omarchy/plugins/<plugin-id>/` with their own `manifest.json`
|
|
||||||
declaring `kinds: ["bar-widget"]` and a `barWidget` entry point. See
|
|
||||||
[../../README.md](../../README.md) for the manifest schema. Enable or
|
|
||||||
rescan third-party plugins with `omarchy-shell-ipc shell setPluginEnabled`
|
|
||||||
and `omarchy-shell-ipc shell rescanPlugins`.
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
# omarchy-shell
|
||||||
|
|
||||||
|
A single long-running [Quickshell](https://quickshell.org/) instance
|
||||||
|
that hosts the Omarchy desktop. The bar, panels, overlays, menus, and
|
||||||
|
services all run inside as plugins. IPC is the canonical way for CLIs
|
||||||
|
to talk to a running shell — `omarchy-shell-ipc` auto-starts it on
|
||||||
|
first call.
|
||||||
|
|
||||||
|
## Plugin manifest
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "my.org.cool-clock",
|
||||||
|
"name": "Cool clock",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "You",
|
||||||
|
"description": "A clock that does cool things",
|
||||||
|
"kinds": ["bar-widget"],
|
||||||
|
"activation": "on-demand",
|
||||||
|
"entryPoints": { "barWidget": "Widget.qml" }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`kinds` (a manifest may declare more than one):
|
||||||
|
|
||||||
|
| Kind | What it is |
|
||||||
|
|--------------|---------------------------------------------|
|
||||||
|
| `bar-widget` | Component the bar drops into a section |
|
||||||
|
| `panel` | Floating window (e.g. bar settings) |
|
||||||
|
| `overlay` | Fullscreen overlay (e.g. background picker) |
|
||||||
|
| `menu` | Summoned menu surface |
|
||||||
|
| `service` | Headless singleton, no UI |
|
||||||
|
|
||||||
|
`activation` is `persistent` (loaded at startup) or `on-demand`
|
||||||
|
(loaded by `shell summon`, unloaded by `shell hide`). On-demand
|
||||||
|
plugins can set `keepLoaded: true` to survive between summons.
|
||||||
|
|
||||||
|
Full schema: `services/PluginRegistry.qml`.
|
||||||
|
|
||||||
|
## Installing a third-party plugin
|
||||||
|
|
||||||
|
1. Drop into `~/.config/omarchy/plugins/<id>/` with a `manifest.json`
|
||||||
|
plus the QML referenced from `entryPoints`.
|
||||||
|
2. `omarchy-shell-ipc shell rescanPlugins`
|
||||||
|
3. `omarchy-shell-ipc shell setPluginEnabled <id> true`
|
||||||
|
4. Bar widgets also need adding to a section via bar settings.
|
||||||
|
|
||||||
|
## IPC
|
||||||
|
|
||||||
|
The shell exposes a `shell` target plus extra targets registered by
|
||||||
|
individual plugins (`bar`, `image-selector`, …).
|
||||||
|
|
||||||
|
| Method | Effect |
|
||||||
|
|---------------------------------------|---------------------------------|
|
||||||
|
| `ping` | health check |
|
||||||
|
| `summon <id> <payloadJson>` | load + open a plugin |
|
||||||
|
| `hide <id>` | close a previously-summoned |
|
||||||
|
| `toggle <id> <payloadJson>` | summon if closed, hide if open |
|
||||||
|
| `rescanPlugins` | re-walk plugin dirs |
|
||||||
|
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit |
|
||||||
|
| `listPlugins` | JSON of every discovered plugin |
|
||||||
|
|
||||||
|
`setPluginEnabled` takes a string; only literal `"true"` enables.
|
||||||
|
|
||||||
|
## shell.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"bar": {
|
||||||
|
"position": "top",
|
||||||
|
"transparent": false,
|
||||||
|
"centerAnchor": "calendar",
|
||||||
|
"fontFamily": "JetBrainsMono Nerd Font",
|
||||||
|
"layout": {
|
||||||
|
"left": [ { "id": "omarchy" } ],
|
||||||
|
"center": [ { "id": "calendar", "format": "HH:mm" } ],
|
||||||
|
"right": [ { "id": "audioPanel" } ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
{ "id": "omarchy.settings" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
1. Every plugin instance is one entry — `bar.layout.<section>` for
|
||||||
|
bar widgets, `plugins[]` for everything else.
|
||||||
|
2. Settings are inline on the entry. No `config:` sub-object, no
|
||||||
|
merge layers.
|
||||||
|
3. Enabled ⇔ present.
|
||||||
|
4. `allowMultiple: true` in the manifest permits multiple instances.
|
||||||
|
5. `version: 1` is required.
|
||||||
|
|
||||||
|
`shell-defaults.json` describes the fresh-install state. When no
|
||||||
|
user `shell.json` exists, defaults are used verbatim. Once the user
|
||||||
|
customizes, `shell.json` is canonical — there is no deep-merge.
|
||||||
|
|
||||||
|
## Custom bar modules
|
||||||
|
|
||||||
|
If a full plugin is overkill, declare a one-off module inline in
|
||||||
|
`bar.layout.<section>`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "id": "vpn", "type": "command", "exec": "~/.config/omarchy/bar/scripts/vpn-status",
|
||||||
|
"interval": 5, "tooltip": "VPN", "onClick": "nm-connection-editor" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Output is plain text or Waybar-style JSON (`{ "text": ..., "tooltip": ..., "class": ... }`).
|
||||||
|
|
||||||
|
For a custom QML widget:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "id": "gpu", "type": "qml" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Then `~/.config/omarchy/bar/modules/gpu.qml` (or set `source` to point
|
||||||
|
elsewhere). The module is an `Item` and receives `bar`, `moduleName`,
|
||||||
|
`settings` properties. `bar` exposes `foreground` / `background` /
|
||||||
|
`urgent` / `fontFamily` / `position` / `vertical` / `barSize`, plus
|
||||||
|
`run(cmd)`, `shellQuote(v)`, `showTooltip(t, s)` / `hideTooltip(t)`,
|
||||||
|
`requestPopout(o)` / `releasePopout(o)`.
|
||||||
Reference in New Issue
Block a user