Ship the model usage widget in the default bar

The widget hid itself well enough that nobody found it: it only ever
appeared if you knew the id and ran `omarchy bar plugin add`, since it was
in neither the shipped layout nor any migration.

Defaulting it on costs nothing on machines that don't use it. Panel.qml is
`visible: providers.length > 0`, and Main.qml only counts a provider that
is enabled and has actually recorded prompts, sessions, active days, or a
rate limit. A box that has never run Claude Code or Codex draws an empty
bar item, and the icon arrives on its own at the first scan that finds
usage — which is the behavior the widget already advertised.

The migration skips any config that already lists the widget in any
section, so a curated bar keeps its own placement rather than gaining a
second copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-07-29 14:49:55 -04:00
co-authored by Claude Opus 5
parent 63e57e1bd5
commit 39936d2982
4 changed files with 176 additions and 0 deletions
+3
View File
@@ -38,6 +38,9 @@
{
"id": "omarchy.tray"
},
{
"id": "omarchy.model-usage"
},
{
"id": "omarchy.bluetooth"
},
+49
View File
@@ -0,0 +1,49 @@
echo "Add the model usage widget to the bar"
# The widget is now in the default layout. It draws nothing until a provider
# reports usage — Panel.qml is `visible: providers.length > 0`, and Main.qml
# only counts a provider that is enabled *and* has recorded prompts, sessions,
# active days, or a rate limit. So adding it to every existing bar is free:
# machines without Claude Code or Codex never see it.
config_file="$HOME/.config/omarchy/shell.json"
if [[ -s $config_file ]] && omarchy-cmd-present jq; then
tmp=$(mktemp)
jq '
def entry_id:
if type == "string" then
.
elif type == "object" then
(.id // "")
else
""
end;
def has_widget($id):
[(.bar.layout // {}) | to_entries[] | .value | select(type == "array") | .[] | entry_id]
| any(. == $id);
def insert_after($anchor; $entry):
if type != "array" then
[$entry]
else
([range(0; length) as $i | select((.[$i] | entry_id) == $anchor) | $i][0]) as $anchor_index |
if $anchor_index == null then
[$entry] + .
else
.[0:$anchor_index + 1] + [$entry] + .[$anchor_index + 1:]
end
end;
# Respect a bar the user already curated: only place the widget when it is
# absent from every section, never a second copy.
if has_widget("omarchy.model-usage") then
.
else
.bar.layout.right |= insert_after("omarchy.tray"; { id: "omarchy.model-usage" })
end
' "$config_file" >"$tmp" && mv "$tmp" "$config_file" || rm -f "$tmp"
fi
omarchy-restart-shell
+5
View File
@@ -29,6 +29,11 @@ there is no switch row at all; with none, the module leaves the bar entirely
rather than sitting there with nothing to say. A CLI installed mid-session
shows up at the next refresh, so nothing polls the disk waiting for it.
That self-hiding is why the widget ships in the default bar layout: a machine
that has never run Claude Code or Codex draws nothing, and the icon arrives on
its own the first time a scan finds usage. Drop it with
`omarchy bar plugin remove omarchy.model-usage`.
## Providers
| Provider | Limits | Local stats |
+119
View File
@@ -0,0 +1,119 @@
#!/bin/bash
set -euo pipefail
source "$(dirname "$0")/base-test.sh"
require_command jq
migration="$ROOT/migrations/1785344985.sh"
test_dir=$(mktemp -d)
trap 'rm -rf "$test_dir"' EXIT
mkdir -p "$test_dir/bin"
cat >"$test_dir/bin/omarchy-cmd-present" <<'STUB'
#!/bin/bash
command -v "$1" >/dev/null
STUB
cat >"$test_dir/bin/omarchy-restart-shell" <<'STUB'
#!/bin/bash
echo restart >>"$SHELL_RESTARTS"
STUB
chmod +x "$test_dir/bin/"*
export SHELL_RESTARTS="$test_dir/shell-restarts"
home="$test_dir/home"
config="$home/.config/omarchy/shell.json"
run_migration() {
: >"$SHELL_RESTARTS"
HOME="$home" PATH="$test_dir/bin:$PATH" bash -euo pipefail "$migration" >/dev/null
}
# The shipped default minus the widget is what every machine installed before
# this migration has on disk.
write_config() {
rm -rf "$home"
mkdir -p "$home/.config/omarchy"
jq "${1:-.}" "$ROOT/config/omarchy/shell.json" >"$config"
}
without_widget='del(.bar.layout[][] | select((if type == "object" then .id else . end) == "omarchy.model-usage"))'
ids() {
jq -c --arg section "$1" '[.bar.layout[$section][]? | if type == "object" then .id else . end]' "$config"
}
# ------------------------------------------------------------------ shipped default
jq -e '[.bar.layout.right[].id] | index("omarchy.model-usage")' "$ROOT/config/omarchy/shell.json" >/dev/null ||
fail "shipped config puts model usage in the bar"
pass "shipped config puts model usage in the bar"
# ------------------------------------------------------------------ placement
write_config "$without_widget"
run_migration
[[ $(ids right) == '["omarchy.tray","omarchy.model-usage","omarchy.bluetooth","omarchy.network","omarchy.audio","omarchy.monitor","omarchy.power"]' ]] ||
fail "migration inserts model usage after the tray" "$(ids right)"
pass "migration inserts model usage after the tray"
(($(wc -l <"$SHELL_RESTARTS") == 1)) || fail "migration restarts the shell"
pass "migration restarts the shell"
before=$(sha256sum "$config")
run_migration
[[ $before == $(sha256sum "$config") ]] || fail "migration is idempotent" "$(ids right)"
pass "migration is idempotent"
# ------------------------------------------------------------------ curated bars
# A user who already placed the widget keeps it exactly where they put it, in
# whichever section, and never gets a second copy.
write_config "$without_widget | .bar.layout.center += [{ id: \"omarchy.model-usage\" }]"
run_migration
[[ $(ids center) == *'"omarchy.model-usage"'* ]] || fail "migration leaves a user-placed widget alone" "$(ids center)"
[[ $(ids right) != *'"omarchy.model-usage"'* ]] || fail "migration does not add a second copy" "$(ids right)"
pass "migration respects a widget the user already placed"
# Layouts written before entries grew options are bare id strings.
write_config "$without_widget | .bar.layout.right = [\"omarchy.tray\", \"omarchy.model-usage\", \"omarchy.power\"]"
run_migration
[[ $(ids right) == '["omarchy.tray","omarchy.model-usage","omarchy.power"]' ]] ||
fail "migration reads string-form entries" "$(ids right)"
pass "migration reads string-form entries"
# A tray dropped from the right section must not strand the widget or drop it.
write_config "$without_widget | del(.bar.layout.right[] | select(.id == \"omarchy.tray\"))"
run_migration
[[ $(ids right) == '["omarchy.model-usage",'* ]] || fail "migration places the widget without a tray" "$(ids right)"
pass "migration places the widget without a tray"
# ------------------------------------------------------------------ everything else
write_config "$without_widget"
cp "$config" "$test_dir/before.json"
run_migration
diff <(jq -S 'del(.bar.layout.right)' "$test_dir/before.json") <(jq -S 'del(.bar.layout.right)' "$config") >/dev/null ||
fail "migration touches nothing but the right section" "$(diff <(jq -S . "$test_dir/before.json") <(jq -S . "$config"))"
pass "migration touches nothing but the right section"
# A config the migration cannot parse is left alone rather than truncated.
rm -rf "$home"
mkdir -p "$home/.config/omarchy"
printf '{ not json' >"$config"
run_migration
[[ $(cat "$config") == '{ not json' ]] || fail "migration leaves an unparsable config untouched" "$(cat "$config")"
pass "migration leaves an unparsable config untouched"