Make built-in widgets plugins
This commit is contained in:
@@ -60,6 +60,7 @@ individual plugins (`bar`, `image-selector`, …).
|
||||
| `summon <id> <payloadJson>` | load + open a plugin |
|
||||
| `hide <id>` | close a previously-summoned |
|
||||
| `toggle <id> <payloadJson>` | summon if closed, hide if open |
|
||||
| `call <id> <method> <arg>` | call an already-loaded plugin |
|
||||
| `rescanPlugins` | re-walk plugin dirs |
|
||||
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit |
|
||||
| `listPlugins` | JSON of every discovered plugin |
|
||||
@@ -98,8 +99,8 @@ Rules:
|
||||
bar widgets, `plugins[]` for everything else.
|
||||
2. Settings are inline on the entry. No `config:` sub-object, no
|
||||
merge layers.
|
||||
3. Built-in bar widget ids are namespaced (`omarchy.clock`, `omarchy.audio`, …);
|
||||
legacy ids such as `Clock` and `AudioPanel` are accepted as aliases.
|
||||
3. Built-in bar widget ids are namespaced (`omarchy.clock`, `omarchy.audio`, …).
|
||||
The migration rewrites older ids such as `Clock` and `AudioPanel` forward.
|
||||
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.
|
||||
|
||||
+1
-25
@@ -42,32 +42,8 @@ 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
|
||||
return String(id || "")
|
||||
}
|
||||
|
||||
// Best-effort base64 decode. Returns "" on parse failure rather than
|
||||
|
||||
+7
-3
@@ -104,8 +104,10 @@ 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 omarchy.clock local.clock --replace --with ai --prompt "Customize this clock widget"
|
||||
omarchy plugin clone # interactive source/name/tool picker
|
||||
omarchy plugin edit local.clock --with editor # edit with `omarchy launch editor`
|
||||
omarchy plugin edit local.clock --with ai # edit with `omarchy launch ai`
|
||||
```
|
||||
|
||||
First-party plugins under `shell/plugins/`
|
||||
@@ -125,6 +127,7 @@ running a separate Quickshell instance.
|
||||
| `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 |
|
||||
| `call <id> <method> <arg>` | string | call a method on an already-loaded plugin |
|
||||
| `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) |
|
||||
@@ -145,6 +148,7 @@ calls to the running shell. It does not start the shell.
|
||||
```
|
||||
omarchy-shell shell ping
|
||||
omarchy-shell shell summon omarchy.settings "{}"
|
||||
omarchy-shell shell toggle omarchy.menu '{"menu":"root"}'
|
||||
omarchy-shell shell listPlugins
|
||||
omarchy-shell shell rescanPlugins
|
||||
```
|
||||
@@ -205,8 +209,8 @@ rewrites the `bar` subtree from the current `shell-defaults.json`.
|
||||
separate per-plugin settings file, no merge layers. The fields on each
|
||||
entry are the values the plugin sees.
|
||||
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.
|
||||
`omarchy.audio`, and `omarchy.network`. The migration rewrites older ids
|
||||
like `Clock` and `AudioPanel` 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
|
||||
|
||||
+5
-4
@@ -1,10 +1,10 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
|
||||
// Base item for shell panels. Panels are not bar widgets, but the bar may host
|
||||
// or toggle them and injects the same ambient context while doing so. The base
|
||||
// owns the shared IPC-backed open/close lifecycle; panel implementations own
|
||||
// their button behavior, keyboard navigation, and content.
|
||||
// Base item for plugin popup widgets. Many first-party plugins expose a bar
|
||||
// button plus a popup from one QML entry point; this base owns the shared
|
||||
// IPC-backed open/close lifecycle while implementations own button behavior,
|
||||
// keyboard navigation, and content.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
@@ -51,4 +51,5 @@ Item {
|
||||
function toggle(): void { root.toggle() }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-15
@@ -9,21 +9,27 @@ startup; other panels, overlays, and menus are loaded on demand.
|
||||
User-installed plugins live alongside these conceptually but on disk under
|
||||
`~/.config/omarchy/plugins/<plugin-id>/` rather than in this directory.
|
||||
|
||||
| Plugin | id | kinds | entry point |
|
||||
|---------------|---------------------------|-----------|---------------------------------------|
|
||||
| Bar | `omarchy.bar` | `bar` | `bar/Bar.qml` |
|
||||
| Bar settings | `omarchy.settings` | `panel` | `settings/SettingsPanel.qml` |
|
||||
| Launcher | `omarchy.launcher` | `overlay` | `launcher/Launcher.qml` |
|
||||
| Image picker | `omarchy.image-picker` | `overlay` | `image-picker/ImagePicker.qml` |
|
||||
| Emojis | `omarchy.emojis` | `overlay` | `emojis/Emojis.qml` |
|
||||
| Clipboard mgr | `omarchy.clipboard` | `overlay` | `clipboard/Clipboard.qml` |
|
||||
| Omarchy menu | `omarchy.menu` | `menu` | `menu/Menu.qml` |
|
||||
| Notifications | `omarchy.notifications` | `service` | `notifications/Service.qml` |
|
||||
| Battery | `omarchy.battery` | `service` | `services/battery/Service.qml` |
|
||||
| Idle | `omarchy.idle` | `service` | `services/idle/Service.qml` |
|
||||
| Lock screen | `omarchy.lock` | `service` | `lock/Service.qml` |
|
||||
| OSD | `omarchy.osd` | `panel` | `osd/Osd.qml` |
|
||||
| Polkit agent | `omarchy.polkit` | `service` | `polkit/PolkitAgent.qml` |
|
||||
| Plugin | id | kinds | entry point |
|
||||
|---------------|---------------------------|-------------------------|---------------------------------------|
|
||||
| Bar | `omarchy.bar` | `bar` | `bar/Bar.qml` |
|
||||
| Bar settings | `omarchy.settings` | `panel` | `settings/SettingsPanel.qml` |
|
||||
| Launcher | `omarchy.launcher` | `overlay` | `launcher/Launcher.qml` |
|
||||
| Image picker | `omarchy.image-picker` | `overlay` | `image-picker/ImagePicker.qml` |
|
||||
| Emojis | `omarchy.emojis` | `overlay` | `emojis/Emojis.qml` |
|
||||
| Clipboard mgr | `omarchy.clipboard` | `overlay` | `clipboard/Clipboard.qml` |
|
||||
| Omarchy menu | `omarchy.menu` | `menu`, `bar-widget` | `menu/Menu.qml`, `menu/BarWidget.qml` |
|
||||
| Notifications | `omarchy.notifications` | `service`, `bar-widget` | `notifications/Service.qml`, `notifications/BarWidget.qml` |
|
||||
| Media | `omarchy.media` | `service`, `bar-widget` | `services/media/Service.qml`, `services/media/BarWidget.qml` |
|
||||
| Battery | `omarchy.battery` | `service` | `services/battery/Service.qml` |
|
||||
| Idle | `omarchy.idle` | `service` | `services/idle/Service.qml` |
|
||||
| Lock screen | `omarchy.lock` | `service` | `lock/Service.qml` |
|
||||
| OSD | `omarchy.osd` | `panel` | `osd/Osd.qml` |
|
||||
| Polkit agent | `omarchy.polkit` | `service` | `polkit/PolkitAgent.qml` |
|
||||
|
||||
First-party bar-only widgets also carry manifests next to their QML files,
|
||||
e.g. `bar/widgets/Clock.manifest.json`. Rich popup widgets live in feature
|
||||
plugin directories such as `audio/`, `network/`, and `power/`, each with its
|
||||
own `manifest.json`.
|
||||
|
||||
## Bar
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import qs.Commons
|
||||
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "AudioPanel"
|
||||
ipcTarget: "panels.audio"
|
||||
moduleName: "omarchy.audio"
|
||||
ipcTarget: "omarchy.audio"
|
||||
|
||||
readonly property var sink: Pipewire.defaultAudioSink
|
||||
readonly property var source: Pipewire.defaultAudioSource
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.audio",
|
||||
"name": "Audio",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Volume slider, output picker, per-app mixer",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Audio",
|
||||
"description": "Volume slider, output picker, per-app mixer",
|
||||
"category": "Audio",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
required property var barWidgetRegistry
|
||||
|
||||
readonly property var metadata: ({
|
||||
"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: [
|
||||
{ value: "Dnd", label: "Do not disturb", description: "Notification silencing" },
|
||||
{ value: "NightLight", label: "Night light", description: "Blue-light filter" },
|
||||
{ value: "StayAwake", label: "Stay awake", description: "Idle lock and screensaver override" },
|
||||
{ value: "ScreenRecording", label: "Screen recording", description: "GPU screen recorder status" },
|
||||
{ value: "Dictation", label: "Dictation", description: "Voice typing status" }
|
||||
] },
|
||||
{ key: "alwaysShow", type: "boolean", label: "Always Show", description: "Show inactive indicators without waiting for hover.", defaultValue: false }
|
||||
] },
|
||||
"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: ({})
|
||||
|
||||
Component.onCompleted: registerWidgets()
|
||||
|
||||
function registerWidgets() {
|
||||
var ids = Object.keys(metadata)
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i]
|
||||
if (barWidgetRegistry.has(id)) continue
|
||||
registerOne(id)
|
||||
}
|
||||
}
|
||||
|
||||
function registerOne(id) {
|
||||
var meta = metadata[id] || {}
|
||||
var sourceDir = meta.sourceDir || "widgets"
|
||||
var sourceName = meta.sourceName || id
|
||||
var url = Qt.resolvedUrl(sourceDir + "/" + sourceName + ".qml")
|
||||
var enrichedMeta = {
|
||||
displayName: meta.displayName || id,
|
||||
description: meta.description || "",
|
||||
category: meta.category || "Misc",
|
||||
allowMultiple: meta.allowMultiple === true,
|
||||
settingsForm: meta.settingsForm || "",
|
||||
schema: Array.isArray(meta.schema) ? meta.schema : [],
|
||||
source: "first-party",
|
||||
legacyId: meta.legacyId || ""
|
||||
}
|
||||
var comp = Qt.createComponent(url, Component.Asynchronous)
|
||||
function finalize() {
|
||||
if (comp.status === Component.Ready) {
|
||||
barWidgetRegistry.register(id, comp, enrichedMeta)
|
||||
var next = ({})
|
||||
for (var k in registeredComponents) next[k] = registeredComponents[k]
|
||||
next[id] = comp
|
||||
registeredComponents = next
|
||||
} else if (comp.status === Component.Error) {
|
||||
console.warn("first-party widget " + id + " failed to load: " + comp.errorString())
|
||||
}
|
||||
}
|
||||
if (comp.status === Component.Loading) comp.statusChanged.connect(finalize)
|
||||
else finalize()
|
||||
}
|
||||
}
|
||||
+17
-18
@@ -7,8 +7,8 @@ the shell for its whole session.
|
||||
|
||||
- `manifest.json` declares the plugin (`id: omarchy.bar`, `kind: bar`) 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 bar widgets.
|
||||
- `../panels/` holds first-party panels that the bar can toggle by id.
|
||||
- `widgets/` holds simple first-party bar widgets with sibling manifests.
|
||||
- Feature plugins such as `../audio/`, `../network/`, and `../power/` provide richer popup bar widgets.
|
||||
- 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.
|
||||
|
||||
@@ -66,17 +66,13 @@ Example `shell.json` (bar subtree only shown):
|
||||
| `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/`)
|
||||
| `omarchy.audio` | Volume icon + popup with master slider, output-device picker, per-app mixer | left = popup · right = mute · middle = popup · scroll = volume |
|
||||
| `omarchy.network` | Wi-Fi/Ethernet icon + popup with Wi-Fi scan, signal, connect, DNS provider selection | left = popup · right = nmtui |
|
||||
| `omarchy.power` | Battery/AC icon + popup with battery stats, power profiles, and system info | left = popup |
|
||||
| `omarchy.bluetooth` | Bluetooth icon + popup with device list, connect/disconnect, battery | left = popup · right = toggle radio · middle = bluetoothctl TUI |
|
||||
| `omarchy.monitor` | Brightness and laptop display controls | left = popup |
|
||||
|
||||
| Name | What it does | Interactions |
|
||||
|---|---|---|
|
||||
| `panels.audio` | Volume icon + popup with master slider, output-device picker, per-app mixer | left = popup · right = mute · middle = popup · scroll = volume |
|
||||
| `panels.network` | Wi-Fi/Ethernet icon + popup with Wi-Fi scan, signal, connect, DNS provider selection | left = popup · right = nmtui |
|
||||
| `panels.power` | Battery/AC icon + popup with battery stats, power profiles, and system info | left = popup |
|
||||
| `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 `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.
|
||||
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.
|
||||
|
||||
## Orientation
|
||||
|
||||
@@ -169,12 +165,15 @@ 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`; richer popup widgets
|
||||
live in `../panels/<Name>.qml` and expose IPC targets such as
|
||||
`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.
|
||||
First-party bar widgets are manifest-backed just like third-party widgets.
|
||||
Simple widgets carry sibling manifests such as `widgets/Clock.manifest.json`;
|
||||
richer popup widgets live in feature directories such as `../audio/` and
|
||||
`../network/`; and feature plugins such as `omarchy.menu`, `omarchy.media`, and
|
||||
`omarchy.notifications` declare their bar-widget entry points in their own
|
||||
`manifest.json`. Bar layout ids are namespaced, e.g. `omarchy.audio`,
|
||||
`omarchy.network`, and `omarchy.clock`. Older UpperCamelCase ids such as
|
||||
`AudioPanel` and `Clock` are migrated forward; 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`
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.active-window",
|
||||
"name": "Active window",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Title of the focused window",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "ActiveWindow.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Active window",
|
||||
"description": "Title of the focused window",
|
||||
"category": "Compositor",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "ActiveWindow"
|
||||
moduleName: "omarchy.active-window"
|
||||
|
||||
|
||||
readonly property var toplevel: ToplevelManager.activeToplevel
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.clock",
|
||||
"name": "Clock",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Day/time label; click to toggle alternate format",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "Clock.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Clock",
|
||||
"description": "Day/time label; click to toggle alternate format",
|
||||
"category": "Time",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Clock"
|
||||
moduleName: "omarchy.clock"
|
||||
|
||||
property bool alt: false
|
||||
property date displayDate: clock.date
|
||||
@@ -46,7 +46,7 @@ BarWidget {
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "Clock"
|
||||
target: "omarchy.clock"
|
||||
function refresh(): void { root.refresh() }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.indicators",
|
||||
"name": "Indicators",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Manual state indicators",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "Indicators.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Indicators",
|
||||
"description": "Manual state indicators",
|
||||
"category": "Status",
|
||||
"allowMultiple": true,
|
||||
"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": [
|
||||
{
|
||||
"value": "Dnd",
|
||||
"label": "Do not disturb",
|
||||
"description": "Notification silencing"
|
||||
},
|
||||
{
|
||||
"value": "NightLight",
|
||||
"label": "Night light",
|
||||
"description": "Blue-light filter"
|
||||
},
|
||||
{
|
||||
"value": "StayAwake",
|
||||
"label": "Stay awake",
|
||||
"description": "Idle lock and screensaver override"
|
||||
},
|
||||
{
|
||||
"value": "ScreenRecording",
|
||||
"label": "Screen recording",
|
||||
"description": "GPU screen recorder status"
|
||||
},
|
||||
{
|
||||
"value": "Dictation",
|
||||
"label": "Dictation",
|
||||
"description": "Voice typing status"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "alwaysShow",
|
||||
"type": "boolean",
|
||||
"label": "Always Show",
|
||||
"description": "Show inactive indicators without waiting for hover.",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Indicators"
|
||||
moduleName: "omarchy.indicators"
|
||||
|
||||
readonly property int indicatorSlotExtent: Style.space(22)
|
||||
readonly property var defaultIndicatorEntries: [ "Dnd", "NightLight", "StayAwake", "ScreenRecording", "Dictation" ]
|
||||
@@ -160,7 +160,7 @@ BarWidget {
|
||||
implicitHeight: root.vertical ? verticalIndicators.implicitHeight : horizontalIndicators.implicitHeight
|
||||
|
||||
IpcHandler {
|
||||
target: "Indicators"
|
||||
target: "omarchy.indicators"
|
||||
|
||||
function refresh(): void {
|
||||
root.refreshRequested()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.keyboard-layout",
|
||||
"name": "Keyboard layout",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Current xkb layout, click cycles",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "KeyboardLayout.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Keyboard layout",
|
||||
"description": "Current xkb layout, click cycles",
|
||||
"category": "Compositor",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import qs.Commons
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "KeyboardLayout"
|
||||
moduleName: "omarchy.keyboard-layout"
|
||||
|
||||
|
||||
property string layoutLabel: ""
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.lock-keys",
|
||||
"name": "Lock keys",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Caps / Num / Scroll lock indicators",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "LockKeys.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Lock keys",
|
||||
"description": "Caps / Num / Scroll lock indicators",
|
||||
"category": "System",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "LockKeys"
|
||||
moduleName: "omarchy.lock-keys"
|
||||
|
||||
|
||||
property bool capsOn: false
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.microphone",
|
||||
"name": "Microphone",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Mic input state and mute toggle",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "Microphone.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Microphone",
|
||||
"description": "Mic input state and mute toggle",
|
||||
"category": "Audio",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Microphone"
|
||||
moduleName: "omarchy.microphone"
|
||||
|
||||
|
||||
readonly property var source: Pipewire.defaultAudioSource
|
||||
@@ -42,7 +42,7 @@ BarWidget {
|
||||
active: root.inUse
|
||||
tooltipText: root.muted ? "Microphone muted" : (root.inUse ? "Microphone in use" : "Microphone live")
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.MiddleButton) root.bar.run("omarchy-shell panels.audio toggle")
|
||||
if (b === Qt.MiddleButton) root.bar.run("omarchy-shell omarchy.audio toggle")
|
||||
else root.toggleMute()
|
||||
}
|
||||
onWheelMoved: function(delta) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.spacer",
|
||||
"name": "Spacer",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Configurable blank space",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "Spacer.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Spacer",
|
||||
"description": "Configurable blank space",
|
||||
"category": "Layout",
|
||||
"allowMultiple": true,
|
||||
"settingsForm": "spacerSettings"
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Spacer"
|
||||
moduleName: "omarchy.spacer"
|
||||
|
||||
readonly property int span: settings && settings.size !== undefined ? Number(settings.size) : 12
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.system-stats",
|
||||
"name": "System stats",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "CPU icon \u2014 hover for graphs, click to open btop",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "SystemStats.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "System stats",
|
||||
"description": "CPU icon \u2014 hover for graphs, click to open btop",
|
||||
"category": "System",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Commons
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "SystemStats"
|
||||
moduleName: "omarchy.system-stats"
|
||||
|
||||
|
||||
property real cpuPercent: 0
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.system-update",
|
||||
"name": "System update",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Indicates available system updates",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "SystemUpdate.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "System update",
|
||||
"description": "Indicates available system updates",
|
||||
"category": "System",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "SystemUpdate"
|
||||
moduleName: "omarchy.system-update"
|
||||
|
||||
property bool updateAvailable: false
|
||||
|
||||
@@ -18,7 +18,7 @@ BarWidget {
|
||||
implicitHeight: button.implicitHeight
|
||||
|
||||
IpcHandler {
|
||||
target: "SystemUpdate"
|
||||
target: "omarchy.system-update"
|
||||
|
||||
function refresh(): void {
|
||||
root.refresh()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.tray",
|
||||
"name": "System tray",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Status notifier items",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "Tray.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "System tray",
|
||||
"description": "Status notifier items",
|
||||
"category": "Status",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Tray"
|
||||
moduleName: "omarchy.tray"
|
||||
|
||||
property bool expanded: false
|
||||
property bool managePopupOpen: false
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.workspaces",
|
||||
"name": "Workspaces",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Workspace number indicators",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "Workspaces.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Workspaces",
|
||||
"description": "Workspace number indicators",
|
||||
"category": "Compositor",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Workspaces"
|
||||
moduleName: "omarchy.workspaces"
|
||||
|
||||
function workspaceById(id) {
|
||||
var values = Hyprland.workspaces.values
|
||||
|
||||
@@ -8,8 +8,8 @@ import qs.Commons
|
||||
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "BluetoothPanel"
|
||||
ipcTarget: "panels.bluetooth"
|
||||
moduleName: "omarchy.bluetooth"
|
||||
ipcTarget: "omarchy.bluetooth"
|
||||
|
||||
// Address -> "connecting" | "disconnecting" | "forgetting".
|
||||
// The actual Bluetooth sequencing lives in bin/omarchy-bluetooth-device;
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.bluetooth",
|
||||
"name": "Bluetooth",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Bluetooth device list with connect/disconnect",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Bluetooth",
|
||||
"description": "Bluetooth device list with connect/disconnect",
|
||||
"category": "Network",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -407,7 +407,7 @@ Item {
|
||||
color: Qt.darker(root.foreground, 1.4)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns cursorActive + focusSection + selectedIndex; each element binds hasCursor: root.cursorActive && root.focusSection === 'X' && root.selectedIndex === N, and onHovered flips cursorActive on while updating the same state. No initial highlight, then one highlight on screen once the keyboard or mouse enters. See plugins/panels/Audio.qml for the canonical recipe."
|
||||
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns cursorActive + focusSection + selectedIndex; each element binds hasCursor: root.cursorActive && root.focusSection === 'X' && root.selectedIndex === N, and onHovered flips cursorActive on while updating the same state. No initial highlight, then one highlight on screen once the keyboard or mouse enters. See plugins/audio/BarWidget.qml for the canonical recipe."
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
@@ -1524,7 +1524,7 @@ Item {
|
||||
width: Style.spacing.dropdownWidth
|
||||
label: "Center anchor"
|
||||
fontFamily: root.fontFamily
|
||||
options: ["Clock", "Weather", "PowerPanel"]
|
||||
options: ["omarchy.clock", "omarchy.weather", "omarchy.power"]
|
||||
value: root.dropdownDemoValue
|
||||
hasCursor: root.focusSection === "dropdown" && root.selectedIndex === 0
|
||||
onHovered: function(h) {
|
||||
@@ -1589,7 +1589,7 @@ Item {
|
||||
options: [
|
||||
{ value: "Clock", label: "Clock", description: "Time + date display" },
|
||||
{ value: "Weather", label: "Weather", description: "Local conditions and forecast" },
|
||||
{ value: "PowerPanel", label: "Power", description: "Charge level + power profile" },
|
||||
{ value: "omarchy.power", label: "Power", description: "Charge level + power profile" },
|
||||
{ value: "audio", label: "Audio", description: "Output sink + volume" },
|
||||
{ value: "network", label: "Network", description: "Wi-Fi + ethernet status" },
|
||||
{ value: "bluetooth", label: "Bluetooth", description: "Paired and nearby devices" },
|
||||
|
||||
@@ -3,7 +3,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Omarchy"
|
||||
moduleName: "omarchy.menu"
|
||||
|
||||
implicitWidth: button.implicitWidth
|
||||
implicitHeight: button.implicitHeight
|
||||
@@ -18,7 +18,7 @@ BarWidget {
|
||||
onPressed: function(button) {
|
||||
if (!root.bar) return
|
||||
if (button === Qt.RightButton) root.bar.run("xdg-terminal-exec")
|
||||
else root.bar.run("omarchy-shell menu toggle root")
|
||||
else root.bar.run("omarchy-shell shell toggle omarchy.menu '{\"menu\":\"root\"}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,18 @@
|
||||
"author": "Omarchy",
|
||||
"description": "Quickshell-powered Omarchy command menu",
|
||||
"kinds": [
|
||||
"menu"
|
||||
"menu",
|
||||
"bar-widget"
|
||||
],
|
||||
"keepLoaded": true,
|
||||
"entryPoints": {
|
||||
"menu": "Menu.qml"
|
||||
"menu": "Menu.qml",
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Omarchy menu",
|
||||
"description": "Launches the Omarchy menu",
|
||||
"category": "Compositor",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import qs.Commons
|
||||
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "MonitorPanel"
|
||||
ipcTarget: "panels.monitor"
|
||||
moduleName: "omarchy.monitor"
|
||||
ipcTarget: "omarchy.monitor"
|
||||
manageIpc: false
|
||||
|
||||
// manageIpc: false so this panel can own the single IpcHandler the target
|
||||
@@ -169,25 +169,27 @@ Panel {
|
||||
flick.contentY = bottom + margin - flick.height
|
||||
}
|
||||
|
||||
function brightnessIpc(percent) {
|
||||
var value = Number(percent)
|
||||
root.setBrightness(value)
|
||||
return "got " + root.pendingBrightnessPercent
|
||||
}
|
||||
|
||||
function stateIpc() {
|
||||
return JSON.stringify({
|
||||
brightness: root.brightnessPercent,
|
||||
brightnessAvailable: root.brightnessAvailable,
|
||||
focusedMonitor: root.focusedMonitor,
|
||||
scale: root.monitorScale,
|
||||
displays: root.displays
|
||||
})
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "panels.monitor"
|
||||
|
||||
function brightness(percent: string): string {
|
||||
var value = Number(percent)
|
||||
root.setBrightness(value)
|
||||
return "got " + root.pendingBrightnessPercent
|
||||
}
|
||||
|
||||
function state(): string {
|
||||
return JSON.stringify({
|
||||
brightness: root.brightnessPercent,
|
||||
brightnessAvailable: root.brightnessAvailable,
|
||||
focusedMonitor: root.focusedMonitor,
|
||||
scale: root.monitorScale,
|
||||
displays: root.displays
|
||||
})
|
||||
}
|
||||
target: "omarchy.monitor"
|
||||
|
||||
function brightness(percent: string): string { return root.brightnessIpc(percent) }
|
||||
function state(): string { return root.stateIpc() }
|
||||
function open(): void { root.open() }
|
||||
function close(): void { root.close() }
|
||||
function toggle(): void { root.toggle() }
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.monitor",
|
||||
"name": "Display",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Brightness slider and laptop display controls",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Display",
|
||||
"description": "Brightness slider and laptop display controls",
|
||||
"category": "System",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import qs.Commons
|
||||
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "NetworkPanel"
|
||||
ipcTarget: "panels.network"
|
||||
moduleName: "omarchy.network"
|
||||
ipcTarget: "omarchy.network"
|
||||
|
||||
// Centralized close so callers can't forget to drop the passphrase prompt.
|
||||
function close() {
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.network",
|
||||
"name": "Network",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Wi-Fi list and connection state",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Network",
|
||||
"description": "Wi-Fi list and connection state",
|
||||
"category": "Network",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "NotificationCenter"
|
||||
moduleName: "omarchy.notifications"
|
||||
|
||||
|
||||
property bool popupOpen: false
|
||||
@@ -6,10 +6,18 @@
|
||||
"author": "Omarchy",
|
||||
"description": "Notification daemon, popups, and history",
|
||||
"kinds": [
|
||||
"service"
|
||||
"service",
|
||||
"bar-widget"
|
||||
],
|
||||
"keepLoaded": true,
|
||||
"entryPoints": {
|
||||
"service": "Service.qml"
|
||||
"service": "Service.qml",
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Notification center",
|
||||
"description": "Recent notifications + DND",
|
||||
"category": "Status",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import qs.Ui
|
||||
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "PowerPanel"
|
||||
ipcTarget: "panels.power"
|
||||
moduleName: "omarchy.power"
|
||||
ipcTarget: "omarchy.power"
|
||||
property var batteryInfo: ({})
|
||||
property var systemInfo: ({})
|
||||
property var profiles: []
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.power",
|
||||
"name": "Power",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Battery, power profile, and system stats",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Power",
|
||||
"description": "Battery, power profile, and system stats",
|
||||
"category": "System",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import qs.Commons
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Media"
|
||||
moduleName: "omarchy.media"
|
||||
|
||||
readonly property var mediaService: bar && bar.shell ? bar.shell.firstPartyServiceFor("omarchy.media") : null
|
||||
readonly property var activePlayer: mediaService ? mediaService.activePlayer : null
|
||||
@@ -6,10 +6,18 @@
|
||||
"author": "Omarchy",
|
||||
"description": "MPRIS media control service",
|
||||
"kinds": [
|
||||
"service"
|
||||
"service",
|
||||
"bar-widget"
|
||||
],
|
||||
"keepLoaded": true,
|
||||
"entryPoints": {
|
||||
"service": "Service.qml"
|
||||
"service": "Service.qml",
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Media",
|
||||
"description": "MPRIS now-playing with playback controls",
|
||||
"category": "Media",
|
||||
"allowMultiple": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "Weather"
|
||||
moduleName: "omarchy.weather"
|
||||
|
||||
function injectPanel() {
|
||||
var target = panelLoader.item
|
||||
@@ -32,7 +32,7 @@ BarWidget {
|
||||
Loader {
|
||||
id: panelLoader
|
||||
active: true
|
||||
source: Qt.resolvedUrl("../../panels/Weather.qml")
|
||||
source: Qt.resolvedUrl("Panel.qml")
|
||||
visible: false
|
||||
onLoaded: {
|
||||
root.injectPanel()
|
||||
@@ -6,8 +6,8 @@ import qs.Ui
|
||||
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "WeatherPanel"
|
||||
ipcTarget: "Weather"
|
||||
moduleName: "omarchy.weather"
|
||||
ipcTarget: "omarchy.weather"
|
||||
|
||||
property var anchorItem: null
|
||||
|
||||
@@ -21,12 +21,6 @@ Panel {
|
||||
else root.open()
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "Weather"
|
||||
function show(): void { root.open() }
|
||||
function toggle(): void { root.toggle() }
|
||||
}
|
||||
|
||||
// Parsed wttr.in j1 response. Kept on failure so stale data stays visible.
|
||||
property var report: null
|
||||
property var dailyForecastReport: null
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "omarchy.weather",
|
||||
"name": "Weather",
|
||||
"version": "1.0.0",
|
||||
"author": "Omarchy",
|
||||
"description": "Weather pill with detail popup",
|
||||
"kinds": [
|
||||
"bar-widget"
|
||||
],
|
||||
"entryPoints": {
|
||||
"barWidget": "BarWidget.qml"
|
||||
},
|
||||
"barWidget": {
|
||||
"displayName": "Weather",
|
||||
"description": "Weather pill with detail popup",
|
||||
"category": "Info",
|
||||
"allowMultiple": false,
|
||||
"settingsForm": "weatherSettings"
|
||||
}
|
||||
}
|
||||
@@ -119,19 +119,20 @@ QtObject {
|
||||
|
||||
function findEntryLocation(config, id) {
|
||||
if (!Util.isPlainObject(config)) return { found: false }
|
||||
var key = Util.canonicalWidgetId(String(id))
|
||||
if (Util.isPlainObject(config.bar) && Util.isPlainObject(config.bar.layout)) {
|
||||
var sections = ["left", "center", "right"]
|
||||
for (var s = 0; s < sections.length; s++) {
|
||||
var arr = config.bar.layout[sections[s]]
|
||||
if (!Array.isArray(arr)) continue
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] && arr[i].id === id) return { found: true, kind: "bar", section: sections[s], index: i }
|
||||
if (arr[i] && Util.canonicalWidgetId(arr[i].id) === key) return { found: true, kind: "bar", section: sections[s], index: i }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(config.plugins)) {
|
||||
for (var j = 0; j < config.plugins.length; j++) {
|
||||
if (config.plugins[j] && config.plugins[j].id === id) return { found: true, kind: "plugin", index: j }
|
||||
if (config.plugins[j] && Util.canonicalWidgetId(config.plugins[j].id) === key) return { found: true, kind: "plugin", index: j }
|
||||
}
|
||||
}
|
||||
return { found: false }
|
||||
@@ -141,7 +142,7 @@ QtObject {
|
||||
// kinds. Bar widgets default to the right section; panels/overlays/menus/
|
||||
// services go into the plugins[] array.
|
||||
function setEnabled(id, value) {
|
||||
var key = String(id)
|
||||
var key = Util.canonicalWidgetId(String(id))
|
||||
if (!shellConfigMutator) {
|
||||
console.warn("PluginRegistry.setEnabled called before shellConfigMutator wired")
|
||||
return
|
||||
@@ -267,16 +268,20 @@ QtObject {
|
||||
scanning = true
|
||||
// $0 = first-party dir, $1 = third-party dir. Some bash versions need the explicit -- separator.
|
||||
// First-party plugins may be grouped one level deeper, e.g. services/battery.
|
||||
// First-party bar widgets can also carry sibling manifests such as
|
||||
// widgets/Clock.manifest.json so multiple widgets can live in one source
|
||||
// directory without wrapper folders.
|
||||
// Third-party plugins stay at the top level of ~/.config/omarchy/plugins.
|
||||
var script = ""
|
||||
+ "emit_manifest() { local kind=\"$1\"; local manifest=\"$2\"; local sub=\"${manifest%/manifest.json}\"; "
|
||||
+ "emit_manifest() { local kind=\"$1\"; local manifest=\"$2\"; local sub; "
|
||||
+ " if [[ ${manifest##*/} == \"manifest.json\" ]]; then sub=\"${manifest%/manifest.json}\"; else sub=\"$(dirname -- \"$manifest\")\"; fi; "
|
||||
+ " printf '===%s::%s===\\n' \"$kind\" \"$sub\"; "
|
||||
+ " cat \"$manifest\"; "
|
||||
+ " printf '\\n=== EOM ===\\n'; "
|
||||
+ "}; "
|
||||
+ "scan_firstparty() { local dir=\"$1\"; "
|
||||
+ " [[ -d \"$dir\" ]] || return 0; "
|
||||
+ " while IFS= read -r manifest; do emit_manifest firstparty \"$manifest\"; done < <(find \"$dir\" -mindepth 2 -maxdepth 3 -name manifest.json -type f | sort); "
|
||||
+ " while IFS= read -r manifest; do emit_manifest firstparty \"$manifest\"; done < <(find \"$dir\" -mindepth 2 -maxdepth 3 -type f \\( -name manifest.json -o -name '*.manifest.json' \\) | sort); "
|
||||
+ "}; "
|
||||
+ "scan_thirdparty() { local dir=\"$1\"; "
|
||||
+ " [[ -d \"$dir\" ]] || return 0; "
|
||||
|
||||
+29
-8
@@ -17,9 +17,6 @@ ShellRoot {
|
||||
// own empty copies.
|
||||
property PluginRegistry pluginRegistry: PluginRegistry { }
|
||||
property BarWidgetRegistry barWidgetRegistry: BarWidgetRegistry { }
|
||||
property FirstPartyWidgets firstPartyWidgets: FirstPartyWidgets {
|
||||
barWidgetRegistry: shell.barWidgetRegistry
|
||||
}
|
||||
|
||||
property string home: Quickshell.env("HOME")
|
||||
|
||||
@@ -362,9 +359,17 @@ ShellRoot {
|
||||
return true
|
||||
}
|
||||
|
||||
function isPluginOpen(pluginId) {
|
||||
var id = String(pluginId || "")
|
||||
var loader = panelLoaders[id]
|
||||
if (loader && loader.item && loader.item.opened !== undefined)
|
||||
return loader.item.opened === true
|
||||
return openPanelIds[id] === true
|
||||
}
|
||||
|
||||
function toggle(pluginId, payloadJson) {
|
||||
var id = String(pluginId || "")
|
||||
return openPanelIds[id] ? hide(id) : summon(id, payloadJson)
|
||||
return isPluginOpen(id) ? hide(id) : summon(id, payloadJson)
|
||||
}
|
||||
|
||||
// Map of pluginId -> Loader, populated by the Instantiator delegate below.
|
||||
@@ -411,6 +416,19 @@ ShellRoot {
|
||||
}
|
||||
}
|
||||
|
||||
function callIfLoaded(pluginId, method, arg) {
|
||||
var loader = panelLoaders[pluginId]
|
||||
if (!loader || !loader.item) return "unknown"
|
||||
if (typeof loader.item[method] !== "function") return "unknown"
|
||||
try {
|
||||
var result = loader.item[method](arg)
|
||||
return result === undefined || result === null ? "ok" : String(result)
|
||||
} catch (e) {
|
||||
console.warn("plugin " + pluginId + " " + method + "() threw:", e)
|
||||
return "error"
|
||||
}
|
||||
}
|
||||
|
||||
// One Loader per discoverable panel/overlay/menu plugin. Active when the
|
||||
// host marks it open. The Loader holds onto the instance while active so the
|
||||
// plugin's FloatingWindow + state survive between summons within a session.
|
||||
@@ -490,10 +508,8 @@ ShellRoot {
|
||||
|
||||
// Mirror plugin registry state into BarWidgetRegistry whenever it changes.
|
||||
// Each enabled plugin with kind "bar-widget" gets a Component created from
|
||||
// its manifest entry point and registered under its plain manifest id.
|
||||
// First-party widget ids (clock, weather, etc.) are short and don't
|
||||
// collide with namespaced plugin ids, so we don't need a separate
|
||||
// "plugin:" namespace anymore.
|
||||
// its manifest entry point and registered under its manifest id. Built-in
|
||||
// widgets use the same first-party manifest contract as third-party widgets.
|
||||
Connections {
|
||||
target: shell.pluginRegistry
|
||||
function onPluginsChanged() { shell.syncPluginWidgets() }
|
||||
@@ -527,6 +543,7 @@ ShellRoot {
|
||||
category: meta.category || "Plugin",
|
||||
allowMultiple: meta.allowMultiple === true,
|
||||
defaults: meta.defaults || {},
|
||||
settingsForm: meta.settingsForm || "",
|
||||
schema: meta.schema || [],
|
||||
pluginId: manifest.id,
|
||||
sourceDir: manifest.__sourceDir || "",
|
||||
@@ -641,5 +658,9 @@ ShellRoot {
|
||||
function toggle(id: string, payloadJson: string): void {
|
||||
shell.toggle(id, payloadJson)
|
||||
}
|
||||
|
||||
function call(id: string, method: string, arg: string): string {
|
||||
return shell.callIfLoaded(id, method, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user