Namespace omarchy plugins
This commit is contained in:
+16
-10
@@ -41,9 +41,13 @@ Full schema: [`shell/services/PluginRegistry.qml`](../shell/services/PluginRegis
|
||||
|
||||
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.
|
||||
2. `omarchy plugin rescan`
|
||||
3. `omarchy plugin enable <id>`
|
||||
4. Bar widgets also need adding to a section with `omarchy plugin bar add <id>`
|
||||
or the visual editor (`omarchy plugin bar edit`).
|
||||
|
||||
The lower-level IPC methods remain available through `omarchy-shell shell ...`
|
||||
for callers that need to talk directly to the running shell.
|
||||
|
||||
## IPC
|
||||
|
||||
@@ -77,9 +81,9 @@ individual plugins (`bar`, `image-selector`, …).
|
||||
"centerAnchor": "calendar",
|
||||
"fontFamily": "JetBrainsMono Nerd Font",
|
||||
"layout": {
|
||||
"left": [ { "id": "Omarchy" } ],
|
||||
"center": [ { "id": "calendar", "format": "HH:mm" } ],
|
||||
"right": [ { "id": "AudioPanel" } ]
|
||||
"left": [ { "id": "omarchy.menu" } ],
|
||||
"center": [ { "id": "omarchy.clock", "format": "HH:mm" } ],
|
||||
"right": [ { "id": "omarchy.audio" } ]
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
@@ -94,10 +98,12 @@ Rules:
|
||||
bar widgets, `plugins[]` for everything else.
|
||||
2. Settings are inline on the entry. No `config:` sub-object, no
|
||||
merge layers.
|
||||
3. Third-party enabled ⇔ present; first-party plugins are always enabled.
|
||||
4. `allowMultiple: true` in the manifest permits multiple instances.
|
||||
5. `idle.screensaver` and `idle.lock` are seconds since user idle began.
|
||||
6. `version: 1` is required.
|
||||
3. Built-in bar widget ids are namespaced (`omarchy.clock`, `omarchy.audio`, …);
|
||||
legacy ids such as `Clock` and `AudioPanel` are accepted as aliases.
|
||||
4. Third-party enabled ⇔ present; first-party plugins are always enabled.
|
||||
5. `allowMultiple: true` in the manifest permits multiple instances.
|
||||
6. `idle.screensaver` and `idle.lock` are seconds since user idle began.
|
||||
7. `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
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
echo "Namespace Omarchy built-in bar widget ids in shell.json"
|
||||
|
||||
config_file="$HOME/.config/omarchy/shell.json"
|
||||
|
||||
if [[ -s $config_file ]] && jq -e 'type == "object" and .version == 1' "$config_file" >/dev/null 2>&1; then
|
||||
tmp=$(mktemp)
|
||||
jq '
|
||||
def canonical_widget_id:
|
||||
if . == "Omarchy" then "omarchy.menu"
|
||||
elif . == "Workspaces" then "omarchy.workspaces"
|
||||
elif . == "Media" then "omarchy.media"
|
||||
elif . == "AudioPanel" then "omarchy.audio"
|
||||
elif . == "MonitorPanel" then "omarchy.monitor"
|
||||
elif . == "NetworkPanel" then "omarchy.network"
|
||||
elif . == "PowerPanel" then "omarchy.power"
|
||||
elif . == "BluetoothPanel" then "omarchy.bluetooth"
|
||||
elif . == "Clock" then "omarchy.clock"
|
||||
elif . == "Indicators" then "omarchy.indicators"
|
||||
elif . == "NotificationCenter" then "omarchy.notifications"
|
||||
elif . == "SystemUpdate" then "omarchy.system-update"
|
||||
elif . == "SystemStats" then "omarchy.system-stats"
|
||||
elif . == "Tray" then "omarchy.tray"
|
||||
elif . == "Weather" then "omarchy.weather"
|
||||
elif . == "Microphone" then "omarchy.microphone"
|
||||
elif . == "ActiveWindow" then "omarchy.active-window"
|
||||
elif . == "KeyboardLayout" then "omarchy.keyboard-layout"
|
||||
elif . == "LockKeys" then "omarchy.lock-keys"
|
||||
elif . == "Spacer" then "omarchy.spacer"
|
||||
else . end;
|
||||
|
||||
def canonical_entry:
|
||||
if type == "string" then canonical_widget_id
|
||||
elif type == "object" and has("id") then .id = (.id | canonical_widget_id)
|
||||
else . end;
|
||||
|
||||
.bar.centerAnchor = ((.bar.centerAnchor // "") | canonical_widget_id) |
|
||||
.bar.layout.left = ((.bar.layout.left // []) | map(canonical_entry)) |
|
||||
.bar.layout.center = ((.bar.layout.center // []) | map(canonical_entry)) |
|
||||
.bar.layout.right = ((.bar.layout.right // []) | map(canonical_entry))
|
||||
' "$config_file" >"$tmp" && mv "$tmp" "$config_file"
|
||||
fi
|
||||
+34
-2
@@ -42,6 +42,34 @@ QtObject {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value)
|
||||
}
|
||||
|
||||
readonly property var builtinWidgetAliases: ({
|
||||
"Omarchy": "omarchy.menu",
|
||||
"Workspaces": "omarchy.workspaces",
|
||||
"Media": "omarchy.media",
|
||||
"AudioPanel": "omarchy.audio",
|
||||
"MonitorPanel": "omarchy.monitor",
|
||||
"NetworkPanel": "omarchy.network",
|
||||
"PowerPanel": "omarchy.power",
|
||||
"BluetoothPanel": "omarchy.bluetooth",
|
||||
"Clock": "omarchy.clock",
|
||||
"Indicators": "omarchy.indicators",
|
||||
"NotificationCenter": "omarchy.notifications",
|
||||
"SystemUpdate": "omarchy.system-update",
|
||||
"SystemStats": "omarchy.system-stats",
|
||||
"Tray": "omarchy.tray",
|
||||
"Weather": "omarchy.weather",
|
||||
"Microphone": "omarchy.microphone",
|
||||
"ActiveWindow": "omarchy.active-window",
|
||||
"KeyboardLayout": "omarchy.keyboard-layout",
|
||||
"LockKeys": "omarchy.lock-keys",
|
||||
"Spacer": "omarchy.spacer"
|
||||
})
|
||||
|
||||
function canonicalWidgetId(id) {
|
||||
var key = String(id || "")
|
||||
return builtinWidgetAliases[key] || key
|
||||
}
|
||||
|
||||
// Best-effort base64 decode. Returns "" on parse failure rather than
|
||||
// surfacing garbage downstream.
|
||||
function decodeBase64(value) {
|
||||
@@ -72,8 +100,12 @@ QtObject {
|
||||
// so the two never drift. Entries are deep-cloned to decouple from the
|
||||
// input config; consumers can mutate without leaking back to shell.json.
|
||||
function normalizeLayoutEntry(entry) {
|
||||
if (typeof entry === "string") return { id: entry }
|
||||
if (isPlainObject(entry) && entry.id) return cloneJson(entry)
|
||||
if (typeof entry === "string") return { id: canonicalWidgetId(entry) }
|
||||
if (isPlainObject(entry) && entry.id) {
|
||||
var copy = cloneJson(entry)
|
||||
copy.id = canonicalWidgetId(copy.id)
|
||||
return copy
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
+30
-12
@@ -89,9 +89,24 @@ The full schema lives in `services/PluginRegistry.qml`.
|
||||
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 shell rescanPlugins`.
|
||||
3. Enable the plugin with `omarchy-shell shell setPluginEnabled <id> true`.
|
||||
4. If it's a `bar-widget`, add it to a layout section from the bar editor.
|
||||
2. `omarchy plugin rescan`.
|
||||
3. Enable the plugin with `omarchy plugin enable <id>`.
|
||||
4. If it's a `bar-widget`, place it with `omarchy plugin bar add <id>` or
|
||||
open the visual editor with `omarchy plugin bar edit`.
|
||||
|
||||
The lower-level IPC equivalents are still available via `omarchy-shell shell rescanPlugins`,
|
||||
`omarchy-shell shell setPluginEnabled <id> true`, and `omarchy-shell shell listPlugins`.
|
||||
The `omarchy plugin` command wraps those calls and can also edit the persisted
|
||||
bar layout in `shell.json`.
|
||||
|
||||
To hack on an existing widget safely, clone it into a user plugin instead of
|
||||
editing the built-in source. Third-party ids must be namespaced and may not use
|
||||
the reserved `omarchy.*` prefix.
|
||||
|
||||
```bash
|
||||
omarchy plugin clone omarchy.clock local.clock --replace --open pi
|
||||
omarchy plugin clone # interactive source/name/tool picker
|
||||
```
|
||||
|
||||
First-party plugins under `shell/plugins/`
|
||||
are discovered the same way and cannot be disabled.
|
||||
@@ -168,12 +183,12 @@ rewrites the `bar` subtree from the current `shell-defaults.json`.
|
||||
"bar": {
|
||||
"position": "top",
|
||||
"transparent": false,
|
||||
"centerAnchor": "Clock",
|
||||
"centerAnchor": "omarchy.clock",
|
||||
"layout": {
|
||||
"left": [ { "id": "Omarchy" }, { "id": "Workspaces" } ],
|
||||
"center": [ { "id": "Clock", "format": "HH:mm" } ],
|
||||
"left": [ { "id": "omarchy.menu" }, { "id": "omarchy.workspaces" } ],
|
||||
"center": [ { "id": "omarchy.clock", "format": "HH:mm" } ],
|
||||
"right": [
|
||||
{ "id": "AudioPanel" }
|
||||
{ "id": "omarchy.audio" }
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -189,18 +204,21 @@ rewrites the `bar` subtree from the current `shell-defaults.json`.
|
||||
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. **Third-party enabled ⇔ present.** A third-party plugin is enabled iff
|
||||
3. **Built-in widget ids are namespaced.** Use ids such as `omarchy.clock`,
|
||||
`omarchy.audio`, and `omarchy.network`. Legacy ids like `Clock` and
|
||||
`AudioPanel` are accepted as aliases and migrated forward.
|
||||
4. **Third-party enabled ⇔ present.** A third-party 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. First-party plugins are always enabled.
|
||||
4. **Multiple instances** are allowed when a manifest sets
|
||||
5. **Multiple instances** are allowed when a manifest sets
|
||||
`allowMultiple: true`. Each instance is independent — e.g. two clock
|
||||
widgets in different timezones are just two `{"id":"Clock", "timezone": ...}`
|
||||
widgets in different timezones are just two `{"id":"omarchy.clock", "timezone": ...}`
|
||||
entries with their own values.
|
||||
5. **Idle timings are top-level.** `idle.screensaver` and `idle.lock`
|
||||
6. **Idle timings are top-level.** `idle.screensaver` and `idle.lock`
|
||||
are seconds since user idle began, so the default lock fires at 300s
|
||||
even if the 150s screensaver starts first.
|
||||
6. **`version: 1` is required** at the top level. The shell will fall back
|
||||
7. **`version: 1` is required** at the top level. The shell will fall back
|
||||
to defaults rather than load an unknown version.
|
||||
|
||||
## Implementation history
|
||||
|
||||
@@ -31,7 +31,7 @@ Item {
|
||||
property var fallbackBarConfig: ({
|
||||
position: "top",
|
||||
transparent: false,
|
||||
centerAnchor: "Clock",
|
||||
centerAnchor: "omarchy.clock",
|
||||
layout: { left: [], center: [], right: [] }
|
||||
})
|
||||
property var layoutConfig: fallbackBarConfig.layout
|
||||
@@ -133,7 +133,7 @@ Item {
|
||||
var trayEntry = null
|
||||
var result = []
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
if (entryId(entries[i]) === "Tray") trayEntry = entries[i]
|
||||
if (entryId(entries[i]) === "omarchy.tray") trayEntry = entries[i]
|
||||
else result.push(entries[i])
|
||||
}
|
||||
if (trayEntry) {
|
||||
@@ -148,7 +148,7 @@ Item {
|
||||
|
||||
position = normalizePosition(config.position)
|
||||
transparent = config.transparent === true
|
||||
centerAnchor = String(config.centerAnchor || "")
|
||||
centerAnchor = Util.canonicalWidgetId(config.centerAnchor || "")
|
||||
layoutConfig = normalizeLayout(config.layout)
|
||||
barConfigSerial++
|
||||
}
|
||||
@@ -208,7 +208,7 @@ Item {
|
||||
}
|
||||
|
||||
function canonicalWidgetId(name) {
|
||||
return String(name)
|
||||
return Util.canonicalWidgetId(name)
|
||||
}
|
||||
|
||||
function expandPath(path) {
|
||||
|
||||
@@ -6,16 +6,16 @@ QtObject {
|
||||
required property var barWidgetRegistry
|
||||
|
||||
readonly property var metadata: ({
|
||||
"Omarchy": { displayName: "Omarchy menu", description: "Launches the Omarchy menu", category: "Compositor", allowMultiple: false },
|
||||
"Workspaces": { displayName: "Workspaces", description: "Workspace number indicators", category: "Compositor", allowMultiple: false },
|
||||
"Media": { displayName: "Media", description: "MPRIS now-playing with playback controls", category: "Media", allowMultiple: false },
|
||||
"AudioPanel": { displayName: "Audio", description: "Volume slider, output picker, per-app mixer", category: "Audio", allowMultiple: false, sourceDir: "../panels", sourceName: "Audio" },
|
||||
"MonitorPanel": { displayName: "Display", description: "Brightness slider and laptop display controls", category: "System", allowMultiple: false, sourceDir: "../panels", sourceName: "Monitor" },
|
||||
"NetworkPanel": { displayName: "Network", description: "Wi-Fi list and connection state", category: "Network", allowMultiple: false, sourceDir: "../panels", sourceName: "Network" },
|
||||
"PowerPanel": { displayName: "Power", description: "Battery, power profile, and system stats", category: "System", allowMultiple: false, sourceDir: "../panels", sourceName: "Power" },
|
||||
"BluetoothPanel": { displayName: "Bluetooth", description: "Bluetooth device list with connect/disconnect", category: "Network", allowMultiple: false, sourceDir: "../panels", sourceName: "Bluetooth" },
|
||||
"Clock": { displayName: "Clock", description: "Day/time label; click to toggle alternate format", category: "Time", allowMultiple: false, settingsForm: "clockSettings" },
|
||||
"Indicators": { displayName: "Indicators", description: "Manual state indicators", category: "Status", allowMultiple: true,
|
||||
"omarchy.menu": { displayName: "Omarchy menu", description: "Launches the Omarchy menu", category: "Compositor", allowMultiple: false, sourceName: "Omarchy", legacyId: "Omarchy" },
|
||||
"omarchy.workspaces": { displayName: "Workspaces", description: "Workspace number indicators", category: "Compositor", allowMultiple: false, sourceName: "Workspaces", legacyId: "Workspaces" },
|
||||
"omarchy.media": { displayName: "Media", description: "MPRIS now-playing with playback controls", category: "Media", allowMultiple: false, sourceName: "Media", legacyId: "Media" },
|
||||
"omarchy.audio": { displayName: "Audio", description: "Volume slider, output picker, per-app mixer", category: "Audio", allowMultiple: false, sourceDir: "../panels", sourceName: "Audio", legacyId: "AudioPanel" },
|
||||
"omarchy.monitor": { displayName: "Display", description: "Brightness slider and laptop display controls", category: "System", allowMultiple: false, sourceDir: "../panels", sourceName: "Monitor", legacyId: "MonitorPanel" },
|
||||
"omarchy.network": { displayName: "Network", description: "Wi-Fi list and connection state", category: "Network", allowMultiple: false, sourceDir: "../panels", sourceName: "Network", legacyId: "NetworkPanel" },
|
||||
"omarchy.power": { displayName: "Power", description: "Battery, power profile, and system stats", category: "System", allowMultiple: false, sourceDir: "../panels", sourceName: "Power", legacyId: "PowerPanel" },
|
||||
"omarchy.bluetooth": { displayName: "Bluetooth", description: "Bluetooth device list with connect/disconnect", category: "Network", allowMultiple: false, sourceDir: "../panels", sourceName: "Bluetooth", legacyId: "BluetoothPanel" },
|
||||
"omarchy.clock": { displayName: "Clock", description: "Day/time label; click to toggle alternate format", category: "Time", allowMultiple: false, sourceName: "Clock", settingsForm: "clockSettings", legacyId: "Clock" },
|
||||
"omarchy.indicators": { displayName: "Indicators", description: "Manual state indicators", category: "Status", allowMultiple: true, sourceName: "Indicators", legacyId: "Indicators",
|
||||
schema: [
|
||||
{ key: "items", type: "multiselect", label: "Indicators", description: "Choose which indicators this widget instance should show. Leave empty to show all indicators.", noSelectionText: "All indicators", placeholderText: "Search indicators...", emptyText: "No indicators",
|
||||
options: [
|
||||
@@ -27,16 +27,16 @@ QtObject {
|
||||
] },
|
||||
{ key: "alwaysShow", type: "boolean", label: "Always Show", description: "Show inactive indicators without waiting for hover.", defaultValue: false }
|
||||
] },
|
||||
"NotificationCenter": { displayName: "Notification center", description: "Recent notifications + DND", category: "Status", allowMultiple: false },
|
||||
"SystemUpdate": { displayName: "System update", description: "Indicates available system updates", category: "System", allowMultiple: false },
|
||||
"SystemStats": { displayName: "System stats", description: "CPU icon — hover for graphs, click to open btop", category: "System", allowMultiple: false },
|
||||
"Tray": { displayName: "System tray", description: "Status notifier items", category: "Status", allowMultiple: false },
|
||||
"Weather": { displayName: "Weather", description: "Weather pill with detail popup", category: "Info", allowMultiple: false, settingsForm: "weatherSettings" },
|
||||
"Microphone": { displayName: "Microphone", description: "Mic input state and mute toggle", category: "Audio", allowMultiple: false },
|
||||
"ActiveWindow": { displayName: "Active window", description: "Title of the focused window", category: "Compositor", allowMultiple: false },
|
||||
"KeyboardLayout": { displayName: "Keyboard layout", description: "Current xkb layout, click cycles", category: "Compositor", allowMultiple: false },
|
||||
"LockKeys": { displayName: "Lock keys", description: "Caps / Num / Scroll lock indicators", category: "System", allowMultiple: false },
|
||||
"Spacer": { displayName: "Spacer", description: "Configurable blank space", category: "Layout", allowMultiple: true, settingsForm: "spacerSettings" }
|
||||
"omarchy.notifications": { displayName: "Notification center", description: "Recent notifications + DND", category: "Status", allowMultiple: false, sourceName: "NotificationCenter", legacyId: "NotificationCenter" },
|
||||
"omarchy.system-update": { displayName: "System update", description: "Indicates available system updates", category: "System", allowMultiple: false, sourceName: "SystemUpdate", legacyId: "SystemUpdate" },
|
||||
"omarchy.system-stats": { displayName: "System stats", description: "CPU icon — hover for graphs, click to open btop", category: "System", allowMultiple: false, sourceName: "SystemStats", legacyId: "SystemStats" },
|
||||
"omarchy.tray": { displayName: "System tray", description: "Status notifier items", category: "Status", allowMultiple: false, sourceName: "Tray", legacyId: "Tray" },
|
||||
"omarchy.weather": { displayName: "Weather", description: "Weather pill with detail popup", category: "Info", allowMultiple: false, sourceName: "Weather", settingsForm: "weatherSettings", legacyId: "Weather" },
|
||||
"omarchy.microphone": { displayName: "Microphone", description: "Mic input state and mute toggle", category: "Audio", allowMultiple: false, sourceName: "Microphone", legacyId: "Microphone" },
|
||||
"omarchy.active-window": { displayName: "Active window", description: "Title of the focused window", category: "Compositor", allowMultiple: false, sourceName: "ActiveWindow", legacyId: "ActiveWindow" },
|
||||
"omarchy.keyboard-layout": { displayName: "Keyboard layout", description: "Current xkb layout, click cycles", category: "Compositor", allowMultiple: false, sourceName: "KeyboardLayout", legacyId: "KeyboardLayout" },
|
||||
"omarchy.lock-keys": { displayName: "Lock keys", description: "Caps / Num / Scroll lock indicators", category: "System", allowMultiple: false, sourceName: "LockKeys", legacyId: "LockKeys" },
|
||||
"omarchy.spacer": { displayName: "Spacer", description: "Configurable blank space", category: "Layout", allowMultiple: true, sourceName: "Spacer", settingsForm: "spacerSettings", legacyId: "Spacer" }
|
||||
})
|
||||
|
||||
property var registeredComponents: ({})
|
||||
@@ -64,7 +64,8 @@ QtObject {
|
||||
allowMultiple: meta.allowMultiple === true,
|
||||
settingsForm: meta.settingsForm || "",
|
||||
schema: Array.isArray(meta.schema) ? meta.schema : [],
|
||||
source: "first-party"
|
||||
source: "first-party",
|
||||
legacyId: meta.legacyId || ""
|
||||
}
|
||||
var comp = Qt.createComponent(url, Component.Asynchronous)
|
||||
function finalize() {
|
||||
|
||||
+32
-31
@@ -16,7 +16,7 @@ the shell for its whole session.
|
||||
|
||||
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 module to open it; double-left-click the same empty space to toggle bar transparency.
|
||||
Launch the visual editor with `omarchy launch bar settings` / `omarchy plugin bar edit` (or run `omarchy-launch-bar-settings`) to reorder widgets, add/remove them, and tweak per-widget options without editing JSON by hand. For scriptable changes, use `omarchy plugin bar list`, `omarchy plugin bar add`, `omarchy plugin bar move`, `omarchy plugin bar remove`, and `omarchy plugin bar set`. You can also right-click empty space to the left or right of the centered clock module to open it; double-left-click the same empty space to toggle bar transparency.
|
||||
|
||||
Example `shell.json` (bar subtree only shown):
|
||||
|
||||
@@ -26,20 +26,20 @@ Example `shell.json` (bar subtree only shown):
|
||||
"bar": {
|
||||
"position": "top",
|
||||
"transparent": false,
|
||||
"centerAnchor": "Clock",
|
||||
"centerAnchor": "omarchy.clock",
|
||||
"layout": {
|
||||
"left": [
|
||||
{ "id": "Omarchy" },
|
||||
{ "id": "Spacer", "size": 12 },
|
||||
{ "id": "Workspaces" }
|
||||
{ "id": "omarchy.menu" },
|
||||
{ "id": "omarchy.spacer", "size": 12 },
|
||||
{ "id": "omarchy.workspaces" }
|
||||
],
|
||||
"center": [
|
||||
{ "id": "Media" },
|
||||
{ "id": "Clock", "format": "HH:mm" }
|
||||
{ "id": "omarchy.media" },
|
||||
{ "id": "omarchy.clock", "format": "HH:mm" }
|
||||
],
|
||||
"right": [
|
||||
{ "id": "AudioPanel" },
|
||||
{ "id": "PowerPanel" }
|
||||
{ "id": "omarchy.audio" },
|
||||
{ "id": "omarchy.power" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -54,17 +54,17 @@ Example `shell.json` (bar subtree only shown):
|
||||
|
||||
| Name | What it does | Interactions |
|
||||
|---|---|---|
|
||||
| `Omarchy` | Omarchy menu launcher | left = menu · right = terminal |
|
||||
| `Workspaces` | Hyprland workspace switcher | left = focus workspace |
|
||||
| `Clock` | Date/time label | left = alternate format · right = timezone selector |
|
||||
| `Media` | MPRIS now-playing — scrolling track + artist, cover-art popup | left = play/pause · middle = next · scroll = prev/next · right = popup |
|
||||
| `Indicators` | Manual state indicators | left = indicator action |
|
||||
| `NotificationCenter` | Bell with badge + popup with recent notifications, DND toggle | left = popup · right = toggle DND |
|
||||
| `SystemUpdate` | Available update indicator | left = update |
|
||||
| `SystemStats` | Inline CPU + memory sparklines, popup with detail | left = popup · right = terminal |
|
||||
| `Tray` | System tray | hover = reveal drawer · right on chevron = manage |
|
||||
| `Weather` | Weather icon + popup with forecast | left = popup · right = full notification |
|
||||
| `Microphone` | Mic icon + scroll volume | left = mute toggle · middle = audio panel · scroll = source volume |
|
||||
| `omarchy.menu` | Omarchy menu launcher | left = menu · right = terminal |
|
||||
| `omarchy.workspaces` | Hyprland workspace switcher | left = focus workspace |
|
||||
| `omarchy.clock` | Date/time label | left = alternate format · right = timezone selector |
|
||||
| `omarchy.media` | MPRIS now-playing — scrolling track + artist, cover-art popup | left = play/pause · middle = next · scroll = prev/next · right = popup |
|
||||
| `omarchy.indicators` | Manual state indicators | left = indicator action |
|
||||
| `omarchy.notifications` | Bell with badge + popup with recent notifications, DND toggle | left = popup · right = toggle DND |
|
||||
| `omarchy.system-update` | Available update indicator | left = update |
|
||||
| `omarchy.system-stats` | Inline CPU + memory sparklines, popup with detail | left = popup · right = terminal |
|
||||
| `omarchy.tray` | System tray | hover = reveal drawer · right on chevron = manage |
|
||||
| `omarchy.weather` | Weather icon + popup with forecast | left = popup · right = full notification |
|
||||
| `omarchy.microphone` | Mic icon + scroll volume | left = mute toggle · middle = audio panel · scroll = source volume |
|
||||
|
||||
### First-party panels (in `../panels/`)
|
||||
|
||||
@@ -76,7 +76,7 @@ Example `shell.json` (bar subtree only shown):
|
||||
| `panels.bluetooth` | Bluetooth icon + popup with device list, connect/disconnect, battery | left = popup · right = toggle radio · middle = bluetoothctl TUI |
|
||||
| `panels.monitor` | Brightness and laptop display controls | left = popup |
|
||||
|
||||
The `Indicators` widget loads individual bar indicators from `indicators/`. Omit `items` (or set it to an empty array) to show all indicators in the default order, or set `items` to a subset such as `["Dnd", "NightLight"]`. Set `alwaysShow` to `true` to keep inactive indicators visible instead of revealing them only on hover. Multiple `Indicators` instances are allowed, so different sections can show different subsets. Rich panels such as `PowerPanel`, `NetworkPanel`, and `AudioPanel` live in `../panels/` above.
|
||||
The `omarchy.indicators` widget loads individual bar indicators from `indicators/`. Omit `items` (or set it to an empty array) to show all indicators in the default order, or set `items` to a subset such as `["Dnd", "NightLight"]`. Set `alwaysShow` to `true` to keep inactive indicators visible instead of revealing them only on hover. Multiple `omarchy.indicators` instances are allowed, so different sections can show different subsets. Rich popup widgets such as `omarchy.power`, `omarchy.network`, and `omarchy.audio` live in `../panels/` above.
|
||||
|
||||
## Orientation
|
||||
|
||||
@@ -94,9 +94,9 @@ Command module:
|
||||
"bar": {
|
||||
"layout": {
|
||||
"right": [
|
||||
{ "id": "Tray" },
|
||||
{ "id": "omarchy.tray" },
|
||||
{ "id": "vpn", "type": "command", "exec": "~/.config/omarchy/bar/scripts/vpn-status", "interval": 5, "tooltip": "VPN", "onClick": "nm-connection-editor" },
|
||||
{ "id": "AudioPanel" }
|
||||
{ "id": "omarchy.audio" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ QML module:
|
||||
"layout": {
|
||||
"right": [
|
||||
{ "id": "gpu", "type": "qml" },
|
||||
{ "id": "AudioPanel" }
|
||||
{ "id": "omarchy.audio" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -169,15 +169,16 @@ Widgets receive `bar` (the shell root), `moduleName` (string), and `settings` (o
|
||||
- `bar.showTooltip(target, text)` / `bar.hideTooltip(target)` — shared tooltip popup
|
||||
- `bar.requestPopout(owner)` / `bar.releasePopout(owner)` — one-popup-at-a-time coordinator
|
||||
|
||||
First-party bar widgets live in `widgets/<Name>.qml`; first-party panels
|
||||
First-party bar widgets live in `widgets/<Name>.qml`; richer popup widgets
|
||||
live in `../panels/<Name>.qml` and expose IPC targets such as
|
||||
`panels.audio`. Bar layout ids use UpperCamelCase names such as `AudioPanel`,
|
||||
`NetworkPanel`, and so on, and are picked up by the shell's
|
||||
`BarWidgetRegistry` at startup; reference one by `id` in any layout list.
|
||||
`panels.audio`. Bar layout ids are namespaced, e.g. `omarchy.audio`,
|
||||
`omarchy.network`, and `omarchy.clock`. Legacy UpperCamelCase ids such as
|
||||
`AudioPanel` and `Clock` are still accepted as aliases, but new configs should
|
||||
use the namespaced ids.
|
||||
|
||||
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 shell setPluginEnabled`
|
||||
and `omarchy-shell shell rescanPlugins`.
|
||||
[../../README.md](../../README.md) for the manifest schema. Enable,
|
||||
rescan, and place third-party plugins with `omarchy plugin enable`,
|
||||
`omarchy plugin rescan`, and `omarchy plugin bar add`.
|
||||
|
||||
@@ -70,7 +70,8 @@ BarWidget {
|
||||
|
||||
function persistTrayState(pinned, hidden) {
|
||||
if (!root.bar || !root.bar.shell || typeof root.bar.shell.updateEntryInline !== "function") return
|
||||
root.bar.shell.updateEntryInline("Tray", { id: "Tray", pinned: pinned, hidden: hidden })
|
||||
var id = root.moduleName || "omarchy.tray"
|
||||
root.bar.shell.updateEntryInline(id, { id: id, pinned: pinned, hidden: hidden })
|
||||
}
|
||||
|
||||
function togglePin(iid) {
|
||||
|
||||
@@ -128,16 +128,16 @@ Item {
|
||||
bar: {
|
||||
position: "top",
|
||||
transparent: false,
|
||||
centerAnchor: "Clock",
|
||||
centerAnchor: "omarchy.clock",
|
||||
layout: {
|
||||
left: [{ id: "Omarchy" }, { id: "Workspaces" }],
|
||||
left: [{ id: "omarchy.menu" }, { id: "omarchy.workspaces" }],
|
||||
center: [
|
||||
{ id: "Clock", format: "dddd HH:mm", formatAlt: "dd MMMM 'W'ww yyyy", verticalFormat: "HH\n\u2014\nmm" },
|
||||
{ id: "Weather" }, { id: "Indicators" }, { id: "SystemUpdate" }
|
||||
{ id: "omarchy.clock", format: "dddd HH:mm", formatAlt: "dd MMMM 'W'ww yyyy", verticalFormat: "HH\n\u2014\nmm" },
|
||||
{ id: "omarchy.weather" }, { id: "omarchy.indicators" }, { id: "omarchy.system-update" }
|
||||
],
|
||||
right: [
|
||||
{ id: "Tray" }, { id: "BluetoothPanel" }, { id: "NetworkPanel" },
|
||||
{ id: "AudioPanel" }, { id: "MonitorPanel" }, { id: "PowerPanel" }
|
||||
{ id: "omarchy.tray" }, { id: "omarchy.bluetooth" }, { id: "omarchy.network" },
|
||||
{ id: "omarchy.audio" }, { id: "omarchy.monitor" }, { id: "omarchy.power" }
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -145,7 +145,7 @@ Item {
|
||||
})
|
||||
|
||||
property var defaultConfig: builtinShellConfig
|
||||
property var draft: ({ version: 1, bar: { position: "top", transparent: false, centerAnchor: "Clock", layout: { left: [], center: [], right: [] } }, plugins: [] })
|
||||
property var draft: ({ version: 1, bar: { position: "top", transparent: false, centerAnchor: "omarchy.clock", layout: { left: [], center: [], right: [] } }, plugins: [] })
|
||||
property int draftRevision: 0
|
||||
property bool suppressReload: false
|
||||
|
||||
@@ -179,7 +179,7 @@ Item {
|
||||
bar: {
|
||||
position: String(bar.position || "top"),
|
||||
transparent: bar.transparent === true,
|
||||
centerAnchor: String(bar.centerAnchor || ""),
|
||||
centerAnchor: Util.canonicalWidgetId(bar.centerAnchor || ""),
|
||||
layout: Util.normalizeLayout(bar.layout || {})
|
||||
},
|
||||
plugins: plugins
|
||||
@@ -320,7 +320,7 @@ Item {
|
||||
}
|
||||
|
||||
function canonicalWidgetId(id) {
|
||||
return String(id || "")
|
||||
return Util.canonicalWidgetId(id)
|
||||
}
|
||||
|
||||
function widgetMetadata(id) {
|
||||
@@ -380,7 +380,7 @@ Item {
|
||||
function widgetAllowsMultiple(id) {
|
||||
var meta = widgetMetadata(id)
|
||||
if (meta.allowMultiple === true) return true
|
||||
return String(id) === "Spacer"
|
||||
return canonicalWidgetId(id) === "omarchy.spacer"
|
||||
}
|
||||
|
||||
function catalogIds() {
|
||||
|
||||
@@ -229,11 +229,13 @@ QtObject {
|
||||
|
||||
var merged = {}
|
||||
for (var fk in firstParty) merged[fk] = firstParty[fk]
|
||||
// Third-party plugins never shadow a first-party one with the same id.
|
||||
// Third-party plugins never shadow first-party ids. The whole
|
||||
// `omarchy.*` namespace is reserved for built-ins, including bar widgets
|
||||
// registered outside the manifest-based plugin registry.
|
||||
for (var tk in thirdParty) {
|
||||
if (firstParty[tk]) {
|
||||
if (firstParty[tk] || String(tk).indexOf("omarchy.") === 0) {
|
||||
console.warn("PluginRegistry: plugin " + tk
|
||||
+ " rejected: id collides with first-party plugin")
|
||||
+ " rejected: id is reserved for first-party Omarchy plugins")
|
||||
continue
|
||||
}
|
||||
merged[tk] = thirdParty[tk]
|
||||
|
||||
+13
-13
@@ -7,51 +7,51 @@
|
||||
"bar": {
|
||||
"position": "top",
|
||||
"transparent": false,
|
||||
"centerAnchor": "Clock",
|
||||
"centerAnchor": "omarchy.clock",
|
||||
"layout": {
|
||||
"left": [
|
||||
{
|
||||
"id": "Omarchy"
|
||||
"id": "omarchy.menu"
|
||||
},
|
||||
{
|
||||
"id": "Workspaces"
|
||||
"id": "omarchy.workspaces"
|
||||
}
|
||||
],
|
||||
"center": [
|
||||
{
|
||||
"id": "Clock",
|
||||
"id": "omarchy.clock",
|
||||
"format": "dddd HH:mm",
|
||||
"formatAlt": "dd MMMM 'W'ww yyyy",
|
||||
"verticalFormat": "HH\n\u2014\nmm"
|
||||
},
|
||||
{
|
||||
"id": "Weather"
|
||||
"id": "omarchy.weather"
|
||||
},
|
||||
{
|
||||
"id": "Indicators"
|
||||
"id": "omarchy.indicators"
|
||||
},
|
||||
{
|
||||
"id": "SystemUpdate"
|
||||
"id": "omarchy.system-update"
|
||||
}
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"id": "Tray"
|
||||
"id": "omarchy.tray"
|
||||
},
|
||||
{
|
||||
"id": "BluetoothPanel"
|
||||
"id": "omarchy.bluetooth"
|
||||
},
|
||||
{
|
||||
"id": "NetworkPanel"
|
||||
"id": "omarchy.network"
|
||||
},
|
||||
{
|
||||
"id": "AudioPanel"
|
||||
"id": "omarchy.audio"
|
||||
},
|
||||
{
|
||||
"id": "MonitorPanel"
|
||||
"id": "omarchy.monitor"
|
||||
},
|
||||
{
|
||||
"id": "PowerPanel"
|
||||
"id": "omarchy.power"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+6
-6
@@ -44,11 +44,11 @@ ShellRoot {
|
||||
bar: {
|
||||
position: "top",
|
||||
transparent: false,
|
||||
centerAnchor: "Clock",
|
||||
centerAnchor: "omarchy.clock",
|
||||
layout: {
|
||||
left: [{ id: "Omarchy" }, { id: "Workspaces" }],
|
||||
center: [{ id: "Clock", format: "dddd HH:mm" }],
|
||||
right: [{ id: "AudioPanel" }]
|
||||
left: [{ id: "omarchy.menu" }, { id: "omarchy.workspaces" }],
|
||||
center: [{ id: "omarchy.clock", format: "dddd HH:mm" }],
|
||||
right: [{ id: "omarchy.audio" }]
|
||||
}
|
||||
},
|
||||
plugins: []
|
||||
@@ -264,7 +264,7 @@ ShellRoot {
|
||||
// new shellConfig in a local clone, and only persist if anything actually
|
||||
// changed so reactive bindings do not dirty shell.json unnecessarily.
|
||||
function updateEntryInline(moduleName, settings) {
|
||||
var stripped = String(moduleName)
|
||||
var stripped = Util.canonicalWidgetId(moduleName)
|
||||
var copy = JSON.parse(JSON.stringify(shellConfig || builtinShellConfig))
|
||||
if (!Util.isPlainObject(copy.bar)) copy.bar = { layout: { left: [], center: [], right: [] } }
|
||||
if (!Util.isPlainObject(copy.bar.layout)) copy.bar.layout = { left: [], center: [], right: [] }
|
||||
@@ -276,7 +276,7 @@ ShellRoot {
|
||||
for (var s = 0; s < sections.length; s++) {
|
||||
var arr = copy.bar.layout[sections[s]] || []
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] && arr[i].id === stripped) {
|
||||
if (arr[i] && Util.canonicalWidgetId(arr[i].id) === stripped) {
|
||||
var next = { id: stripped }
|
||||
for (var k in settings) if (k !== "id") next[k] = settings[k]
|
||||
if (JSON.stringify(arr[i]) !== JSON.stringify(next)) {
|
||||
|
||||
Reference in New Issue
Block a user