#!/bin/bash # omarchy:summary=Configure the bar and its widget layout # omarchy:group=bar # omarchy:args=use | reset | defaults | position | transparent | put [placement] | move [placement] | set [--json] [placement] # omarchy:examples=omarchy bar use local.neon-bar | omarchy bar put omarchy.keyboard-layout --after omarchy.clock | omarchy bar move omarchy.clock --section center --index 0 | omarchy bar set omarchy.clock format HH:mm set -euo pipefail source omarchy-shell-config usage() { cat < [args...] use Use a bar option as the active bar reset Return to the built-in Omarchy bar defaults Restore the default bar and service widgets position Bar position transparent Bar transparency put [placement] Put a widget on the bar, leaving one that is already there where it is move [placement] Move a widget within or between sections set [--json] [placement] Set a per-widget option Placement: --section Target section --index Target index --before Insert before a widget --after Insert after a widget --from-section
Source section --from-index Source index Enable and disable widgets with 'omarchy plugin enable' and 'omarchy plugin disable'. 'put' places a widget the way 'plugin enable' does, but leaves one that is already on the bar where it is, and falls back to the widget's usual spot when --before / --after names a widget the bar does not carry. Examples: omarchy bar use local.neon-bar omarchy bar put omarchy.keyboard-layout --after omarchy.clock omarchy bar move omarchy.media left omarchy bar move omarchy.clock --section center --index 0 omarchy bar set omarchy.clock format HH:mm USAGE } # ------------------------------------------------------------------ validation bar_option_exists() { omarchy-plugin-catalog | jq -e --arg id "$1" ' any(.[]; (.kinds | index("bar")) and .barPath != null and .id == $id) ' >/dev/null } validate_section() { [[ $1 =~ ^(left|center|right)$ ]] || fail "section must be left, center, or right" } validate_index() { [[ $1 =~ ^[0-9]+$ ]] || fail "index must be a non-negative integer" } # --------------------------------------------------------------------- placement PLACEMENT_SECTION="" PLACEMENT_INDEX="" PLACEMENT_BEFORE="" PLACEMENT_AFTER="" PLACEMENT_FROM_SECTION="" PLACEMENT_FROM_INDEX="" 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 ;; -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" } placement_json() { jq -cn \ --arg section "$PLACEMENT_SECTION" \ --arg index "$PLACEMENT_INDEX" \ --arg before "$PLACEMENT_BEFORE" \ --arg after "$PLACEMENT_AFTER" \ --arg fromSection "$PLACEMENT_FROM_SECTION" \ --arg fromIndex "$PLACEMENT_FROM_INDEX" ' {} + (if $section == "" then {} else {section: $section} end) + (if $index == "" then {} else {index: ($index | tonumber)} end) + (if $before == "" then {} else {before: $before} end) + (if $after == "" then {} else {after: $after} end) + (if $fromSection == "" then {} else {fromSection: $fromSection} end) + (if $fromIndex == "" then {} else {fromIndex: ($fromIndex | tonumber)} end) ' } # -------------------------------------------------------------------- commands cmd_use() { local plugin="${1:-}" [[ -n $plugin ]] || fail "bar option id is required" (( $# == 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 plugin list'" if [[ $plugin == "omarchy.bar" ]]; then commit "$NORMALIZE | del(.bar.id)" else commit "$NORMALIZE | .bar.id = \$plugin" --arg plugin "$plugin" fi echo "Using $plugin as the active bar" } cmd_defaults() { (( $# == 0 )) || fail "defaults does not take arguments" local optional_widgets="[]" local service widget for service in dropbox tailscale; do if "omarchy-installed-service-$service"; then widget=$(jq -cn \ --arg id "omarchy.$service" \ --arg section "$(bar_widget_default_section "omarchy.$service")" \ '{id: $id, section: $section}') optional_widgets=$(jq -c --argjson widget "$widget" '. + [$widget]' <<<"$optional_widgets") fi done # This remains one file mutation so it also works during the headless # Quattro upgrade and cannot race the shell's in-memory config. commit "$NORMALIZE | .bar = \$defaults[0].bar | 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]; reduce \$widgets[] as \$widget (.; .bar.layout.left = (.bar.layout.left | map(select(entry_id != \$widget.id))) | .bar.layout.center = (.bar.layout.center | map(select(entry_id != \$widget.id))) | .bar.layout.right = (.bar.layout.right | map(select(entry_id != \$widget.id))) | (.bar.layout[\$widget.section] | map(entry_id) | index(anchor_for(\$widget.section))) as \$anchor | (\$anchor | if . == null then (.bar.layout[\$widget.section] | length) else . + 1 end) as \$index | .bar.layout[\$widget.section] = ( .bar.layout[\$widget.section][0:\$index] + [{id: \$widget.id}] + .bar.layout[\$widget.section][\$index:] ) ) " \ --slurpfile defaults "$DEFAULTS_FILE" \ --argjson widgets "$optional_widgets" echo "Restored the default Omarchy bar" } cmd_position() { local position="${1:-}" [[ -n $position ]] || fail "position is required" (( $# == 1 )) || fail "position takes a single value" [[ $position =~ ^(top|bottom|left|right)$ ]] || fail "position must be top, bottom, left, or right" commit "$NORMALIZE | .bar.position = \$position" --arg position "$position" echo "Bar position set to $position" } cmd_transparent() { local transparent="${1:-}" [[ -n $transparent ]] || fail "transparent is required" (( $# == 1 )) || fail "transparent takes a single value" [[ $transparent =~ ^(true|false|toggle)$ ]] || fail "transparent must be true, false, or toggle" if [[ $transparent == "toggle" ]]; then commit "$NORMALIZE | .bar.transparent = (.bar.transparent != true)" echo "Bar transparency toggled" else commit "$NORMALIZE | .bar.transparent = \$transparent" --argjson transparent "$transparent" echo "Bar transparency set to $transparent" fi } bar_widget_default_section() { local catalog catalog=$(omarchy-plugin-catalog 2>/dev/null) || { echo "center" return 0 } jq -r --arg id "$1" ' map(select(.id == $id))[0].barWidget.defaultSection // "center" | if IN("left", "center", "right") then . else "center" end ' <<<"$catalog" } # Asks the shell to place a widget, waiting out one still coming up. Answers 0 # with the reply in PUT_RESULT, 1 when there was no shell to ask. Only an # absent shell is carried on from: omarchy-migrate marks a 0 return as done. PUT_RESULT="" SHELL_ANSWERED=0 ask_to_put() { local id="$1" placement="$2" attempt absent=0 for (( attempt = 0; attempt < ${OMARCHY_SHELL_READY_ATTEMPTS:-50}; attempt++ )); do if PUT_RESULT=$(omarchy-shell shell putBarWidget "$id" "$placement" 2>&1); then SHELL_ANSWERED=1 [[ $PUT_RESULT == "not ready" ]] || return 0 elif [[ $PUT_RESULT == *"not ready"* ]]; then SHELL_ANSWERED=1 elif [[ $PUT_RESULT == *"is not running"* ]]; then # Answered once and now gone: it stopped mid-request. if (( SHELL_ANSWERED )); then fail "omarchy-shell did not become ready; $id was not put on the bar" fi # A shell being spawned has no socket yet, and nothing says a launch is # under way, so give one a few seconds to turn up. if (( ++absent >= ${OMARCHY_SHELL_ABSENT_ATTEMPTS:-30} )); then echo "omarchy-shell is not running; $id was not put on the bar" >&2 return 1 fi else fail "could not put $id on the bar: $PUT_RESULT" fi sleep 0.1 done fail "omarchy-shell did not become ready; $id was not put on the bar" } # Placement lives in the shell, which owns the config it has in memory. Putting # a widget therefore asks the shell rather than editing the file behind it. cmd_put() { local id="${1:-}" [[ -n $id ]] || fail "put requires a widget id" shift local positional_section="" if (( $# > 0 )) && [[ $1 != --* ]]; then positional_section="$1" validate_section "$positional_section" shift fi parse_placement "$@" [[ -z $positional_section || -z $PLACEMENT_SECTION ]] || fail "specify a section positionally or with --section, not both" [[ -z $positional_section ]] || PLACEMENT_SECTION="$positional_section" [[ -z $PLACEMENT_FROM_SECTION && -z $PLACEMENT_FROM_INDEX ]] || fail "put does not accept --from-section or --from-index" local placement placement=$(placement_json) ask_to_put "$id" "$placement" || return 0 # An update runs migrations before it restarts the shell, so this one can # predate the fallback. Ask it again without the neighbour it cannot find. if [[ $PUT_RESULT == "could not find target widget"* ]]; then PLACEMENT_BEFORE="" PLACEMENT_AFTER="" ask_to_put "$id" "$(placement_json)" || return 0 fi [[ $PUT_RESULT != "unknown" ]] || fail "$id is not a known widget; run 'omarchy plugin list'" [[ $PUT_RESULT == "ok" ]] || fail "$PUT_RESULT" # Says nothing about whether it had to be placed: a widget already on the bar # is left where it is, and both outcomes are the same answer to the caller. echo "$id is on the bar" } cmd_move() { local id="${1:-}" [[ -n $id ]] || fail "move requires a widget id" shift local positional_section="" if (( $# > 0 )) && [[ $1 != --* ]]; then positional_section="$1" validate_section "$positional_section" shift fi 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_SECTION="$positional_section" local result result=$(omarchy-shell shell moveBarWidget "$id" "$(placement_json)") [[ $result == "ok" ]] || fail "$result" echo "Moved $id" } cmd_set() { local id="${1:-}" local key="${2:-}" local value="${3:-}" [[ -n $id ]] || fail "set requires a widget id" [[ -n $key ]] || fail "set requires a setting key" (( $# >= 3 )) || fail "set requires a value" shift 3 local value_is_json="false" if (( $# > 0 )) && [[ $1 == "--json" ]]; then value_is_json="true" shift fi parse_placement "$@" [[ -z $PLACEMENT_BEFORE && -z $PLACEMENT_AFTER ]] || fail "set does not accept --before or --after" local value_json if [[ $value_is_json == "true" ]]; then value_json=$(jq -cn --argjson value "$value" '$value') || fail "invalid JSON value: $value" else value_json=$(jq -cn --arg value "$value" '$value') fi local result result=$(omarchy-shell shell setBarWidget "$id" "$key" "$value_json" "$(placement_json)") [[ $result == "ok" ]] || fail "$result" echo "Set $key on $id" } # --------------------------------------------------------------------- dispatch command="${1:-}" (( $# > 0 )) && shift || true case "$command" in use) cmd_use "$@" ;; reset) (( $# == 0 )) || fail "reset does not take arguments" cmd_use omarchy.bar ;; defaults) cmd_defaults "$@" ;; position) cmd_position "$@" ;; transparent) cmd_transparent "$@" ;; put) cmd_put "$@" ;; move) cmd_move "$@" ;; set) cmd_set "$@" ;; -h | --help | help | "") usage ;; *) fail "unknown command: $command" ;; esac