* Hide the keyboard layout widget on a single-layout install There is nothing to read or switch when only one layout is configured, so the label is noise on the bar most people have. Hide it until the keyboard reports more than one, and keep showing it on a Hyprland that doesn't report the list at all rather than hiding the widget everywhere. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Put the keyboard layout widget on the bar by default The widget hides itself unless the active keyboard has more than one layout, so shipping it costs a single-layout machine nothing and saves everyone else from finding it in the plugin list. Sit it just right of the clock, and add it to existing bars the way the agents widget was added, leaving a curated bar and a disabled widget alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Cycle the layout with the hyprctl command that exists switchxkblayout is a hyprctl command, not a dispatcher, so sending it over the dispatch socket only produced a Lua syntax error and clicking the widget did nothing. Run it instead, against the keyboard the label was read from. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Add an idempotent bar add command Nothing put a widget on the bar without going through the running shell: plugin enable and bar move both forward to it over IPC, which a migration cannot rely on. Add writes the config file the way position and transparent already do, and leaves a widget that is already on the bar where the user put it, so callers can ask for it repeatedly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Put the keyboard layout widget on bars through the bar CLI The hand-written jq was a normalizer, a presence check and a splice for what is now one command that carries all three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Keep bar add from writing a bar the shell was not reading The shell takes a user shell.json only when it parses, says version 1, and carries a bar layout, and does not deep-merge; anything else leaves the shipped defaults on screen. Reading and writing the user file regardless turned a config holding nothing but an idle timeout into a bar holding nothing but the new widget, and made an unparsable one abort the migration chain on every update. Work against whichever layout is actually in effect, seeding the defaults before placing a widget they do not already carry. A malformed hand-installed manifest fails the whole plugin catalog, which was enough to refuse a first-party widget, so treat an unreadable catalog as no answer rather than a no. Leave a widget listed in disabledPlugins off the bar instead of writing a layout entry the registry refuses to load, and re-check presence inside the mutation so two adds cannot both miss it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Read a widget's default bar section in one place cmd_defaults spelled out the same "defaultSection, or center when it is missing or not a section" rule that the add path already asks for by name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Rename bar add to bar put 'omarchy plugin add' installs a plugin and 'omarchy bar add' placed one that was already installed, which is too much meaning for one verb. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Place a newly added bar widget with bar put plugin add reached the bar through plugin enable, which forwards to the running shell, so it first had to poll until the shell noticed the clone and then failed outright when no shell was there to ask. Putting a widget on the bar is a config edit, so do that directly and leave plugin enable to the plugins that need registering rather than placing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Put bar widgets through the shell instead of the config file Placing a widget existed twice: once in PluginRegistry, which the shell uses and owns the config it holds in memory, and once as jq against shell.json. The second was there so migrations could run without a shell, which they do not need to: the Quattro upgrade hands over the shipped shell.json before it runs any, and every other path runs inside a session with a shell up. Ask the shell, and say so and carry on when there is none to ask. putBarWidget enables only what is not already on the bar, which is what a caller that cannot know whether it ran before needs, and is the one thing the existing enable path would not do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
370 lines
12 KiB
Bash
Executable File
370 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Configure the bar and its widget layout
|
|
# omarchy:group=bar
|
|
# omarchy:args=use <id> | reset | defaults | position <top|bottom|left|right> | transparent <true|false|toggle> | put <id> [placement] | move <id> [placement] | set <id> <key> <value> [--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 <<USAGE
|
|
Usage: omarchy bar <command> [args...]
|
|
|
|
use <id> 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 <top|bottom|left|right> Bar position
|
|
transparent <true|false|toggle> Bar transparency
|
|
put <id> [placement] Put a widget on the bar, leaving one
|
|
that is already there where it is
|
|
move <id> [placement] Move a widget within or between sections
|
|
set <id> <key> <value> [--json] [placement]
|
|
Set a per-widget option
|
|
|
|
Placement:
|
|
--section <left|center|right> Target section
|
|
--index <n> Target index
|
|
--before <id> Insert before a widget
|
|
--after <id> Insert after a widget
|
|
--from-section <section> Source section
|
|
--from-index <n> Source index
|
|
|
|
Enable and disable widgets with 'omarchy plugin enable' and
|
|
'omarchy plugin disable'.
|
|
|
|
'put' looks for --before / --after within the section it is adding to, and
|
|
falls back to the end of that section when the named widget is not there.
|
|
|
|
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"
|
|
}
|
|
|
|
# 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"
|
|
|
|
# Nothing to place into with no shell to place it, and a caller that runs
|
|
# unattended should say so and carry on rather than fail. A shell that is
|
|
# there and refuses is a real error.
|
|
if ! omarchy-shell shell ping >/dev/null 2>&1; then
|
|
echo "omarchy-shell is not running; $id was not put on the bar" >&2
|
|
return 0
|
|
fi
|
|
|
|
local result
|
|
result=$(omarchy-shell shell putBarWidget "$id" "$(placement_json)") ||
|
|
fail "could not put $id on the bar"
|
|
[[ $result != "unknown" ]] || fail "$id is not a known widget; run 'omarchy plugin list'"
|
|
[[ $result == "ok" ]] || fail "$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
|