Move panel plugins under panels
This commit is contained in:
1 parent
e6d990e31d
commit
10898aea16
31 files changed
+45
-27
No files matched your search
@@ -29,6 +29,13 @@ shell/
|
||||
image-picker/
|
||||
menu/
|
||||
notifications/
|
||||
panels/
|
||||
audio/
|
||||
bluetooth/
|
||||
monitor/
|
||||
network/
|
||||
power/
|
||||
weather/
|
||||
services/
|
||||
battery/
|
||||
idle/
|
||||
|
||||
@@ -20,6 +20,12 @@ User-installed plugins live alongside these conceptually but on disk under
|
||||
| Reminders | `omarchy.reminders` | `overlay` | `reminders/ReminderFlow.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` |
|
||||
| Audio | `omarchy.audio` | `bar-widget` | `panels/audio/Panel.qml` |
|
||||
| Bluetooth | `omarchy.bluetooth` | `bar-widget` | `panels/bluetooth/Panel.qml` |
|
||||
| Monitor | `omarchy.monitor` | `bar-widget` | `panels/monitor/Panel.qml` |
|
||||
| Network | `omarchy.network` | `bar-widget` | `panels/network/Panel.qml` |
|
||||
| Power | `omarchy.power` | `bar-widget` | `panels/power/Panel.qml` |
|
||||
| Weather | `omarchy.weather` | `bar-widget` | `panels/weather/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` |
|
||||
@@ -28,9 +34,8 @@ User-installed plugins live alongside these conceptually but on disk under
|
||||
| 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`.
|
||||
e.g. `bar/widgets/Clock.manifest.json`. Rich popup widgets live under
|
||||
`panels/`, each with its own `manifest.json`.
|
||||
|
||||
## Bar
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ 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 simple first-party bar widgets with sibling manifests.
|
||||
- Feature plugins such as `../audio/`, `../network/`, and `../power/` provide richer popup bar widgets.
|
||||
- Feature plugins such as `../panels/audio/`, `../panels/network/`, and `../panels/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 `config/omarchy/shell.json` when the user has no file).
|
||||
- `omarchy-style-bar-position` updates only the user shell.json file.
|
||||
|
||||
@@ -167,8 +167,8 @@ Widgets receive `bar` (the shell root), `moduleName` (string), and `settings` (o
|
||||
|
||||
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
|
||||
richer popup widgets live in feature directories such as `../panels/audio/` and
|
||||
`../panels/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
|
||||
|
||||
@@ -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/audio/Panel.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/panels/audio/Panel.qml for the canonical recipe."
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -191,11 +191,11 @@ Panel {
|
||||
|
||||
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() }
|
||||
function show(): void { root.open() }
|
||||
function hide(): void { root.close() }
|
||||
function open() { root.open() }
|
||||
function close() { root.close() }
|
||||
function toggle() { root.toggle() }
|
||||
function show() { root.open() }
|
||||
function hide() { root.close() }
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -267,7 +267,8 @@ QtObject {
|
||||
if (scanning) return
|
||||
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 plugins may be grouped one level deeper, e.g. panels/audio
|
||||
// or 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.
|
||||
|
||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
run_node_test <<'JS'
|
||||
const audio = requireFromRoot('shell/plugins/audio/Model.js')
|
||||
const audio = requireFromRoot('shell/plugins/panels/audio/Model.js')
|
||||
|
||||
assert(audio.isPlaybackStream({ isStream: true, isSink: true }), 'audio detects sink-backed playback streams')
|
||||
assert(audio.isPlaybackStream({ isStream: true, type: 'Stream/Output/Audio' }), 'audio detects typed playback streams')
|
||||
|
||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
run_node_test <<'JS'
|
||||
const bluetooth = requireFromRoot('shell/plugins/bluetooth/Model.js')
|
||||
const bluetooth = requireFromRoot('shell/plugins/panels/bluetooth/Model.js')
|
||||
|
||||
assert(bluetooth.isUuidLike('0000110b-0000-1000-8000-00805f9b34fb'), 'bluetooth detects UUID-like names')
|
||||
assert(bluetooth.isAddressLike('AA:BB:CC:DD:EE:FF'), 'bluetooth detects address-like names')
|
||||
|
||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
run_node_test <<'JS'
|
||||
const monitor = requireFromRoot('shell/plugins/monitor/Model.js')
|
||||
const monitor = requireFromRoot('shell/plugins/panels/monitor/Model.js')
|
||||
|
||||
assertEqual(monitor.clampBrightness(0), 1, 'monitor clamps minimum brightness')
|
||||
assertEqual(monitor.clampBrightness(101), 100, 'monitor clamps maximum brightness')
|
||||
|
||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
run_node_test <<'JS'
|
||||
const network = requireFromRoot('shell/plugins/network/Model.js')
|
||||
const network = requireFromRoot('shell/plugins/panels/network/Model.js')
|
||||
|
||||
assertDeepEqual(
|
||||
network.parseNetworkStatus('wifi\tCafe WiFi\t78\t5200\n'),
|
||||
|
||||
@@ -59,24 +59,29 @@ function assertSafeEntryPoint(manifest, manifestPath, key, value) {
|
||||
const manifests = walk(pluginsDir)
|
||||
const manifestPaths = manifests.map(relativeFromPlugins)
|
||||
const manifestSet = new Set(manifestPaths)
|
||||
const groupedPluginRoots = new Set(['panels', 'services'])
|
||||
|
||||
assert(manifests.length > 0, 'plugin manifests are present')
|
||||
|
||||
for (const entry of fs.readdirSync(pluginsDir, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory() || entry.name === 'services') continue
|
||||
if (!entry.isDirectory() || groupedPluginRoots.has(entry.name)) continue
|
||||
check(
|
||||
manifestSet.has(`${entry.name}/manifest.json`),
|
||||
`top-level plugin ${entry.name} must have a manifest`
|
||||
)
|
||||
}
|
||||
|
||||
const serviceRoot = path.join(pluginsDir, 'services')
|
||||
for (const entry of fs.readdirSync(serviceRoot, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) continue
|
||||
check(
|
||||
manifestSet.has(`services/${entry.name}/manifest.json`),
|
||||
`service plugin ${entry.name} must have a manifest`
|
||||
)
|
||||
for (const groupName of groupedPluginRoots) {
|
||||
const groupRoot = path.join(pluginsDir, groupName)
|
||||
if (!fs.existsSync(groupRoot)) continue
|
||||
|
||||
for (const entry of fs.readdirSync(groupRoot, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) continue
|
||||
check(
|
||||
manifestSet.has(`${groupName}/${entry.name}/manifest.json`),
|
||||
`${groupName} plugin ${entry.name} must have a manifest`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (const manifestPath of manifestPaths) {
|
||||
|
||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
run_node_test <<'JS'
|
||||
const power = requireFromRoot('shell/plugins/power/Model.js')
|
||||
const power = requireFromRoot('shell/plugins/panels/power/Model.js')
|
||||
const states = { Charging: 1, Discharging: 2, FullyCharged: 3, PendingCharge: 4 }
|
||||
|
||||
assertEqual(power.selectProfileIndex(0, 1, ['balanced', 'performance']), 1, 'power advances profile selection')
|
||||
|
||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
run_node_test <<'JS'
|
||||
const weather = requireFromRoot('shell/plugins/weather/Model.js')
|
||||
const weather = requireFromRoot('shell/plugins/panels/weather/Model.js')
|
||||
|
||||
assertDeepEqual(
|
||||
weather.parseWeatherStatus('{"text":"☀","class":"sunny"}'),
|
||||
|
||||
Reference in new issue
Block a user