The legacy 'workspaces' built-in renders cleaner than the animated workspacesPro file widget. Removed the widget file, its first-party registration in Bar.qml, and updated the default layouts and docs. 'workspaces' loses the (legacy) tag in the catalog since there's no alternative anymore.
210 lines
8.7 KiB
Markdown
210 lines
8.7 KiB
Markdown
# 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)
|
|
bar-settings/
|
|
image-picker/
|
|
```
|
|
|
|
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 |
|
|
|
|
`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` — or open the Plugin Manager
|
|
tab in `omarchy launch bar-settings` and click **Rescan**.
|
|
3. Enable the plugin (Plugin Manager **Enable** toggle, or
|
|
`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 background switcher's `image-selector` target).
|
|
|
|
| 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
|
|
```
|
|
|
|
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.bar-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 to defaults** in `omarchy launch bar-settings`
|
|
rewrites `shell.json` from the current `shell-defaults.json`.
|
|
|
|
### shell.json shape
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"bar": {
|
|
"position": "top",
|
|
"centerAnchor": "calendar",
|
|
"fontFamily": "JetBrainsMono Nerd Font",
|
|
"layout": {
|
|
"left": [ { "id": "omarchy" }, { "id": "workspaces" } ],
|
|
"center": [ { "id": "calendar", "format": "HH:mm" } ],
|
|
"right": [
|
|
{ "id": "audioPanel" },
|
|
{ "id": "controlCenter" },
|
|
{ "id": "powerMenu" }
|
|
]
|
|
}
|
|
},
|
|
"plugins": [
|
|
{ "id": "omarchy.bar-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. To disable, remove it. (The bar settings UI does both.)
|
|
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.
|