Merge remote-tracking branch 'origin/omarchy-shell' into omarchy-4
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Mutate the user shell.json bar layout
|
# omarchy:summary=Mutate the user shell.json bar layout and active bar option
|
||||||
# omarchy:args=show | list [--json] [--all] | add [plugin] [left|center|right] | remove [plugin] | drop [plugin] | position <top|bottom|left|right> | transparent <true|false>
|
# omarchy:args=show | options [--json] | use <plugin> | reset | list [--json] [--all] | add [plugin] [left|center|right] | remove [plugin] | drop [plugin] | position <top|bottom|left|right> | transparent <true|false>
|
||||||
# omarchy:examples=omarchy config shell bar show | omarchy config shell bar list | omarchy config shell bar add | omarchy config shell bar remove | omarchy config shell bar add omarchy.tailscale | omarchy config shell bar position top | omarchy config shell bar transparent true
|
# omarchy:examples=omarchy config shell bar show | omarchy config shell bar options | omarchy config shell bar use local.neon-bar | omarchy config shell bar reset | omarchy config shell bar list | omarchy config shell bar add omarchy.tailscale | omarchy config shell bar position top | omarchy config shell bar transparent true
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ OMARCHY_ROOT="${OMARCHY_PATH:-}"
|
|||||||
DEFAULTS_FILE="$OMARCHY_ROOT/config/omarchy/shell.json"
|
DEFAULTS_FILE="$OMARCHY_ROOT/config/omarchy/shell.json"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage: omarchy-config-shell-bar show | list [--json] [--all] | add [plugin] [left|center|right] | remove [plugin] | drop [plugin] | position <top|bottom|left|right> | transparent <true|false>" >&2
|
echo "Usage: omarchy-config-shell-bar show | options [--json] | use <plugin> | reset | list [--json] [--all] | add [plugin] [left|center|right] | remove [plugin] | drop [plugin] | position <top|bottom|left|right> | transparent <true|false>" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
@@ -49,6 +49,95 @@ bar_widget_manifest_paths() {
|
|||||||
} | sort -u
|
} | sort -u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bar_option_manifest_paths() {
|
||||||
|
{
|
||||||
|
if [[ -n $OMARCHY_ROOT && -d $OMARCHY_ROOT/shell/plugins ]]; then
|
||||||
|
find "$OMARCHY_ROOT/shell/plugins" -mindepth 2 -maxdepth 4 -type f \( -name manifest.json -o -name '*.manifest.json' \) 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d $HOME/.config/omarchy/plugins ]]; then
|
||||||
|
find -L "$HOME/.config/omarchy/plugins" -mindepth 2 -maxdepth 2 -type f -name manifest.json 2>/dev/null
|
||||||
|
fi
|
||||||
|
} | sort -u
|
||||||
|
}
|
||||||
|
|
||||||
|
current_bar_option_id() {
|
||||||
|
jq -r '(.bar.id // "omarchy.bar") | tostring' "$(source_file)"
|
||||||
|
}
|
||||||
|
|
||||||
|
available_bar_options_json() {
|
||||||
|
local current_id
|
||||||
|
local manifest_paths=()
|
||||||
|
|
||||||
|
current_id=$(current_bar_option_id)
|
||||||
|
mapfile -t manifest_paths < <(bar_option_manifest_paths)
|
||||||
|
|
||||||
|
if (( ${#manifest_paths[@]} == 0 )); then
|
||||||
|
printf '[]\n'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
jq -s --arg currentId "$current_id" '
|
||||||
|
map(
|
||||||
|
select(((.kinds // []) | index("bar")) and ((.entryPoints.bar // "") != "")) |
|
||||||
|
{
|
||||||
|
id: (.id // ""),
|
||||||
|
name: (.name // .id // ""),
|
||||||
|
description: (.description // ""),
|
||||||
|
firstParty: ((.id // "") | startswith("omarchy.")),
|
||||||
|
active: ((.id // "") == $currentId)
|
||||||
|
} |
|
||||||
|
select(.id != "")
|
||||||
|
) |
|
||||||
|
unique_by(.id) |
|
||||||
|
sort_by((if .id == "omarchy.bar" then 0 else 1 end), .name, .id)
|
||||||
|
' "${manifest_paths[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_bar_options() {
|
||||||
|
local json="false"
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
--json)
|
||||||
|
json="true"
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
fail "unknown options option: $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $json == "true" ]]; then
|
||||||
|
available_bar_options_json
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
available_bar_options_json | jq -r '
|
||||||
|
.[] |
|
||||||
|
[
|
||||||
|
.id,
|
||||||
|
(if .active then "active" else "available" end),
|
||||||
|
(if .firstParty then "built-in" else "plugin" end),
|
||||||
|
.name,
|
||||||
|
.description
|
||||||
|
] | @tsv
|
||||||
|
' | awk -F '\t' '
|
||||||
|
BEGIN { printf "%-32s %-10s %-8s %-22s %s\n", "ID", "STATUS", "SOURCE", "NAME", "DESCRIPTION" }
|
||||||
|
{ printf "%-32s %-10s %-8s %-22s %s\n", $1, $2, $3, $4, $5 }
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
bar_option_exists() {
|
||||||
|
local id="$1"
|
||||||
|
available_bar_options_json | jq -e --arg id "$id" 'any(.[]; .id == $id)' >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
current_bar_ids_json() {
|
current_bar_ids_json() {
|
||||||
jq -c '
|
jq -c '
|
||||||
def array_or_empty: if type == "array" then . else [] end;
|
def array_or_empty: if type == "array" then . else [] end;
|
||||||
@@ -256,7 +345,88 @@ case "$command" in
|
|||||||
|
|
||||||
jq '.bar // {}' "$(source_file)"
|
jq '.bar // {}' "$(source_file)"
|
||||||
;;
|
;;
|
||||||
list|available|options)
|
selected|active)
|
||||||
|
if (( $# != 1 )); then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_bar_option_id
|
||||||
|
;;
|
||||||
|
options|option)
|
||||||
|
shift
|
||||||
|
print_bar_options "$@"
|
||||||
|
;;
|
||||||
|
use)
|
||||||
|
if (( $# != 2 )); then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
plugin="${2:-}"
|
||||||
|
[[ -n $plugin ]] || fail "bar option id is required"
|
||||||
|
if [[ $plugin == "default" || $plugin == "built-in" ]]; then
|
||||||
|
plugin="omarchy.bar"
|
||||||
|
fi
|
||||||
|
bar_option_exists "$plugin" || fail "$plugin is not a known bar option; run 'omarchy config shell bar options'"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$CONFIG_FILE")"
|
||||||
|
|
||||||
|
source="$(source_file)"
|
||||||
|
tmp=$(mktemp)
|
||||||
|
trap 'rm -f "$tmp"' EXIT
|
||||||
|
|
||||||
|
if [[ $plugin == "omarchy.bar" ]]; then
|
||||||
|
jq '
|
||||||
|
def object_or_empty: if type == "object" then . else {} end;
|
||||||
|
|
||||||
|
object_or_empty
|
||||||
|
| .version = 1
|
||||||
|
| .bar = (.bar | object_or_empty)
|
||||||
|
| del(.bar.id)
|
||||||
|
' "$source" >"$tmp"
|
||||||
|
else
|
||||||
|
jq --arg plugin "$plugin" '
|
||||||
|
def object_or_empty: if type == "object" then . else {} end;
|
||||||
|
|
||||||
|
object_or_empty
|
||||||
|
| .version = 1
|
||||||
|
| .bar = (.bar | object_or_empty)
|
||||||
|
| .bar.id = $plugin
|
||||||
|
' "$source" >"$tmp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "$tmp" "$CONFIG_FILE"
|
||||||
|
trap - EXIT
|
||||||
|
omarchy-shell -q shell rescanPlugins >/dev/null 2>&1 || true
|
||||||
|
refresh_shell_config
|
||||||
|
;;
|
||||||
|
reset)
|
||||||
|
if (( $# != 1 )); then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$CONFIG_FILE")"
|
||||||
|
|
||||||
|
source="$(source_file)"
|
||||||
|
tmp=$(mktemp)
|
||||||
|
trap 'rm -f "$tmp"' EXIT
|
||||||
|
|
||||||
|
jq '
|
||||||
|
def object_or_empty: if type == "object" then . else {} end;
|
||||||
|
|
||||||
|
object_or_empty
|
||||||
|
| .version = 1
|
||||||
|
| .bar = (.bar | object_or_empty)
|
||||||
|
| del(.bar.id)
|
||||||
|
' "$source" >"$tmp"
|
||||||
|
|
||||||
|
mv "$tmp" "$CONFIG_FILE"
|
||||||
|
trap - EXIT
|
||||||
|
refresh_shell_config
|
||||||
|
;;
|
||||||
|
list|available|widgets)
|
||||||
shift
|
shift
|
||||||
print_available_widgets "$@"
|
print_available_widgets "$@"
|
||||||
;;
|
;;
|
||||||
|
|||||||
+25
-1
@@ -41,9 +41,13 @@ Clone options:
|
|||||||
--prompt <prompt> Initial prompt when opening with AI
|
--prompt <prompt> Initial prompt when opening with AI
|
||||||
--replace Replace the first source bar instance
|
--replace Replace the first source bar instance
|
||||||
--add [placement] Add cloned bar widget to the bar
|
--add [placement] Add cloned bar widget to the bar
|
||||||
|
--use Use cloned bar options as the active bar
|
||||||
|
|
||||||
Bar widget commands:
|
Bar widget commands:
|
||||||
bar settings Open the inline bar config panel
|
bar settings Open the inline bar config panel
|
||||||
|
bar options [--json] List available bar options
|
||||||
|
bar use <id> Use a bar option as the active bar
|
||||||
|
bar reset Return to the built-in Omarchy bar
|
||||||
bar list [--json] List current bar layout
|
bar list [--json] List current bar layout
|
||||||
bar add <id> [placement] [--duplicate]
|
bar add <id> [placement] [--duplicate]
|
||||||
bar move <id> [placement] [--from-section SECTION] [--from-index N]
|
bar move <id> [placement] [--from-section SECTION] [--from-index N]
|
||||||
@@ -65,6 +69,9 @@ Examples:
|
|||||||
omarchy plugin edit local.clock --with editor
|
omarchy plugin edit local.clock --with editor
|
||||||
omarchy plugin edit local.clock --with ai --prompt "Customize this clock"
|
omarchy plugin edit local.clock --with ai --prompt "Customize this clock"
|
||||||
omarchy plugin enable acme.weather --section right
|
omarchy plugin enable acme.weather --section right
|
||||||
|
omarchy plugin bar options
|
||||||
|
omarchy plugin bar use local.neon-bar
|
||||||
|
omarchy plugin bar reset
|
||||||
omarchy plugin bar add acme.weather --section right --before omarchy.tray
|
omarchy plugin bar add acme.weather --section right --before omarchy.tray
|
||||||
omarchy plugin bar move omarchy.clock --section center --index 0
|
omarchy plugin bar move omarchy.clock --section center --index 0
|
||||||
omarchy plugin bar remove acme.weather
|
omarchy plugin bar remove acme.weather
|
||||||
@@ -705,7 +712,7 @@ clone_source_options() {
|
|||||||
first_party_manifest_paths | while IFS= read -r manifest; do
|
first_party_manifest_paths | while IFS= read -r manifest; do
|
||||||
id=$(jq -r '.id // empty' "$manifest" 2>/dev/null || true)
|
id=$(jq -r '.id // empty' "$manifest" 2>/dev/null || true)
|
||||||
name=$(jq -r '.barWidget.displayName // .name // .id // empty' "$manifest" 2>/dev/null || true)
|
name=$(jq -r '.barWidget.displayName // .name // .id // empty' "$manifest" 2>/dev/null || true)
|
||||||
[[ -n $id && $id != "omarchy.bar" ]] || continue
|
[[ -n $id ]] || continue
|
||||||
if jq -e '(.kinds // []) | index("bar-widget")' "$manifest" >/dev/null 2>&1; then
|
if jq -e '(.kinds // []) | index("bar-widget")' "$manifest" >/dev/null 2>&1; then
|
||||||
printf '%s\tBuilt-in widget\t%s\n' "$id" "${name:-$id}"
|
printf '%s\tBuilt-in widget\t%s\n' "$id" "${name:-$id}"
|
||||||
else
|
else
|
||||||
@@ -1030,6 +1037,7 @@ clone_command() {
|
|||||||
local prompt=""
|
local prompt=""
|
||||||
local replace="false"
|
local replace="false"
|
||||||
local add="false"
|
local add="false"
|
||||||
|
local use_bar="false"
|
||||||
local placement=()
|
local placement=()
|
||||||
|
|
||||||
while (( $# > 0 )); do
|
while (( $# > 0 )); do
|
||||||
@@ -1061,6 +1069,10 @@ clone_command() {
|
|||||||
add="true"
|
add="true"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--use)
|
||||||
|
use_bar="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--section | --index | --before | --after)
|
--section | --index | --before | --after)
|
||||||
placement+=("$1" "${2:-}")
|
placement+=("$1" "${2:-}")
|
||||||
shift 2
|
shift 2
|
||||||
@@ -1155,6 +1167,9 @@ clone_command() {
|
|||||||
elif [[ $add == "true" ]]; then
|
elif [[ $add == "true" ]]; then
|
||||||
bar_add "$new_id" "${placement[@]}"
|
bar_add "$new_id" "${placement[@]}"
|
||||||
echo "Cloned $source_id to $new_id"
|
echo "Cloned $source_id to $new_id"
|
||||||
|
elif [[ $use_bar == "true" ]]; then
|
||||||
|
omarchy-config-shell-bar use "$new_id"
|
||||||
|
echo "Cloned $source_id to $new_id and selected it as the active bar option"
|
||||||
else
|
else
|
||||||
echo "Cloned $source_id to $new_id"
|
echo "Cloned $source_id to $new_id"
|
||||||
fi
|
fi
|
||||||
@@ -1176,6 +1191,15 @@ bar_command() {
|
|||||||
(( $# == 0 )) || fail "bar settings does not take arguments"
|
(( $# == 0 )) || fail "bar settings does not take arguments"
|
||||||
exec omarchy-launch-bar-settings
|
exec omarchy-launch-bar-settings
|
||||||
;;
|
;;
|
||||||
|
options | option)
|
||||||
|
exec omarchy-config-shell-bar options "$@"
|
||||||
|
;;
|
||||||
|
use)
|
||||||
|
exec omarchy-config-shell-bar use "$@"
|
||||||
|
;;
|
||||||
|
reset)
|
||||||
|
exec omarchy-config-shell-bar reset "$@"
|
||||||
|
;;
|
||||||
list)
|
list)
|
||||||
bar_list "$@"
|
bar_list "$@"
|
||||||
;;
|
;;
|
||||||
|
|||||||
+22
-13
@@ -25,12 +25,15 @@ first call.
|
|||||||
|
|
||||||
| Kind | What it is |
|
| Kind | What it is |
|
||||||
|--------------|---------------------------------------------|
|
|--------------|---------------------------------------------|
|
||||||
| `bar-widget` | Component the bar drops into a section |
|
| `bar-widget` | Component the active bar drops into a section |
|
||||||
| `panel` | Floating window (e.g. OSD) |
|
| `bar` | Full bar option that can replace `omarchy.bar` |
|
||||||
| `overlay` | Fullscreen overlay (e.g. background picker) |
|
| `panel` | Floating window (e.g. OSD) |
|
||||||
| `menu` | Summoned menu surface |
|
| `overlay` | Fullscreen overlay (e.g. background picker) |
|
||||||
| `service` | Headless singleton, no UI |
|
| `menu` | Summoned menu surface |
|
||||||
|
| `service` | Headless singleton, no UI |
|
||||||
|
|
||||||
|
Only one full bar option is active at a time. The built-in `omarchy.bar` is
|
||||||
|
used when `bar.id` is omitted or when a selected third-party bar cannot load.
|
||||||
Panels, overlays, and menus are loaded when summoned. Plugins can set
|
Panels, overlays, and menus are loaded when summoned. Plugins can set
|
||||||
`keepLoaded: true` to survive between summons. First-party services are
|
`keepLoaded: true` to survive between summons. First-party services are
|
||||||
loaded at startup.
|
loaded at startup.
|
||||||
@@ -59,7 +62,8 @@ every prompt (the path for scripts and agents).
|
|||||||
Sources are recorded in `~/.config/omarchy/plugins/sources.json` and cloned into
|
Sources are recorded in `~/.config/omarchy/plugins/sources.json` and cloned into
|
||||||
`~/.cache/omarchy/plugin-sources/`. You can still install by hand: drop a plugin
|
`~/.cache/omarchy/plugin-sources/`. You can still install by hand: drop a plugin
|
||||||
into `~/.config/omarchy/plugins/<id>/`, run `omarchy plugin rescan`, then
|
into `~/.config/omarchy/plugins/<id>/`, run `omarchy plugin rescan`, then
|
||||||
`omarchy plugin enable <id>` (bar widgets also need `omarchy plugin bar add <id>`).
|
`omarchy plugin enable <id>` (bar widgets also need `omarchy plugin bar add <id>`;
|
||||||
|
full bar replacements are selected with `omarchy plugin bar use <id>`).
|
||||||
The lower-level IPC methods remain available through `omarchy-shell shell ...`.
|
The lower-level IPC methods remain available through `omarchy-shell shell ...`.
|
||||||
|
|
||||||
## IPC
|
## IPC
|
||||||
@@ -91,6 +95,7 @@ individual plugins (`bar`, `image-selector`, …).
|
|||||||
"lock": 300
|
"lock": 300
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
|
"id": "omarchy.bar",
|
||||||
"position": "top",
|
"position": "top",
|
||||||
"transparent": false,
|
"transparent": false,
|
||||||
"centerAnchor": "calendar",
|
"centerAnchor": "calendar",
|
||||||
@@ -109,16 +114,20 @@ individual plugins (`bar`, `image-selector`, …).
|
|||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
1. Every plugin instance is one entry — `bar.layout.<section>` for
|
1. The active bar option is `bar.id`. Omit it or set it to `omarchy.bar` for
|
||||||
|
the built-in bar; set it to a plugin whose manifest declares `kind: "bar"`
|
||||||
|
to replace the full bar.
|
||||||
|
2. Every plugin instance is one entry — `bar.layout.<section>` for
|
||||||
bar widgets, `plugins[]` for everything else.
|
bar widgets, `plugins[]` for everything else.
|
||||||
2. Settings are inline on the entry. No `config:` sub-object, no
|
3. Settings are inline on the entry. No `config:` sub-object, no
|
||||||
merge layers.
|
merge layers.
|
||||||
3. Built-in bar widget ids are namespaced (`omarchy.clock`, `omarchy.audio`, …).
|
4. Built-in bar widget ids are namespaced (`omarchy.clock`, `omarchy.audio`, …).
|
||||||
The migration rewrites older ids such as `Clock` and `AudioPanel` forward.
|
The migration rewrites older ids such as `Clock` and `AudioPanel` forward.
|
||||||
4. Third-party enabled ⇔ present; first-party plugins are always enabled.
|
5. Third-party enabled ⇔ present; for full bar options that means `bar.id`.
|
||||||
5. `allowMultiple: true` in the manifest permits multiple instances.
|
First-party non-bar plugins are always enabled.
|
||||||
6. `idle.screensaver` and `idle.lock` are seconds since user idle began.
|
6. `allowMultiple: true` in the manifest permits multiple instances.
|
||||||
7. `version: 1` is required.
|
7. `idle.screensaver` and `idle.lock` are seconds since user idle began.
|
||||||
|
8. `version: 1` is required.
|
||||||
|
|
||||||
`config/omarchy/shell.json` describes the fresh-install state. When no
|
`config/omarchy/shell.json` describes the fresh-install state. When no
|
||||||
user `shell.json` exists, defaults are used verbatim. Once the user
|
user `shell.json` exists, defaults are used verbatim. Once the user
|
||||||
|
|||||||
+23
-14
@@ -75,13 +75,15 @@ Supported `kinds`:
|
|||||||
|
|
||||||
| Kind | What it is |
|
| Kind | What it is |
|
||||||
|--------------|--------------------------------------------------------------|
|
|--------------|--------------------------------------------------------------|
|
||||||
| `bar-widget` | A component that the bar can drop into a section |
|
| `bar-widget` | A component that the active bar can drop into a section |
|
||||||
| `panel` | A persistent or summoned floating window (e.g. OSD) |
|
| `panel` | A persistent or summoned floating window (e.g. OSD) |
|
||||||
| `overlay` | A fullscreen overlay (e.g. background switcher) |
|
| `overlay` | A fullscreen overlay (e.g. background switcher) |
|
||||||
| `menu` | A summoned menu surface |
|
| `menu` | A summoned menu surface |
|
||||||
| `service` | A headless singleton, no UI |
|
| `service` | A headless singleton, no UI |
|
||||||
| `bar` | Reserved for the first-party bar host (`omarchy.bar`). Third-party plugins should ship `bar-widget`s; they do not replace the host bar. |
|
| `bar` | A full bar option that can replace the built-in `omarchy.bar` |
|
||||||
|
|
||||||
|
Only one `bar` plugin is active at a time. Missing or invalid selections fall
|
||||||
|
back to the built-in `omarchy.bar`, so users always have a safe path home.
|
||||||
Panels, overlays, and menus are loaded when summoned. Plugins that need
|
Panels, overlays, and menus are loaded when summoned. Plugins that need
|
||||||
to outlive a single summon can set `keepLoaded: true` (e.g. the image
|
to outlive a single summon can set `keepLoaded: true` (e.g. the image
|
||||||
picker keeps its overlay window mounted between summons). First-party
|
picker keeps its overlay window mounted between summons). First-party
|
||||||
@@ -132,7 +134,7 @@ You can still drop a plugin in without a source:
|
|||||||
1. Put it in `~/.config/omarchy/plugins/<plugin-id>/` with a `manifest.json`
|
1. Put it in `~/.config/omarchy/plugins/<plugin-id>/` with a `manifest.json`
|
||||||
plus the QML referenced from its `entryPoints`.
|
plus the QML referenced from its `entryPoints`.
|
||||||
2. `omarchy plugin rescan`.
|
2. `omarchy plugin rescan`.
|
||||||
3. `omarchy plugin enable <id>` (bar widgets also need `omarchy plugin bar add <id>`).
|
3. `omarchy plugin enable <id>` (bar widgets also need `omarchy plugin bar add <id>`; full bar replacements are selected with `omarchy plugin bar use <id>`).
|
||||||
|
|
||||||
The lower-level IPC equivalents remain available via `omarchy-shell shell rescanPlugins`,
|
The lower-level IPC equivalents remain available via `omarchy-shell shell rescanPlugins`,
|
||||||
`omarchy-shell shell setPluginEnabled <id> true`, and `omarchy-shell shell listPlugins`.
|
`omarchy-shell shell setPluginEnabled <id> true`, and `omarchy-shell shell listPlugins`.
|
||||||
@@ -151,7 +153,9 @@ omarchy plugin edit local.clock --with ai # edit with `omarchy launch ai`
|
|||||||
```
|
```
|
||||||
|
|
||||||
First-party plugins under `shell/plugins/`
|
First-party plugins under `shell/plugins/`
|
||||||
are discovered the same way and cannot be disabled.
|
are discovered the same way and cannot be disabled, except that the built-in
|
||||||
|
bar option can become inactive while a third-party `kind: "bar"` plugin is the
|
||||||
|
selected bar.
|
||||||
|
|
||||||
## IPC contract
|
## IPC contract
|
||||||
|
|
||||||
@@ -224,6 +228,7 @@ becomes the authoritative file — we do **not** deep-merge defaults back in.
|
|||||||
"lock": 300
|
"lock": 300
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
|
"id": "omarchy.bar",
|
||||||
"position": "top",
|
"position": "top",
|
||||||
"transparent": false,
|
"transparent": false,
|
||||||
"centerAnchor": "omarchy.clock",
|
"centerAnchor": "omarchy.clock",
|
||||||
@@ -241,27 +246,31 @@ becomes the authoritative file — we do **not** deep-merge defaults back in.
|
|||||||
|
|
||||||
### Storage rules
|
### Storage rules
|
||||||
|
|
||||||
1. **Every plugin instance is one entry.** Either in `bar.layout.<section>`
|
1. **The active bar option is `bar.id`.** Omit it or set it to `omarchy.bar`
|
||||||
|
to use the built-in bar. Set it to another plugin id whose manifest declares
|
||||||
|
`kind: "bar"` to replace the full bar.
|
||||||
|
2. **Every plugin instance is one entry.** Either in `bar.layout.<section>`
|
||||||
for bar widgets, or in `plugins[]` for panels, overlays, services,
|
for bar widgets, or in `plugins[]` for panels, overlays, services,
|
||||||
menus, and anything else non-bar.
|
menus, and anything else non-bar.
|
||||||
2. **Settings are inline on the entry.** No `config:` sub-object, no
|
3. **Settings are inline on the entry.** No `config:` sub-object, no
|
||||||
separate per-plugin settings file, no merge layers. The fields on each
|
separate per-plugin settings file, no merge layers. The fields on each
|
||||||
entry are the values the plugin sees.
|
entry are the values the plugin sees.
|
||||||
3. **Built-in widget ids are namespaced.** Use ids such as `omarchy.clock`,
|
4. **Built-in widget ids are namespaced.** Use ids such as `omarchy.clock`,
|
||||||
`omarchy.audio`, and `omarchy.network`. The migration rewrites older ids
|
`omarchy.audio`, and `omarchy.network`. The migration rewrites older ids
|
||||||
like `Clock` and `AudioPanel` forward.
|
like `Clock` and `AudioPanel` forward.
|
||||||
4. **Third-party enabled ⇔ present.** A third-party plugin is enabled iff
|
5. **Third-party enabled ⇔ present.** A third-party plugin is enabled iff
|
||||||
its id appears somewhere in shell.json. For bar widgets, the bar
|
its id appears somewhere in shell.json. For full bar options, that means
|
||||||
settings UI adds/removes layout entries; other plugin kinds are enabled
|
`bar.id`; for bar widgets, the bar settings UI adds/removes layout entries;
|
||||||
with the shell IPC. First-party plugins are always enabled.
|
other plugin kinds are enabled with the shell IPC. First-party non-bar
|
||||||
5. **Multiple instances** are allowed when a manifest sets
|
plugins are always enabled.
|
||||||
|
6. **Multiple instances** are allowed when a manifest sets
|
||||||
`allowMultiple: true`. Each instance is independent — e.g. two clock
|
`allowMultiple: true`. Each instance is independent — e.g. two clock
|
||||||
widgets in different timezones are just two `{"id":"omarchy.clock", "timezone": ...}`
|
widgets in different timezones are just two `{"id":"omarchy.clock", "timezone": ...}`
|
||||||
entries with their own values.
|
entries with their own values.
|
||||||
6. **Idle timings are top-level.** `idle.screensaver` and `idle.lock`
|
7. **Idle timings are top-level.** `idle.screensaver` and `idle.lock`
|
||||||
are seconds since user idle began, so the default lock fires at 300s
|
are seconds since user idle began, so the default lock fires at 300s
|
||||||
even if the 150s screensaver starts first.
|
even if the 150s screensaver starts first.
|
||||||
7. **`version: 1` is required** at the top level. The shell will fall back
|
8. **`version: 1` is required** at the top level. The shell will fall back
|
||||||
to defaults rather than load an unknown version.
|
to defaults rather than load an unknown version.
|
||||||
|
|
||||||
## Implementation history
|
## Implementation history
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
These plugins ship with Omarchy and are discovered by the shell at startup.
|
These plugins ship with Omarchy and are discovered by the shell at startup.
|
||||||
They use the same `manifest.json` contract as third-party plugins; the
|
They use the same `manifest.json` contract as third-party plugins; the
|
||||||
only difference is that the shell flags them with `__isFirstParty: true`
|
only difference is that the shell flags them with `__isFirstParty: true`.
|
||||||
so they are always enabled. Services and keep-loaded panels are mounted at
|
First-party non-bar plugins are always enabled; `omarchy.bar` is the default
|
||||||
startup; other panels, overlays, and menus are loaded on demand.
|
bar option and becomes inactive only while a third-party `kind: "bar"` plugin is
|
||||||
|
selected. Services and keep-loaded panels are mounted at startup; other panels,
|
||||||
|
overlays, and menus are loaded on demand.
|
||||||
|
|
||||||
User-installed plugins live alongside these conceptually but on disk under
|
User-installed plugins live alongside these conceptually but on disk under
|
||||||
`~/.config/omarchy/plugins/<plugin-id>/` rather than in this directory.
|
`~/.config/omarchy/plugins/<plugin-id>/` rather than in this directory.
|
||||||
@@ -40,7 +42,7 @@ own plugin directories, each with its own `manifest.json`.
|
|||||||
|
|
||||||
## Bar
|
## Bar
|
||||||
|
|
||||||
The status bar. Mounted at startup, lives forever. Layout lives in the
|
The built-in status bar and default full-bar option. Layout lives in the
|
||||||
top-level `bar:` subtree of `~/.config/omarchy/shell.json` (with the shell
|
top-level `bar:` subtree of `~/.config/omarchy/shell.json` (with the shell
|
||||||
providing [`config/omarchy/shell.json`](../../config/omarchy/shell.json) when
|
providing [`config/omarchy/shell.json`](../../config/omarchy/shell.json) when
|
||||||
the user has no file). See [`bar/README.md`](bar/README.md) for the widget catalogue
|
the user has no file). See [`bar/README.md`](bar/README.md) for the widget catalogue
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ Item {
|
|||||||
// Injected by the host shell. Used for shell-wide actions such as opening
|
// Injected by the host shell. Used for shell-wide actions such as opening
|
||||||
// settings and persisting inline widget state.
|
// settings and persisting inline widget state.
|
||||||
property var shell: null
|
property var shell: null
|
||||||
|
// Manifest for the active bar option. Present for custom bars and useful for
|
||||||
|
// diagnostics; the built-in bar does not otherwise need it.
|
||||||
|
property var manifest: null
|
||||||
// Mirrors the on-disk `bar-off` flag so the user can hide the bar without
|
// Mirrors the on-disk `bar-off` flag so the user can hide the bar without
|
||||||
// killing the entire shell. Wired to BarPanel.visible below; updated by the
|
// killing the entire shell. Wired to BarPanel.visible below; updated by the
|
||||||
// FileView watcher further down.
|
// FileView watcher further down.
|
||||||
|
|||||||
@@ -97,29 +97,41 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enabled = the plugin id is referenced somewhere in shell.json. That can
|
// Enabled = the plugin id is referenced somewhere in shell.json. That can
|
||||||
// be either a layout entry inside `bar.layout.*` (bar widgets) or a top-level
|
// be either the active bar option in `bar.id`, a layout entry inside
|
||||||
// entry in `plugins[]` (panels, overlays, services).
|
// `bar.layout.*` (bar widgets), or a top-level entry in `plugins[]` (panels,
|
||||||
|
// overlays, services).
|
||||||
//
|
//
|
||||||
// Special cases (implicitly always enabled, no shell.json entry needed):
|
// Special cases (implicitly always enabled, no shell.json entry needed):
|
||||||
// - plugins whose `kinds` contains "bar" are mounted directly by the host.
|
// - the built-in bar option (`omarchy.bar`) is active when `bar.id` is
|
||||||
// - first-party plugins are shell infrastructure (settings,
|
// missing or set to `omarchy.bar`.
|
||||||
|
// - first-party non-bar plugins are shell infrastructure (settings,
|
||||||
// image-picker, ...). Requiring users to add them to plugins[] just to
|
// image-picker, ...). Requiring users to add them to plugins[] just to
|
||||||
// summon them was a footgun: a stock shell.json with `plugins: []` would
|
// summon them was a footgun: a stock shell.json with `plugins: []` would
|
||||||
// silently make `omarchy launch bar-settings` a no-op.
|
// silently make `omarchy launch bar-settings` a no-op.
|
||||||
function isEnabled(id) {
|
function isEnabled(id) {
|
||||||
var key = String(id)
|
var key = String(id)
|
||||||
var manifest = installedPlugins[key]
|
var manifest = installedPlugins[key]
|
||||||
|
var config = shellConfigProvider ? shellConfigProvider() : null
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
if (Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar") !== -1) return true
|
if (Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar") !== -1) {
|
||||||
|
var selectedBar = ""
|
||||||
|
if (Util.isPlainObject(config) && Util.isPlainObject(config.bar))
|
||||||
|
selectedBar = Util.canonicalWidgetId(String(config.bar.id || ""))
|
||||||
|
if (!selectedBar) selectedBar = "omarchy.bar"
|
||||||
|
return selectedBar === key
|
||||||
|
}
|
||||||
if (manifest.__isFirstParty) return true
|
if (manifest.__isFirstParty) return true
|
||||||
}
|
}
|
||||||
var config = shellConfigProvider ? shellConfigProvider() : null
|
|
||||||
return findEntryLocation(config, key).found
|
return findEntryLocation(config, key).found
|
||||||
}
|
}
|
||||||
|
|
||||||
function findEntryLocation(config, id) {
|
function findEntryLocation(config, id) {
|
||||||
if (!Util.isPlainObject(config)) return { found: false }
|
if (!Util.isPlainObject(config)) return { found: false }
|
||||||
var key = Util.canonicalWidgetId(String(id))
|
var key = Util.canonicalWidgetId(String(id))
|
||||||
|
if (Util.isPlainObject(config.bar)) {
|
||||||
|
var selectedBar = Util.canonicalWidgetId(String(config.bar.id || ""))
|
||||||
|
if (selectedBar === key) return { found: true, kind: "bar-option" }
|
||||||
|
}
|
||||||
if (Util.isPlainObject(config.bar) && Util.isPlainObject(config.bar.layout)) {
|
if (Util.isPlainObject(config.bar) && Util.isPlainObject(config.bar.layout)) {
|
||||||
var sections = ["left", "center", "right"]
|
var sections = ["left", "center", "right"]
|
||||||
for (var s = 0; s < sections.length; s++) {
|
for (var s = 0; s < sections.length; s++) {
|
||||||
@@ -148,12 +160,23 @@ QtObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var manifest = installedPlugins[key]
|
var manifest = installedPlugins[key]
|
||||||
|
var isBarOption = manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar") !== -1
|
||||||
var isBarWidget = manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar-widget") !== -1
|
var isBarWidget = manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar-widget") !== -1
|
||||||
shellConfigMutator(function(config) {
|
shellConfigMutator(function(config) {
|
||||||
// Ensure shape exists.
|
// Ensure shape exists.
|
||||||
if (!Util.isPlainObject(config.bar)) config.bar = { layout: { left: [], center: [], right: [] } }
|
if (!Util.isPlainObject(config.bar)) config.bar = { layout: { left: [], center: [], right: [] } }
|
||||||
if (!Util.isPlainObject(config.bar.layout)) config.bar.layout = { left: [], center: [], right: [] }
|
if (!Util.isPlainObject(config.bar.layout)) config.bar.layout = { left: [], center: [], right: [] }
|
||||||
if (!Array.isArray(config.plugins)) config.plugins = []
|
if (!Array.isArray(config.plugins)) config.plugins = []
|
||||||
|
|
||||||
|
if (isBarOption) {
|
||||||
|
if (value) {
|
||||||
|
config.bar.id = key
|
||||||
|
} else if (Util.canonicalWidgetId(String(config.bar.id || "")) === key) {
|
||||||
|
delete config.bar.id
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var location = findEntryLocation(config, key)
|
var location = findEntryLocation(config, key)
|
||||||
if (value && !location.found) {
|
if (value && !location.found) {
|
||||||
var entry = { id: key }
|
var entry = { id: key }
|
||||||
@@ -166,7 +189,7 @@ QtObject {
|
|||||||
} else if (!value && location.found) {
|
} else if (!value && location.found) {
|
||||||
if (location.kind === "bar") {
|
if (location.kind === "bar") {
|
||||||
config.bar.layout[location.section].splice(location.index, 1)
|
config.bar.layout[location.section].splice(location.index, 1)
|
||||||
} else {
|
} else if (location.kind === "plugin") {
|
||||||
config.plugins.splice(location.index, 1)
|
config.plugins.splice(location.index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-10
@@ -56,6 +56,7 @@ ShellRoot {
|
|||||||
property bool suppressUserReload: false
|
property bool suppressUserReload: false
|
||||||
|
|
||||||
onShellConfigChanged: {
|
onShellConfigChanged: {
|
||||||
|
if (failedBarId !== "") failedBarId = ""
|
||||||
pluginRegistry.registryRevision++
|
pluginRegistry.registryRevision++
|
||||||
pluginRegistry.pluginsChanged()
|
pluginRegistry.pluginsChanged()
|
||||||
}
|
}
|
||||||
@@ -105,6 +106,7 @@ ShellRoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property var barConfig: shellConfig && Util.isPlainObject(shellConfig.bar) ? shellConfig.bar : builtinShellConfig.bar
|
readonly property var barConfig: shellConfig && Util.isPlainObject(shellConfig.bar) ? shellConfig.bar : builtinShellConfig.bar
|
||||||
|
onBarConfigChanged: if (bar && "barConfig" in bar) bar.barConfig = shell.barConfig
|
||||||
FileView {
|
FileView {
|
||||||
id: defaultsFile
|
id: defaultsFile
|
||||||
path: shell.defaultsPath
|
path: shell.defaultsPath
|
||||||
@@ -159,15 +161,102 @@ ShellRoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exposed as a property so child plugins (notifications, future panels)
|
// Exposed as a property so child plugins (notifications, future panels)
|
||||||
// can read barSize/barHidden/position to anchor relative to the bar.
|
// can read barSize/barHidden/position to anchor relative to the active bar.
|
||||||
property alias bar: bar
|
readonly property string defaultBarId: "omarchy.bar"
|
||||||
|
readonly property string selectedBarId: {
|
||||||
|
var config = shell.barConfig
|
||||||
|
if (Util.isPlainObject(config)) {
|
||||||
|
var configured = Util.canonicalWidgetId(String(config.id || ""))
|
||||||
|
if (configured) return configured
|
||||||
|
}
|
||||||
|
return shell.defaultBarId
|
||||||
|
}
|
||||||
|
property string failedBarId: ""
|
||||||
|
readonly property bool selectedBarAvailable: {
|
||||||
|
var revision = shell.pluginRegistry.registryRevision
|
||||||
|
return shell.barOptionAvailable(shell.selectedBarId)
|
||||||
|
}
|
||||||
|
readonly property string activeBarId: selectedBarId !== failedBarId && selectedBarAvailable ? selectedBarId : defaultBarId
|
||||||
|
readonly property var activeBarManifest: {
|
||||||
|
var revision = shell.pluginRegistry.registryRevision
|
||||||
|
return shell.barManifestFor(shell.activeBarId)
|
||||||
|
}
|
||||||
|
readonly property string activeBarSourceUrl: activeBarId === defaultBarId ? "" : shell.pluginRegistry.entryPointUrl(activeBarManifest, "bar")
|
||||||
|
property var bar: null
|
||||||
|
|
||||||
Bar {
|
onSelectedBarIdChanged: if (failedBarId !== "") failedBarId = ""
|
||||||
id: bar
|
|
||||||
omarchyPath: shell.omarchyPath
|
function barManifestFor(pluginId) {
|
||||||
barWidgetRegistry: shell.barWidgetRegistry
|
var plugins = shell.pluginRegistry ? shell.pluginRegistry.installedPlugins : null
|
||||||
barConfig: shell.barConfig
|
return plugins ? plugins[String(pluginId || "")] || null : null
|
||||||
shell: shell
|
}
|
||||||
|
|
||||||
|
function isBarOptionManifest(manifest) {
|
||||||
|
return manifest
|
||||||
|
&& Array.isArray(manifest.kinds)
|
||||||
|
&& manifest.kinds.indexOf("bar") !== -1
|
||||||
|
&& manifest.entryPoints
|
||||||
|
&& manifest.entryPoints.bar
|
||||||
|
}
|
||||||
|
|
||||||
|
function barOptionAvailable(pluginId) {
|
||||||
|
var id = String(pluginId || "")
|
||||||
|
if (id === "" || id === shell.defaultBarId) return true
|
||||||
|
var manifest = shell.barManifestFor(id)
|
||||||
|
return shell.isBarOptionManifest(manifest) && shell.pluginRegistry.entryPointUrl(manifest, "bar") !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActiveBarOption(pluginId) {
|
||||||
|
return String(pluginId || "") === shell.activeBarId
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureBar(target, manifest) {
|
||||||
|
if (!target) return
|
||||||
|
if ("omarchyPath" in target) target.omarchyPath = shell.omarchyPath
|
||||||
|
if ("shell" in target) target.shell = shell
|
||||||
|
if ("manifest" in target) target.manifest = manifest
|
||||||
|
if ("barWidgetRegistry" in target) target.barWidgetRegistry = shell.barWidgetRegistry
|
||||||
|
if ("pluginRegistry" in target) target.pluginRegistry = shell.pluginRegistry
|
||||||
|
if ("barConfig" in target) target.barConfig = shell.barConfig
|
||||||
|
shell.bar = target
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: defaultBarComponent
|
||||||
|
|
||||||
|
Bar {
|
||||||
|
omarchyPath: shell.omarchyPath
|
||||||
|
barWidgetRegistry: shell.barWidgetRegistry
|
||||||
|
barConfig: shell.barConfig
|
||||||
|
shell: shell
|
||||||
|
manifest: shell.barManifestFor(shell.defaultBarId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: defaultBarLoader
|
||||||
|
|
||||||
|
active: shell.activeBarId === shell.defaultBarId
|
||||||
|
sourceComponent: defaultBarComponent
|
||||||
|
onLoaded: shell.configureBar(item, shell.barManifestFor(shell.defaultBarId))
|
||||||
|
onActiveChanged: if (!active && shell.activeBarId !== shell.defaultBarId) shell.bar = null
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: pluginBarLoader
|
||||||
|
|
||||||
|
active: shell.activeBarId !== shell.defaultBarId && shell.activeBarSourceUrl !== ""
|
||||||
|
source: shell.activeBarId !== shell.defaultBarId ? shell.activeBarSourceUrl : ""
|
||||||
|
asynchronous: true
|
||||||
|
onLoaded: shell.configureBar(item, shell.activeBarManifest)
|
||||||
|
onActiveChanged: if (!active && shell.activeBarId === shell.defaultBarId) shell.bar = null
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Loader.Error) {
|
||||||
|
var detail = errorString && errorString() ? errorString() : ""
|
||||||
|
console.warn("bar option " + shell.activeBarId + " failed to load, falling back to " + shell.defaultBarId + ":", detail)
|
||||||
|
shell.failedBarId = shell.activeBarId
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------- services
|
// ------------------------------------------------------------- services
|
||||||
@@ -639,11 +728,15 @@ ShellRoot {
|
|||||||
var out = []
|
var out = []
|
||||||
var plugins = shell.pluginRegistry.installedPlugins
|
var plugins = shell.pluginRegistry.installedPlugins
|
||||||
for (var id in plugins) {
|
for (var id in plugins) {
|
||||||
|
var kinds = plugins[id].kinds || []
|
||||||
|
var isBarOption = Array.isArray(kinds) && kinds.indexOf("bar") !== -1
|
||||||
|
var active = isBarOption && shell.isActiveBarOption(id)
|
||||||
out.push({
|
out.push({
|
||||||
id: id,
|
id: id,
|
||||||
name: plugins[id].name,
|
name: plugins[id].name,
|
||||||
kinds: plugins[id].kinds,
|
kinds: kinds,
|
||||||
enabled: shell.pluginRegistry.isEnabled(id),
|
enabled: isBarOption ? active : shell.pluginRegistry.isEnabled(id),
|
||||||
|
active: active,
|
||||||
firstParty: !!plugins[id].__isFirstParty
|
firstParty: !!plugins[id].__isFirstParty
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,35 @@ cat >"$TMPDIR/home/.config/omarchy/shell.json" <<'JSON'
|
|||||||
}
|
}
|
||||||
JSON
|
JSON
|
||||||
|
|
||||||
|
mkdir -p "$TMPDIR/home/.config/omarchy/plugins/local.demo-bar"
|
||||||
|
cat >"$TMPDIR/home/.config/omarchy/plugins/local.demo-bar/manifest.json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "local.demo-bar",
|
||||||
|
"name": "Demo bar",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Test",
|
||||||
|
"description": "Replacement bar for config tests",
|
||||||
|
"kinds": ["bar"],
|
||||||
|
"entryPoints": { "bar": "Bar.qml" }
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
touch "$TMPDIR/home/.config/omarchy/plugins/local.demo-bar/Bar.qml"
|
||||||
|
|
||||||
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-config-shell-bar options --json | jq -e '
|
||||||
|
any(.[]; .id == "omarchy.bar" and .active == true) and
|
||||||
|
any(.[]; .id == "local.demo-bar" and .active == false)
|
||||||
|
' >/dev/null
|
||||||
|
pass "shell config lists bar options"
|
||||||
|
|
||||||
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-config-shell-bar use local.demo-bar
|
||||||
|
jq -e '.bar.id == "local.demo-bar"' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
||||||
|
pass "shell config selects a bar option"
|
||||||
|
|
||||||
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-config-shell-bar reset
|
||||||
|
jq -e '.bar.id == null' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
||||||
|
pass "shell config resets to built-in bar option"
|
||||||
|
|
||||||
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-config-shell-bar add omarchy.tailscale
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-config-shell-bar add omarchy.tailscale
|
||||||
jq -e '
|
jq -e '
|
||||||
def ids: map(.id // .);
|
def ids: map(.id // .);
|
||||||
|
|||||||
@@ -78,10 +78,11 @@ ShellRoot {
|
|||||||
function runChecks() {
|
function runChecks() {
|
||||||
var scan = ""
|
var scan = ""
|
||||||
scan += block("firstparty", "/first/widgets/clock", manifest("omarchy.first-widget", ["bar-widget"], { barWidget: "Widget.qml" }))
|
scan += block("firstparty", "/first/widgets/clock", manifest("omarchy.first-widget", ["bar-widget"], { barWidget: "Widget.qml" }))
|
||||||
scan += block("firstparty", "/first/bar", manifest("omarchy.first-bar", ["bar"], { bar: "Bar.qml" }))
|
scan += block("firstparty", "/first/bar", manifest("omarchy.bar", ["bar"], { bar: "Bar.qml" }))
|
||||||
scan += block("firstparty", "/first/panels/grouped", manifest("omarchy.grouped-panel", ["panel"], { panel: "Panel.qml" }))
|
scan += block("firstparty", "/first/panels/grouped", manifest("omarchy.grouped-panel", ["panel"], { panel: "Panel.qml" }))
|
||||||
scan += block("thirdparty", "/third/panel", manifest("third.panel", ["panel"], { panel: "Panel.qml" }))
|
scan += block("thirdparty", "/third/panel", manifest("third.panel", ["panel"], { panel: "Panel.qml" }))
|
||||||
scan += block("thirdparty", "/third/widget", manifest("third.widget", ["bar-widget"], { barWidget: "Widget.qml" }))
|
scan += block("thirdparty", "/third/widget", manifest("third.widget", ["bar-widget"], { barWidget: "Widget.qml" }))
|
||||||
|
scan += block("thirdparty", "/third/bar", manifest("third.bar", ["bar"], { bar: "Bar.qml" }))
|
||||||
scan += block("thirdparty", "/third/shadow", manifest("omarchy.first-widget", ["panel"], { panel: "Panel.qml" }))
|
scan += block("thirdparty", "/third/shadow", manifest("omarchy.first-widget", ["panel"], { panel: "Panel.qml" }))
|
||||||
scan += block("thirdparty", "/third/reserved", manifest("omarchy.reserved", ["panel"], { panel: "Panel.qml" }))
|
scan += block("thirdparty", "/third/reserved", manifest("omarchy.reserved", ["panel"], { panel: "Panel.qml" }))
|
||||||
scan += block("thirdparty", "/third/unsafe", manifest("third.unsafe", ["panel"], { panel: "../Panel.qml" }))
|
scan += block("thirdparty", "/third/unsafe", manifest("third.unsafe", ["panel"], { panel: "../Panel.qml" }))
|
||||||
@@ -92,9 +93,10 @@ ShellRoot {
|
|||||||
registry.parseScanOutput(scan)
|
registry.parseScanOutput(scan)
|
||||||
|
|
||||||
root.assertDeepEqual(pluginIds(), [
|
root.assertDeepEqual(pluginIds(), [
|
||||||
"omarchy.first-bar",
|
"omarchy.bar",
|
||||||
"omarchy.first-widget",
|
"omarchy.first-widget",
|
||||||
"omarchy.grouped-panel",
|
"omarchy.grouped-panel",
|
||||||
|
"third.bar",
|
||||||
"third.panel",
|
"third.panel",
|
||||||
"third.widget"
|
"third.widget"
|
||||||
], "registry merges valid first-party and third-party manifests")
|
], "registry merges valid first-party and third-party manifests")
|
||||||
@@ -111,9 +113,18 @@ ShellRoot {
|
|||||||
root.assertTrue(!has("third.schema"), "unsupported schema versions are rejected")
|
root.assertTrue(!has("third.schema"), "unsupported schema versions are rejected")
|
||||||
|
|
||||||
root.assertTrue(registry.isEnabled("omarchy.first-widget"), "first-party plugins are implicitly enabled")
|
root.assertTrue(registry.isEnabled("omarchy.first-widget"), "first-party plugins are implicitly enabled")
|
||||||
root.assertTrue(registry.isEnabled("omarchy.first-bar"), "bar plugins are implicitly enabled")
|
root.assertTrue(registry.isEnabled("omarchy.bar"), "built-in bar option is active by default")
|
||||||
|
root.assertTrue(!registry.isEnabled("third.bar"), "third-party bar options start inactive")
|
||||||
root.assertTrue(!registry.isEnabled("third.panel"), "third-party plugins start disabled")
|
root.assertTrue(!registry.isEnabled("third.panel"), "third-party plugins start disabled")
|
||||||
|
|
||||||
|
registry.setEnabled("third.bar", true)
|
||||||
|
root.assertEqual(root.config.bar.id, "third.bar", "enabling third-party bar options writes bar id")
|
||||||
|
root.assertTrue(registry.isEnabled("third.bar"), "selected third-party bar options are enabled")
|
||||||
|
root.assertTrue(!registry.isEnabled("omarchy.bar"), "selecting third-party bar options deactivates built-in bar")
|
||||||
|
registry.setEnabled("third.bar", false)
|
||||||
|
root.assertTrue(root.config.bar.id === undefined, "disabling active bar options resets to built-in")
|
||||||
|
root.assertTrue(registry.isEnabled("omarchy.bar"), "built-in bar option returns after reset")
|
||||||
|
|
||||||
registry.setEnabled("third.panel", true)
|
registry.setEnabled("third.panel", true)
|
||||||
root.assertDeepEqual(root.config.plugins, [{ id: "third.panel" }], "enabling third-party panels writes plugins array")
|
root.assertDeepEqual(root.config.plugins, [{ id: "third.panel" }], "enabling third-party panels writes plugins array")
|
||||||
root.assertTrue(registry.isEnabled("third.panel"), "enabled third-party panels are found")
|
root.assertTrue(registry.isEnabled("third.panel"), "enabled third-party panels are found")
|
||||||
|
|||||||
Reference in New Issue
Block a user