cmd_replace fed $PLACEMENT_FROM_SECTION/$PLACEMENT_FROM_INDEX to jq without ever calling parse_placement, so they were always empty strings that implied flag support which does not exist. Pass literal empties with a comment stating replace resolves by widget id only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
847 lines
28 KiB
Bash
Executable File
847 lines
28 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Mutate the user shell.json bar layout, active bar option, and bar settings
|
|
# omarchy:group=bar
|
|
# omarchy:args=show | layout [--json] | list [--json] [--all] | options [--json] | use <plugin> | reset | add [plugin] [placement] | move <plugin> [placement] | remove [plugin] [placement] | set <plugin> <key> <value> [--json] [placement] | replace <old> <new> | position <top|bottom|left|right> | transparent <true|false> | settings
|
|
# omarchy:examples=omarchy bar show | omarchy bar list | omarchy bar add omarchy.tailscale | omarchy bar add omarchy.clock --section center --before omarchy.weather | omarchy bar move omarchy.clock --section center --index 0 | omarchy bar remove omarchy.tailscale | omarchy bar set omarchy.clock format HH:mm | omarchy bar use local.neon-bar | omarchy bar position top | omarchy bar transparent true
|
|
|
|
set -euo pipefail
|
|
|
|
CONFIG_FILE="$HOME/.config/omarchy/shell.json"
|
|
OMARCHY_ROOT="${OMARCHY_PATH:-}"
|
|
DEFAULTS_FILE="$OMARCHY_ROOT/config/omarchy/shell.json"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: omarchy bar <command> [args...]
|
|
|
|
Inspect:
|
|
show Current bar JSON
|
|
layout [--json] Current bar layout (section/index/widget)
|
|
list [--json] [--all] Addable bar widgets (alias: available, widgets)
|
|
options [--json] Bar replacement options
|
|
|
|
Select:
|
|
use <id> Use a bar option as the active bar
|
|
reset Return to the built-in Omarchy bar
|
|
|
|
Edit layout:
|
|
add <id> [placement] Add a bar widget
|
|
move <id> [placement] [--from-section S --from-index N]
|
|
Move a widget within/between sections
|
|
remove <id> [placement] (alias: rm, drop)
|
|
Remove a widget
|
|
set <id> <key> <value> [--json] [placement]
|
|
Set a per-widget option
|
|
|
|
Other:
|
|
replace <old-id> <new-id> Replace the first instance of a widget id
|
|
position <top|bottom|left|right> Bar position
|
|
transparent <true|false> Bar transparency
|
|
settings Open the inline bar config panel
|
|
|
|
Placement (for add/move/remove/set):
|
|
--section <left|center|right> Target section
|
|
--index <n> Insert/remove at index in the target section
|
|
--before <id> Insert before the first matching widget
|
|
--after <id> Insert after the first matching widget
|
|
--from-section <section> Source section (move/remove/set)
|
|
--from-index <n> Source index (move/remove/set)
|
|
--duplicate add: allow a second instance of the widget
|
|
--all remove: remove every matching instance
|
|
|
|
Without placement flags, 'add' inserts after the section anchor (workspaces on
|
|
left, weather on center, tray on right) and dedupes across sections; 'move'
|
|
preserves the source section; 'remove' drops the first match (or every match
|
|
with --all).
|
|
|
|
Examples:
|
|
omarchy bar show
|
|
omarchy bar layout
|
|
omarchy bar list
|
|
omarchy bar add omarchy.tailscale
|
|
omarchy bar add omarchy.clock --section center --before omarchy.weather
|
|
omarchy bar move omarchy.clock --section center --index 0
|
|
omarchy bar remove omarchy.tailscale
|
|
omarchy bar remove omarchy.tailscale --all
|
|
omarchy bar set omarchy.clock format HH:mm
|
|
omarchy bar set omarchy.indicators items '["Dnd","NightLight"]' --json
|
|
omarchy bar use local.neon-bar
|
|
omarchy bar reset
|
|
omarchy bar position top
|
|
omarchy bar transparent true
|
|
USAGE
|
|
}
|
|
|
|
fail() {
|
|
echo "omarchy-bar: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_command() {
|
|
command -v "$1" >/dev/null 2>&1 || fail "$1 is required"
|
|
}
|
|
|
|
require_omarchy_path() {
|
|
[[ -n $OMARCHY_ROOT ]] || fail "OMARCHY_PATH is not set"
|
|
}
|
|
|
|
refresh_shell_config() {
|
|
if ! omarchy-shell shell reloadConfig >/dev/null 2>&1; then
|
|
omarchy-shell -q shell rescanPlugins >/dev/null 2>&1 || true
|
|
fi
|
|
}
|
|
|
|
source_file() {
|
|
local source="$CONFIG_FILE"
|
|
|
|
if [[ ! -s $source ]]; then
|
|
require_omarchy_path
|
|
source="$DEFAULTS_FILE"
|
|
fi
|
|
|
|
[[ -s $source ]] || fail "could not find shell config or defaults"
|
|
printf '%s\n' "$source"
|
|
}
|
|
|
|
# jq defs shared across mutations. All `def`s come first so the pipeline that
|
|
# follows them stays valid jq. `entry_id` and the shape helpers are used by both
|
|
# the normalize pipeline and the resolve_target/resolve_source helpers.
|
|
JQ_DEFS='
|
|
def object_or_empty: if type == "object" then . else {} end;
|
|
def array_or_empty: if type == "array" then . else [] end;
|
|
def entry_id: if type == "object" then (.id // "" | tostring) else tostring end;
|
|
def anchor_for($section): { left: "omarchy.workspaces", center: "omarchy.weather", right: "omarchy.tray" }[$section];
|
|
def find_all($id; $only):
|
|
[ ["left","center","right"][] as $section
|
|
| ((.bar.layout[$section] // []) | to_entries[])
|
|
| select((($only == "" or $section == $only)) and ((.value | entry_id) == $id))
|
|
| {section: $section, index: .key, entry: .value} ];
|
|
def entry_at($section; $index):
|
|
if $index < 0 or $index >= (.bar.layout[$section] | length) then
|
|
error("no widget at " + $section + "[" + ($index | tostring) + "]")
|
|
else .bar.layout[$section][$index] end;
|
|
def resolve_target($defaultSection):
|
|
. as $root
|
|
| (if $before != "" or $after != "" then
|
|
($root | find_all($before + $after; $targetSection) | .[0]
|
|
// error("could not find target widget " + ($before + $after) + ($targetSection | if . == "" then "" else " in " + . end)))
|
|
| .section as $section | (.index + (if $after == "" then 0 else 1 end)) as $index
|
|
| {section: $section, index: $index, anchor: false}
|
|
elif $targetSection != "" or $targetIndex != "" then
|
|
{ section: ($targetSection | if . == "" then $defaultSection else . end),
|
|
index: (if $targetIndex == "" then null else ($targetIndex | tonumber) end),
|
|
anchor: false }
|
|
else
|
|
{ section: $defaultSection, index: null, anchor: true }
|
|
end)
|
|
| .section as $section
|
|
| .index as $rawIndex
|
|
| (if $rawIndex == null then
|
|
if .anchor then
|
|
($root.bar.layout[$section] | map(entry_id) | index(anchor_for($section))) as $anchorIndex
|
|
| if $anchorIndex == null then ($root.bar.layout[$section] | length) else ($anchorIndex + 1) end
|
|
else ($root.bar.layout[$section] | length) end
|
|
elif $rawIndex > ($root.bar.layout[$section] | length) then ($root.bar.layout[$section] | length)
|
|
else $rawIndex end) as $index
|
|
| {section: $section, index: $index};
|
|
def resolve_source:
|
|
if $fromIndex != "" then
|
|
if $fromSection == "" then error("--from-index requires --from-section")
|
|
else
|
|
($fromIndex | tonumber) as $index
|
|
| entry_at($fromSection; $index) as $entry
|
|
| if $widgetId != "" and ($entry | entry_id) != $widgetId then
|
|
error("widget at " + $fromSection + "[" + ($index | tostring) + "] is not " + $widgetId)
|
|
else {section: $fromSection, index: $index, entry: $entry} end
|
|
end
|
|
else
|
|
(find_all($widgetId; $fromSection)
|
|
| if length == 0 then
|
|
error("could not find widget " + $widgetId + ($fromSection | if . == "" then "" else " in " + . end))
|
|
else .[0] end)
|
|
end;
|
|
'
|
|
|
|
# jq pipeline that normalizes shell.json into a well-shaped object with
|
|
# version=1, bar.layout.{left,center,right} arrays, and plugins array. Every
|
|
# mutation pipes through this so downstream jq can assume structure.
|
|
NORMALIZE='
|
|
object_or_empty
|
|
| .version = 1
|
|
| .bar = (.bar | object_or_empty)
|
|
| .bar.layout = (.bar.layout | object_or_empty)
|
|
| .bar.layout.left = (.bar.layout.left | array_or_empty)
|
|
| .bar.layout.center = (.bar.layout.center | array_or_empty)
|
|
| .bar.layout.right = (.bar.layout.right | array_or_empty)
|
|
| .plugins = (.plugins | array_or_empty)
|
|
'
|
|
|
|
# Apply a jq program to the source file and atomically write the result to the
|
|
# user config, then refresh the running shell. Extra args after the program are
|
|
# forwarded to jq (e.g. --arg/--argjson).
|
|
_BAR_TMP=""
|
|
cleanup_bar_tmp() {
|
|
if [[ -n ${_BAR_TMP:-} ]]; then rm -f "$_BAR_TMP"; fi
|
|
}
|
|
trap cleanup_bar_tmp EXIT
|
|
|
|
commit() {
|
|
local program="$1"
|
|
shift
|
|
mkdir -p "$(dirname "$CONFIG_FILE")"
|
|
_BAR_TMP=$(mktemp)
|
|
if ! jq -S -e "$@" "$program" "$(source_file)" >"$_BAR_TMP"; then
|
|
rm -f "$_BAR_TMP"
|
|
_BAR_TMP=""
|
|
fail "could not update shell config"
|
|
fi
|
|
mv "$_BAR_TMP" "$CONFIG_FILE"
|
|
_BAR_TMP=""
|
|
refresh_shell_config
|
|
}
|
|
|
|
section_valid() {
|
|
[[ $1 =~ ^(left|center|right)$ ]]
|
|
}
|
|
|
|
validate_section() {
|
|
section_valid "$1" || fail "section must be left, center, or right"
|
|
}
|
|
|
|
validate_index() {
|
|
[[ $1 =~ ^[0-9]+$ ]] || fail "index must be a non-negative integer"
|
|
}
|
|
|
|
# --------------------------------------------------------------------- catalog
|
|
|
|
catalog_json() {
|
|
require_omarchy_path
|
|
require_command jq
|
|
omarchy-plugin-catalog
|
|
}
|
|
|
|
widget_is_known() {
|
|
local id="$1"
|
|
catalog_json | jq -e --arg id "$id" '
|
|
any(.[]; (.kinds | index("bar-widget")) and .id == $id)
|
|
' >/dev/null
|
|
}
|
|
|
|
current_bar_ids_json() {
|
|
jq -c '
|
|
def array_or_empty: if type == "array" then . else [] end;
|
|
def entry_id: if type == "object" then (.id // "" | tostring) else tostring end;
|
|
|
|
["left", "center", "right"] as $sections |
|
|
[ $sections[] as $section |
|
|
((.bar.layout[$section] // []) | array_or_empty)[] |
|
|
entry_id |
|
|
select(length > 0)
|
|
]
|
|
' "$(source_file)"
|
|
}
|
|
|
|
current_bar_entries_json() {
|
|
jq -c '
|
|
def array_or_empty: if type == "array" then . else [] end;
|
|
def entry_id($entry): if ($entry | type) == "object" then ($entry.id // "" | tostring) else ($entry | tostring) end;
|
|
|
|
["left", "center", "right"] as $sections |
|
|
[ $sections[] as $section |
|
|
((.bar.layout[$section] // []) | array_or_empty | to_entries[]) |
|
|
{ section: $section, index: .key, id: entry_id(.value) } |
|
|
select(.id | length > 0)
|
|
]
|
|
' "$(source_file)"
|
|
}
|
|
|
|
available_widgets_json() {
|
|
local include_all="$1"
|
|
local current_ids
|
|
current_ids=$(current_bar_ids_json)
|
|
|
|
catalog_json | jq --argjson currentIds "$current_ids" --argjson includeAll "$include_all" '
|
|
map(select((.kinds | index("bar-widget")) and .barWidgetPath != null))
|
|
| map({
|
|
id: .id,
|
|
name: (.barWidget.displayName // .name // .id),
|
|
description: (.barWidget.description // .description // ""),
|
|
category: (.barWidget.category // "Plugin"),
|
|
allowMultiple: ((.barWidget.allowMultiple // false) == true)
|
|
})
|
|
| unique_by(.id)
|
|
| map(. as $widget |
|
|
$widget + {
|
|
inBar: (($currentIds | index($widget.id)) != null),
|
|
addable: ($widget.allowMultiple or (($currentIds | index($widget.id)) == null))
|
|
})
|
|
| map(select($includeAll or .addable))
|
|
| sort_by(.category, .name, .id)
|
|
'
|
|
}
|
|
|
|
print_available_widgets() {
|
|
local include_all="false"
|
|
local json="false"
|
|
|
|
while (( $# > 0 )); do
|
|
case "$1" in
|
|
--all) include_all="true" ;;
|
|
--json) json="true" ;;
|
|
-h | --help) usage; exit 0 ;;
|
|
*) fail "unknown list option: $1" ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ $json == "true" ]]; then
|
|
available_widgets_json "$include_all"
|
|
return
|
|
fi
|
|
|
|
available_widgets_json "$include_all" | jq -r '
|
|
.[] |
|
|
[
|
|
.id,
|
|
(if .inBar and .allowMultiple then "addable*" elif .inBar then "in-bar" else "addable" end),
|
|
.category,
|
|
.name,
|
|
.description
|
|
] | @tsv
|
|
' | awk -F '\t' '
|
|
BEGIN { printf "%-28s %-10s %-14s %-22s %s\n", "ID", "STATUS", "CATEGORY", "NAME", "DESCRIPTION" }
|
|
{ printf "%-28s %-10s %-14s %-22s %s\n", $1, $2, $3, $4, $5 }
|
|
'
|
|
}
|
|
|
|
# ------------------------------------------------------------------- bar options
|
|
|
|
current_bar_option_id() {
|
|
jq -r '(.bar.id // "omarchy.bar") | tostring' "$(source_file)"
|
|
}
|
|
|
|
available_bar_options_json() {
|
|
local current_id
|
|
current_id=$(current_bar_option_id)
|
|
|
|
catalog_json | jq --arg currentId "$current_id" '
|
|
map(select((.kinds | index("bar")) and .barPath != null))
|
|
| map({
|
|
id: .id,
|
|
name: (.name // .id),
|
|
description: (.description // ""),
|
|
firstParty: .firstParty,
|
|
active: (.id == $currentId)
|
|
})
|
|
| unique_by(.id)
|
|
| sort_by((if .id == "omarchy.bar" then 0 else 1 end), .name, .id)
|
|
'
|
|
}
|
|
|
|
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() {
|
|
available_bar_options_json | jq -e --arg id "$1" 'any(.[]; .id == $id)' >/dev/null
|
|
}
|
|
|
|
# --------------------------------------------------------------------- placement
|
|
|
|
# Parse placement flags shared by add/move/remove/set. Sets the globals below.
|
|
PLACEMENT_SECTION=""
|
|
PLACEMENT_INDEX=""
|
|
PLACEMENT_BEFORE=""
|
|
PLACEMENT_AFTER=""
|
|
PLACEMENT_FROM_SECTION=""
|
|
PLACEMENT_FROM_INDEX=""
|
|
PLACEMENT_DUPLICATE="false"
|
|
PLACEMENT_ALL="false"
|
|
|
|
parse_placement() {
|
|
while (( $# > 0 )); do
|
|
case "$1" in
|
|
--section)
|
|
PLACEMENT_SECTION="${2:-}"
|
|
validate_section "$PLACEMENT_SECTION"
|
|
shift 2
|
|
;;
|
|
--index)
|
|
PLACEMENT_INDEX="${2:-}"
|
|
validate_index "$PLACEMENT_INDEX"
|
|
shift 2
|
|
;;
|
|
--before)
|
|
PLACEMENT_BEFORE="${2:-}"
|
|
[[ -n $PLACEMENT_BEFORE ]] || fail "--before requires a widget id"
|
|
shift 2
|
|
;;
|
|
--after)
|
|
PLACEMENT_AFTER="${2:-}"
|
|
[[ -n $PLACEMENT_AFTER ]] || fail "--after requires a widget id"
|
|
shift 2
|
|
;;
|
|
--from-section)
|
|
PLACEMENT_FROM_SECTION="${2:-}"
|
|
validate_section "$PLACEMENT_FROM_SECTION"
|
|
shift 2
|
|
;;
|
|
--from-index)
|
|
PLACEMENT_FROM_INDEX="${2:-}"
|
|
validate_index "$PLACEMENT_FROM_INDEX"
|
|
shift 2
|
|
;;
|
|
--duplicate)
|
|
PLACEMENT_DUPLICATE="true"
|
|
shift
|
|
;;
|
|
--all)
|
|
PLACEMENT_ALL="true"
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
fail "unknown option: $1"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
[[ -z $PLACEMENT_BEFORE || -z $PLACEMENT_AFTER ]] || fail "use only one of --before or --after"
|
|
}
|
|
|
|
# --------------------------------------------------------------- commands: inspect
|
|
|
|
cmd_show() {
|
|
(( $# == 0 )) || fail "show does not take arguments"
|
|
jq '.bar // {}' "$(source_file)"
|
|
}
|
|
|
|
cmd_layout() {
|
|
local json="false"
|
|
while (( $# > 0 )); do
|
|
case "$1" in
|
|
--json) json="true" ;;
|
|
-h | --help) usage; exit 0 ;;
|
|
*) fail "unknown layout option: $1" ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ $json == "true" ]]; then
|
|
jq '.bar.layout // { left: [], center: [], right: [] }' "$(source_file)"
|
|
return
|
|
fi
|
|
|
|
jq -r '
|
|
(.bar.layout // { left: [], center: [], right: [] }) as $layout |
|
|
["left", "center", "right"][] as $section |
|
|
($layout[$section] // []) as $entries |
|
|
if ($entries | length) == 0 then
|
|
"\($section)\t-\t-"
|
|
else
|
|
$entries | to_entries[] | "\($section)\t\(.key)\t\(.value.id // "<missing>")"
|
|
end
|
|
' "$(source_file)" | awk '
|
|
BEGIN { printf "%-8s %-5s %s\n", "SECTION", "INDEX", "WIDGET" }
|
|
{ printf "%-8s %-5s %s\n", $1, $2, $3 }
|
|
'
|
|
}
|
|
|
|
# --------------------------------------------------------------- commands: select
|
|
|
|
cmd_use() {
|
|
local plugin="${1:-}"
|
|
[[ -n $plugin ]] || fail "bar option id is required"
|
|
[[ $# -eq 1 ]] || fail "use takes a single bar option id"
|
|
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 bar options'"
|
|
|
|
if [[ $plugin == "omarchy.bar" ]]; then
|
|
commit "$JQ_DEFS $NORMALIZE | del(.bar.id)"
|
|
else
|
|
commit "$JQ_DEFS $NORMALIZE | .bar.id = \$plugin" --arg plugin "$plugin"
|
|
fi
|
|
echo "Using $plugin as the active bar"
|
|
}
|
|
|
|
cmd_reset() {
|
|
(( $# == 0 )) || fail "reset does not take arguments"
|
|
commit "$JQ_DEFS $NORMALIZE | del(.bar.id)"
|
|
echo "Reset to the built-in Omarchy bar"
|
|
}
|
|
|
|
# --------------------------------------------------------------- commands: edit
|
|
|
|
cmd_add() {
|
|
local id="${1:-}"
|
|
[[ -n $id ]] || fail "bar add requires a widget id"
|
|
shift
|
|
|
|
local positional_section=""
|
|
while (( $# > 0 )); do
|
|
if [[ $1 == --* ]]; then
|
|
break
|
|
fi
|
|
[[ -z $positional_section ]] || fail "unexpected argument: $1"
|
|
positional_section="$1"
|
|
shift
|
|
done
|
|
|
|
parse_placement "$@"
|
|
[[ -z $positional_section || -z $PLACEMENT_SECTION ]] || fail "specify a section positionally or with --section, not both"
|
|
[[ -z $positional_section || -z $PLACEMENT_INDEX ]] || fail "specify a section positionally or use --index, not both"
|
|
[[ -z $positional_section || -z $PLACEMENT_BEFORE ]] || fail "specify a section positionally or use --before, not both"
|
|
[[ -z $positional_section || -z $PLACEMENT_AFTER ]] || fail "specify a section positionally or use --after, not both"
|
|
[[ -z $positional_section || ( $PLACEMENT_DUPLICATE == false ) ]] || fail "--duplicate needs explicit placement flags"
|
|
if [[ -n $positional_section ]]; then
|
|
validate_section "$positional_section"
|
|
PLACEMENT_SECTION="$positional_section"
|
|
fi
|
|
|
|
require_omarchy_path
|
|
widget_is_known "$id" || fail "$id is not a known bar widget; run 'omarchy bar list' to see valid ids"
|
|
|
|
local explicit="false"
|
|
if [[ -n $PLACEMENT_INDEX || -n $PLACEMENT_BEFORE || -n $PLACEMENT_AFTER || $PLACEMENT_DUPLICATE == true ]]; then
|
|
explicit="true"
|
|
fi
|
|
|
|
if [[ $explicit == "false" ]]; then
|
|
# Anchor-based dedupe insert: remove any existing instance across sections,
|
|
# then insert after the section anchor (workspaces/weather/tray) — or append
|
|
# when the anchor is absent.
|
|
local default_section="${PLACEMENT_SECTION:-right}"
|
|
commit "$JQ_DEFS $NORMALIZE
|
|
| .bar.layout.left = (.bar.layout.left | map(select(entry_id != \$id)))
|
|
| .bar.layout.center = (.bar.layout.center | map(select(entry_id != \$id)))
|
|
| .bar.layout.right = (.bar.layout.right | map(select(entry_id != \$id)))
|
|
| (.bar.layout[\$section] | map(entry_id) | index(\$anchor)) as \$anchorIndex
|
|
| .bar.layout[\$section] = (
|
|
if \$anchorIndex == null then .bar.layout[\$section] + [{id: \$id}]
|
|
else .bar.layout[\$section][0:\$anchorIndex+1] + [{id: \$id}] + .bar.layout[\$section][\$anchorIndex+1:] end)
|
|
" \
|
|
--arg id "$id" \
|
|
--arg section "$default_section" \
|
|
--arg anchor "$(anchor_for_section "$default_section")"
|
|
else
|
|
local default_section="${PLACEMENT_SECTION:-right}"
|
|
local prog
|
|
prog=$(cat <<JQ
|
|
$JQ_DEFS $NORMALIZE
|
|
| (.bar.layout.left + .bar.layout.center + .bar.layout.right | map(entry_id) | index(\$id)) as \$existing
|
|
| if \$existing != null and \$allowDuplicate != "true" then
|
|
error(\$id + " is already in the bar; use 'bar move' or pass --duplicate")
|
|
else . end
|
|
| resolve_target(\$defaultSection) as \$target
|
|
| .bar.layout[\$target.section] = (.bar.layout[\$target.section][0:\$target.index] + [{id: \$id}] + .bar.layout[\$target.section][\$target.index:])
|
|
JQ
|
|
)
|
|
commit "$prog" \
|
|
--arg id "$id" \
|
|
--arg defaultSection "$default_section" \
|
|
--arg targetSection "$PLACEMENT_SECTION" \
|
|
--arg targetIndex "$PLACEMENT_INDEX" \
|
|
--arg before "$PLACEMENT_BEFORE" \
|
|
--arg after "$PLACEMENT_AFTER" \
|
|
--arg allowDuplicate "$PLACEMENT_DUPLICATE"
|
|
fi
|
|
echo "Added $id to the bar"
|
|
}
|
|
|
|
anchor_for_section() {
|
|
case "$1" in
|
|
left) printf 'omarchy.workspaces' ;;
|
|
center) printf 'omarchy.weather' ;;
|
|
right) printf 'omarchy.tray' ;;
|
|
*) fail "section must be left, center, or right" ;;
|
|
esac
|
|
}
|
|
|
|
cmd_move() {
|
|
local id="${1:-}"
|
|
[[ -n $id ]] || fail "bar move requires a widget id"
|
|
shift
|
|
parse_placement "$@"
|
|
|
|
local default_section="${PLACEMENT_SECTION:-}"
|
|
local prog
|
|
prog=$(cat <<JQ
|
|
$JQ_DEFS $NORMALIZE
|
|
| resolve_source as \$source
|
|
| .bar.layout[\$source.section] = (.bar.layout[\$source.section][0:\$source.index] + .bar.layout[\$source.section][\$source.index+1:])
|
|
| (\$defaultSection | if . == "" then \$source.section else . end) as \$fallback
|
|
| resolve_target(\$fallback) as \$target
|
|
| (\$source.entry | if type == "object" then .id = \$id else . end) as \$entry
|
|
| .bar.layout[\$target.section] = (.bar.layout[\$target.section][0:\$target.index] + [\$entry] + .bar.layout[\$target.section][\$target.index:])
|
|
JQ
|
|
)
|
|
commit "$prog" \
|
|
--arg id "$id" \
|
|
--arg defaultSection "$default_section" \
|
|
--arg targetSection "$PLACEMENT_SECTION" \
|
|
--arg targetIndex "$PLACEMENT_INDEX" \
|
|
--arg before "$PLACEMENT_BEFORE" \
|
|
--arg after "$PLACEMENT_AFTER" \
|
|
--arg fromSection "$PLACEMENT_FROM_SECTION" \
|
|
--arg fromIndex "$PLACEMENT_FROM_INDEX" \
|
|
--arg widgetId "$id"
|
|
echo "Moved $id"
|
|
}
|
|
|
|
cmd_remove() {
|
|
local id=""
|
|
if (( $# > 0 )) && [[ $1 != --* ]]; then
|
|
id="$1"
|
|
shift
|
|
fi
|
|
parse_placement "$@"
|
|
|
|
local prog
|
|
prog=$(cat <<JQ
|
|
$JQ_DEFS $NORMALIZE
|
|
| if \$fromIndex != "" then
|
|
resolve_source as \$source
|
|
| .bar.layout[\$source.section] = (.bar.layout[\$source.section][0:\$source.index] + .bar.layout[\$source.section][\$source.index+1:])
|
|
elif \$targetIndex != "" then
|
|
if \$targetSection == "" then error("--index requires --section for bar remove") else . end
|
|
| (\$targetIndex | tonumber) as \$index
|
|
| .bar.layout[\$targetSection][\$index] as \$entry
|
|
| if \$widgetId != "" and (\$entry | entry_id) != \$widgetId then
|
|
error("widget at " + \$targetSection + "[" + (\$index | tostring) + "] is not " + \$widgetId)
|
|
else . end
|
|
| .bar.layout[\$targetSection] = (.bar.layout[\$targetSection][0:\$index] + .bar.layout[\$targetSection][\$index+1:])
|
|
else
|
|
find_all(\$widgetId; \$targetSection) as \$matches
|
|
| if (\$matches | length) == 0 then
|
|
error("could not find widget " + \$widgetId + (\$targetSection | if . == "" then "" else " in " + . end))
|
|
elif \$removeAll != "true" then
|
|
(\$matches | sort_by(.section) | reverse | .[0]) as \$first
|
|
| .bar.layout[\$first.section] = (.bar.layout[\$first.section][0:\$first.index] + .bar.layout[\$first.section][\$first.index+1:])
|
|
else
|
|
reduce (\$matches | sort_by(.section) | reverse | .[]) as \$m (.;
|
|
.bar.layout[\$m.section] = (.bar.layout[\$m.section][0:\$m.index] + .bar.layout[\$m.section][\$m.index+1:]))
|
|
end
|
|
end
|
|
JQ
|
|
)
|
|
commit "$prog" \
|
|
--arg widgetId "$id" \
|
|
--arg targetSection "$PLACEMENT_SECTION" \
|
|
--arg targetIndex "$PLACEMENT_INDEX" \
|
|
--arg fromSection "$PLACEMENT_FROM_SECTION" \
|
|
--arg fromIndex "$PLACEMENT_FROM_INDEX" \
|
|
--arg removeAll "$PLACEMENT_ALL"
|
|
if [[ -n $id ]]; then
|
|
echo "Removed $id from the bar"
|
|
else
|
|
echo "Removed bar widget"
|
|
fi
|
|
}
|
|
|
|
cmd_set() {
|
|
local id="${1:-}"
|
|
local key="${2:-}"
|
|
local value="${3:-}"
|
|
[[ -n $id ]] || fail "bar set requires a widget id"
|
|
[[ -n $key ]] || fail "bar set requires a setting key"
|
|
(( $# >= 3 )) || fail "bar set requires a value"
|
|
shift 3
|
|
local value_is_json="false"
|
|
while (( $# > 0 )); do
|
|
case "$1" in
|
|
--json) value_is_json="true" ;;
|
|
-h | --help) usage; exit 0 ;;
|
|
*) break ;;
|
|
esac
|
|
shift
|
|
done
|
|
parse_placement "$@"
|
|
|
|
if [[ $value_is_json == "true" ]]; then
|
|
jq -e . <<<"$value" >/dev/null 2>&1 || fail "invalid JSON value: $value"
|
|
fi
|
|
|
|
local value_arg
|
|
if [[ $value_is_json == "true" ]]; then
|
|
value_arg=(--argjson value "$value")
|
|
else
|
|
value_arg=(--arg value "$value")
|
|
fi
|
|
|
|
local prog
|
|
prog=$(cat <<JQ
|
|
$JQ_DEFS $NORMALIZE
|
|
| if \$fromIndex != "" then
|
|
resolve_source as \$source
|
|
| .bar.layout[\$source.section][\$source.index] = (.bar.layout[\$source.section][\$source.index] | if type == "object" then .[\$key] = \$value else error("widget entry must be an object") end)
|
|
elif \$targetIndex != "" then
|
|
if \$targetSection == "" then error("--index requires --section for bar set") else . end
|
|
| (\$targetIndex | tonumber) as \$index
|
|
| .bar.layout[\$targetSection][\$index] as \$entry
|
|
| if (\$entry | entry_id) != \$widgetId then
|
|
error("widget at " + \$targetSection + "[" + (\$index | tostring) + "] is not " + \$widgetId)
|
|
elif \$entry | type != "object" then
|
|
error("widget entry must be an object")
|
|
else . end
|
|
| .bar.layout[\$targetSection][\$index][\$key] = \$value
|
|
else
|
|
find_all(\$widgetId; \$targetSection) as \$matches
|
|
| if (\$matches | length) == 0 then
|
|
error("could not find widget " + \$widgetId + (\$targetSection | if . == "" then "" else " in " + . end))
|
|
else
|
|
(\$matches | sort_by(.section) | .[0]) as \$first
|
|
| if \$first.entry | type != "object" then error("widget entry must be an object") else . end
|
|
| .bar.layout[\$first.section][\$first.index][\$key] = \$value
|
|
end
|
|
end
|
|
JQ
|
|
)
|
|
commit "$prog" \
|
|
--arg widgetId "$id" \
|
|
--arg key "$key" \
|
|
"${value_arg[@]}" \
|
|
--arg targetSection "$PLACEMENT_SECTION" \
|
|
--arg targetIndex "$PLACEMENT_INDEX" \
|
|
--arg fromSection "$PLACEMENT_FROM_SECTION" \
|
|
--arg fromIndex "$PLACEMENT_FROM_INDEX"
|
|
echo "Set $key on $id"
|
|
}
|
|
|
|
cmd_replace() {
|
|
local old="${1:-}"
|
|
local new="${2:-}"
|
|
[[ -n $old ]] || fail "bar replace requires the source widget id"
|
|
[[ -n $new ]] || fail "bar replace requires the replacement widget id"
|
|
[[ $# -eq 2 ]] || fail "bar replace takes two widget ids"
|
|
|
|
local prog
|
|
prog=$(cat <<JQ
|
|
$JQ_DEFS $NORMALIZE
|
|
| resolve_source as \$source
|
|
| if \$source.entry | type != "object" then error("widget entry must be an object") else . end
|
|
| .bar.layout[\$source.section][\$source.index].id = \$new
|
|
JQ
|
|
)
|
|
# resolve_source finds by widget id when no placement is given; replace
|
|
# takes no placement flags, so pass empty placement explicitly.
|
|
commit "$prog" \
|
|
--arg widgetId "$old" \
|
|
--arg new "$new" \
|
|
--arg fromSection "" \
|
|
--arg fromIndex ""
|
|
echo "Replaced $old with $new"
|
|
}
|
|
|
|
cmd_position() {
|
|
local position="${1:-}"
|
|
[[ -n $position ]] || fail "position is required"
|
|
[[ $# -eq 1 ]] || fail "position takes a single value"
|
|
[[ $position =~ ^(top|bottom|left|right)$ ]] || fail "position must be top, bottom, left, or right"
|
|
commit "$JQ_DEFS $NORMALIZE | .bar.position = \$position" --arg position "$position"
|
|
echo "Bar position set to $position"
|
|
}
|
|
|
|
cmd_transparent() {
|
|
local transparent="${1:-}"
|
|
[[ -n $transparent ]] || fail "transparent is required"
|
|
[[ $# -eq 1 ]] || fail "transparent takes a single value"
|
|
[[ $transparent =~ ^(true|false)$ ]] || fail "transparent must be true or false"
|
|
commit "$JQ_DEFS $NORMALIZE | .bar.transparent = \$transparent" --argjson transparent "$transparent"
|
|
echo "Bar transparency set to $transparent"
|
|
}
|
|
|
|
# --------------------------------------------------------------------- dispatch
|
|
|
|
command="${1:-}"
|
|
[[ $# -gt 0 ]] && shift || true
|
|
|
|
case "$command" in
|
|
show | current)
|
|
cmd_show "$@"
|
|
;;
|
|
layout)
|
|
cmd_layout "$@"
|
|
;;
|
|
list | available | widgets)
|
|
print_available_widgets "$@"
|
|
;;
|
|
options | option)
|
|
print_bar_options "$@"
|
|
;;
|
|
selected | active)
|
|
(( $# == 0 )) || fail "$command does not take arguments"
|
|
current_bar_option_id
|
|
;;
|
|
use)
|
|
cmd_use "$@"
|
|
;;
|
|
reset)
|
|
cmd_reset "$@"
|
|
;;
|
|
add)
|
|
cmd_add "$@"
|
|
;;
|
|
move)
|
|
cmd_move "$@"
|
|
;;
|
|
remove | rm | drop)
|
|
cmd_remove "$@"
|
|
;;
|
|
set)
|
|
cmd_set "$@"
|
|
;;
|
|
replace)
|
|
cmd_replace "$@"
|
|
;;
|
|
position)
|
|
cmd_position "$@"
|
|
;;
|
|
transparent)
|
|
cmd_transparent "$@"
|
|
;;
|
|
settings | edit)
|
|
(( $# == 0 )) || fail "settings does not take arguments"
|
|
exec omarchy-launch-bar-settings
|
|
;;
|
|
-h | --help | help | "")
|
|
usage
|
|
;;
|
|
*)
|
|
fail "unknown command: $command"
|
|
;;
|
|
esac
|