From 5ec8bd300bd1a98a3c7ffe0ed8430963bb5a2631 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 11 Oct 2026 16:10:11 +0900 Subject: [PATCH] Pick the bar clock format from Appearance > Menu Bar > Indicators > Clock Every preset the clock cycles through gets its own row, labeled with how it reads, and the current one is checked. omarchy-bar gains get and a bare position to read the settings the rows compare against. Co-Authored-By: Claude Opus 5.5 --- bin/omarchy-bar | 32 ++++++++++++++++++++++++++---- default/omarchy/omarchy-menu.jsonc | 15 ++++++++++++++ shell/plugins/menu/MenuModel.js | 3 +++ test/shell.d/menu-test.sh | 14 ++++++++++++- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/bin/omarchy-bar b/bin/omarchy-bar index 2a29c634..d8d99deb 100755 --- a/bin/omarchy-bar +++ b/bin/omarchy-bar @@ -2,7 +2,7 @@ # 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:args=use | reset | defaults | position [top|bottom|left|right] | transparent | put [placement] | move [placement] | set [--json] [placement] | get # 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 @@ -16,13 +16,15 @@ Usage: omarchy bar [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 + position [top|bottom|left|right] Bar position; prints the current one + when given none 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 + get Print a per-widget option Placement: --section Target section @@ -196,8 +198,12 @@ cmd_defaults() { } cmd_position() { - local position="${1:-}" - [[ -n $position ]] || fail "position is required" + if (( $# == 0 )); then + jq -r '.bar.position // "top"' "$(source_file)" + return + fi + + local position="$1" (( $# == 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" @@ -363,6 +369,21 @@ cmd_set() { echo "Set $key on $id" } +# Prints the option from the first instance of the widget on the bar, raw for a +# string and as JSON otherwise, and fails when no instance sets it. +cmd_get() { + local id="${1:-}" + local key="${2:-}" + [[ -n $id ]] || fail "get requires a widget id" + [[ -n $key ]] || fail "get requires a setting key" + (( $# == 2 )) || fail "get takes a widget id and a setting key" + + jq -e -r --arg id "$id" --arg key "$key" ' + first(.bar.layout[]?[]? | select(.id == $id) | .[$key] | select(. != null)) + | if type == "string" then . else tojson end + ' "$(source_file)" 2>/dev/null || exit 1 +} + # --------------------------------------------------------------------- dispatch command="${1:-}" @@ -394,6 +415,9 @@ case "$command" in set) cmd_set "$@" ;; + get) + cmd_get "$@" + ;; -h | --help | help | "") usage ;; diff --git a/default/omarchy/omarchy-menu.jsonc b/default/omarchy/omarchy-menu.jsonc index 276f7d25..b6c71f55 100644 --- a/default/omarchy/omarchy-menu.jsonc +++ b/default/omarchy/omarchy-menu.jsonc @@ -69,6 +69,21 @@ "trigger.appearance.top-bar.opaque": {"icon":"󰂵","label":"Opaque Bar","when":"jq -e .bar.transparent ~/.config/omarchy/shell.json >/dev/null 2>&1","action":"omarchy-bar transparent false"}, "trigger.appearance.top-bar.indicators": {"icon":"󰋽","label":"Indicators"}, "trigger.appearance.top-bar.indicators.battery-percentage": {"icon":"󰁹","label":"Battery Percentage","when":"omarchy-hw-laptop","action":"omarchy-shell omarchy.power togglePercentage"}, + "trigger.appearance.top-bar.indicators.clock": {"icon":"󰥔","label":"Clock"}, + "trigger.appearance.top-bar.indicators.clock.sunday-24h": {"icon":"󰥔","label":"Sunday 14:30","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"dddd HH:mm\" ]]","action":"omarchy-bar set omarchy.clock format \"dddd HH:mm\""}, + "trigger.appearance.top-bar.indicators.clock.sunday-12h": {"icon":"󰥔","label":"Sunday 2:30 PM","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"dddd h:mm AP\" ]]","action":"omarchy-bar set omarchy.clock format \"dddd h:mm AP\""}, + "trigger.appearance.top-bar.indicators.clock.sunday-seconds-24h": {"icon":"󰥔","label":"Sunday 14:30:05","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"dddd HH:mm:ss\" ]]","action":"omarchy-bar set omarchy.clock format \"dddd HH:mm:ss\""}, + "trigger.appearance.top-bar.indicators.clock.sunday-seconds-12h": {"icon":"󰥔","label":"Sunday 2:30:05 PM","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"dddd h:mm:ss AP\" ]]","action":"omarchy-bar set omarchy.clock format \"dddd h:mm:ss AP\""}, + "trigger.appearance.top-bar.indicators.clock.time-24h": {"icon":"󰥔","label":"14:30","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"HH:mm\" ]]","action":"omarchy-bar set omarchy.clock format \"HH:mm\""}, + "trigger.appearance.top-bar.indicators.clock.time-12h": {"icon":"󰥔","label":"2:30 PM","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"h:mm AP\" ]]","action":"omarchy-bar set omarchy.clock format \"h:mm AP\""}, + "trigger.appearance.top-bar.indicators.clock.date-24h": {"icon":"󰥔","label":"Sun 3 May 14:30","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"ddd d MMM HH:mm\" ]]","action":"omarchy-bar set omarchy.clock format \"ddd d MMM HH:mm\""}, + "trigger.appearance.top-bar.indicators.clock.date-12h": {"icon":"󰥔","label":"Sun 3 May 2:30 PM","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"ddd d MMM h:mm AP\" ]]","action":"omarchy-bar set omarchy.clock format \"ddd d MMM h:mm AP\""}, + "trigger.appearance.top-bar.indicators.clock.week": {"icon":"󰥔","label":"3 May W18 2026","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"d MMMM 'W'ww yyyy\" ]]","action":"omarchy-bar set omarchy.clock format \"d MMMM 'W'ww yyyy\""}, + "trigger.appearance.top-bar.indicators.clock.iso": {"icon":"󰥔","label":"2026-05-03 14:30","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"yyyy-MM-dd HH:mm\" ]]","action":"omarchy-bar set omarchy.clock format \"yyyy-MM-dd HH:mm\""}, + "trigger.appearance.top-bar.indicators.clock.stacked-24h": {"icon":"󰥔","label":"14 — 30","when":"[[ $(omarchy-bar position) == \"left\" || $(omarchy-bar position) == \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock verticalFormat) == $'HH\\n—\\nmm' ]]","action":"omarchy-bar set omarchy.clock verticalFormat $'HH\\n—\\nmm'"}, + "trigger.appearance.top-bar.indicators.clock.stacked-12h": {"icon":"󰥔","label":"2 — 30 PM","when":"[[ $(omarchy-bar position) == \"left\" || $(omarchy-bar position) == \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock verticalFormat) == $'h\\n—\\nmm\\nAP' ]]","action":"omarchy-bar set omarchy.clock verticalFormat $'h\\n—\\nmm\\nAP'"}, + "trigger.appearance.top-bar.indicators.clock.stacked-date": {"icon":"󰥔","label":"03 May W18 '26","when":"[[ $(omarchy-bar position) == \"left\" || $(omarchy-bar position) == \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock verticalFormat) == $'dd\\nMMM\\n\\'W\\'ww\\n\\'\\'yy' ]]","action":"omarchy-bar set omarchy.clock verticalFormat $'dd\\nMMM\\n\\'W\\'ww\\n\\'\\'yy'"}, + "trigger.appearance.top-bar.indicators.clock.stacked-time": {"icon":"󰥔","label":"14 30","when":"[[ $(omarchy-bar position) == \"left\" || $(omarchy-bar position) == \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock verticalFormat) == $'HH\\nmm' ]]","action":"omarchy-bar set omarchy.clock verticalFormat $'HH\\nmm'"}, "trigger.appearance.top-bar.position.top": {"icon":"󰁝","label":"Top Bar","action":"omarchy-bar position top"}, "trigger.appearance.top-bar.position.bottom": {"icon":"󰁅","label":"Bottom Bar","action":"omarchy-bar position bottom"}, "trigger.appearance.top-bar.position.left": {"icon":"󰁍","label":"Left Bar","action":"omarchy-bar position left"}, diff --git a/shell/plugins/menu/MenuModel.js b/shell/plugins/menu/MenuModel.js index a3a0b5c3..74507c85 100644 --- a/shell/plugins/menu/MenuModel.js +++ b/shell/plugins/menu/MenuModel.js @@ -393,6 +393,9 @@ function displayRow(items, itemOrder, checkedResults, disabledResults, entry, de // cached while one expression runs lives in that subshell only, so a lazy // memo never survives to the expression after it. var GUARD_READERS = [ + "omarchy-bar get omarchy.clock format", + "omarchy-bar get omarchy.clock verticalFormat", + "omarchy-bar position", "omarchy-channel-current", "omarchy-default-agent", "omarchy-default-browser", diff --git a/test/shell.d/menu-test.sh b/test/shell.d/menu-test.sh index a49a89c2..1630c930 100644 --- a/test/shell.d/menu-test.sh +++ b/test/shell.d/menu-test.sh @@ -392,9 +392,21 @@ assert( ) assertDeepEqual( defaultItems.filter(item => item.parent === 'trigger.appearance.top-bar.indicators').map(item => item.label), - ['Battery Percentage'], + ['Battery Percentage', 'Clock'], 'menu gathers bar indicator options under Menu Bar > Indicators' ) +const clockModel = requireFromRoot('shell/plugins/panels/clock/Model.js') +const clockRows = defaultItems.filter(item => item.parent === 'trigger.appearance.top-bar.indicators.clock') +// Each row hands bash its format, so ask bash what it hands over. +const clockRowFormats = key => clockRows + .filter(item => item.action.startsWith(`omarchy-bar set omarchy.clock ${key} `)) + .map(item => require('child_process').execFileSync('bash', ['-c', item.action.replace(`omarchy-bar set omarchy.clock ${key} `, 'printf %s ')], { encoding: 'utf8' })) +assertDeepEqual(clockRowFormats('format'), clockModel.clockFormats(false), 'menu spells out every horizontal clock format') +assertDeepEqual(clockRowFormats('verticalFormat'), clockModel.clockFormats(true), 'menu spells out every vertical clock format') +assert( + clockRows.every(item => item.checked && item.checked.includes('$(omarchy-bar get omarchy.clock ') && item.when.includes('$(omarchy-bar position)')), + 'menu checks the current clock format and offers only the formats for the bar orientation' +) assertDeepEqual( ['top', 'bottom', 'left', 'right'].map(position => defaultById[`trigger.appearance.top-bar.position.${position}`].label), ['Top Bar', 'Bottom Bar', 'Left Bar', 'Right Bar'],