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.tray"
}, },
{
"id": "omarchy.model-usage"
},
{ {
"id": "omarchy.bluetooth" "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 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. 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 ## Providers
| Provider | Limits | Local stats | | 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"