Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/omarchy/shell.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"position": "top",
"transparent": false,
"centerAnchor": "omarchy.clock",
"trayMenuMaxHeight": 420,
"layout": {
"left": [
{
Expand Down
6 changes: 6 additions & 0 deletions docs/omarchy-shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ string on a miss.
"position": "top",
"transparent": false,
"centerAnchor": "omarchy.clock",
"trayMenuMaxHeight": 420,
"layout": {
"left": [ { "id": "omarchy.menu" } ],
"center": [ { "id": "omarchy.clock", "format": "HH:mm" } ],
Expand Down Expand Up @@ -163,6 +164,11 @@ Rules:
7. `idle.screensaver` and `idle.lock` are seconds since user idle began.
8. `version: 1` is required.

Set `bar.trayMenuMaxHeight` to a positive integer to control the tray menu's
preferred maximum height in theme-scaled units. The default is `420`. Invalid
or missing values use the default, and the popup still clamps to the usable
monitor area.

`config/omarchy/shell.json` describes the fresh-install state. When no
user `shell.json` exists, defaults are used verbatim. Once the user
customizes, `shell.json` is canonical — there is no deep-merge.
Expand Down
6 changes: 6 additions & 0 deletions manual/05-the-top-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ All of it is stored in `~/.config/omarchy/shell.json`, under the `bar` key. Here
}
```

The tray menu defaults to a maximum height of `420` theme-scaled units. To
make a tall tray menu show more entries before scrolling, add
`"trayMenuMaxHeight": 600` under `bar` in `~/.config/omarchy/shell.json`.
The value must be a positive integer; missing or invalid values use the
default, and the menu still stays within the available monitor space.

Every widget is one entry in one of the three layout arrays, and its settings sit inline on that entry — there's no separate settings file and no `config` sub-object. The clock's `format`, `formatAlt` (what right-click cycles to), and `verticalFormat` all live right there on `{ "id": "omarchy.clock" }`.

`centerAnchor` names the one center widget that gets pinned to the exact center of the screen, with the others flanking it. That's how the clock stays dead center even as the weather and update badge come and go. Set it to an empty string and the center list is just centered as a group instead.
Expand Down
1 change: 1 addition & 0 deletions shell/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ becomes the authoritative file — we do **not** deep-merge defaults back in.
"position": "top",
"transparent": false,
"centerAnchor": "omarchy.clock",
"trayMenuMaxHeight": 420,
"layout": {
"left": [ { "id": "omarchy.menu" }, { "id": "omarchy.workspaces" } ],
"center": [ { "id": "omarchy.clock", "format": "HH:mm" } ],
Expand Down
6 changes: 5 additions & 1 deletion shell/plugins/bar/widgets/Tray.qml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ BarWidget {
property var activeTrayAnchor: null
readonly property color foreground: bar ? bar.foreground : Color.foreground
readonly property string fontFamily: bar ? bar.fontFamily : Style.font.family
readonly property int trayMenuMaxHeight: {
var value = bar && bar.barConfig ? bar.barConfig.trayMenuMaxHeight : undefined
return typeof value === "number" && isFinite(value) && value > 0 && Math.floor(value) === value ? value : 420
}
readonly property var pinnedIds: settings.pinned instanceof Array ? settings.pinned : []
readonly property var hiddenIds: settings.hidden instanceof Array ? settings.hidden : []
readonly property var pinnedItems: bucket("pinned")
Expand Down Expand Up @@ -534,7 +538,7 @@ BarWidget {
padding: Style.space(8)
borderColor: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.45)
contentWidth: trayMenuPopup.fittedContentWidth(Style.space(232))
contentHeight: trayMenuPopup.fittedContentHeight(menuHeaderHeight + trayMenuColumn.implicitHeight, Style.space(420))
contentHeight: trayMenuPopup.fittedContentHeight(menuHeaderHeight + trayMenuColumn.implicitHeight, Style.space(root.trayMenuMaxHeight))

// Column skips invisible children but keeps reporting their height, so
// read the header's extent through its own visibility.
Expand Down
13 changes: 13 additions & 0 deletions test/shell.d/bar-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ run_node_test <<'JS'
const fs = require('fs')
const bar = requireFromRoot('shell/plugins/bar/BarModel.js')
const barSource = fs.readFileSync(root + '/shell/plugins/bar/Bar.qml', 'utf8')
const traySource = fs.readFileSync(root + '/shell/plugins/bar/widgets/Tray.qml', 'utf8')
const shellSource = fs.readFileSync(root + '/shell/shell.qml', 'utf8')

assert(/function toggleBarTransparency\(\): string \{[\s\S]*?shell\.bar\.toggleTransparency\(\)/.test(shellSource), 'shell exposes the bar transparency toggle over IPC')
Expand Down Expand Up @@ -286,6 +287,18 @@ assert(

assertEqual(bar.normalizePosition('left'), 'left', 'bar accepts valid positions')
assertEqual(bar.normalizePosition('sideways'), 'top', 'bar defaults invalid positions')
assert(
/readonly property int trayMenuMaxHeight: \{[\s\S]*?typeof value === "number" && isFinite\(value\) && value > 0 && Math\.floor\(value\) === value \? value : 420/.test(traySource),
'tray menu height falls back for missing and invalid values'
)
assert(
/var value = bar && bar\.barConfig \? bar\.barConfig\.trayMenuMaxHeight : undefined/.test(traySource),
'tray menu height reads the bar shell configuration'
)
assert(
/fittedContentHeight\(menuHeaderHeight \+ trayMenuColumn\.implicitHeight, Style\.space\(root\.trayMenuMaxHeight\)\)/.test(traySource),
'tray popup uses the configured height cap'
)
assertDeepEqual(bar.entrySettings({ id: 'omarchy.clock', format: 'HH:mm' }), { format: 'HH:mm' }, 'bar extracts entry settings')
assertEqual(bar.entryId({ id: 'omarchy.clock' }), 'omarchy.clock', 'bar extracts object entry ids')
assertEqual(bar.entryId('omarchy.clock'), 'omarchy.clock', 'bar extracts string entry ids')
Expand Down
3 changes: 3 additions & 0 deletions test/shell.d/config-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pass "default shell.json is valid JSON"
jq -e '.version == 1 and (.bar.layout.left | type == "array") and (.bar.layout.center | type == "array") and (.bar.layout.right | type == "array")' "$ROOT/config/omarchy/shell.json" >/dev/null
pass "default shell.json has versioned bar layout"

jq -e '(.bar.trayMenuMaxHeight | type) == "number" and .bar.trayMenuMaxHeight > 0 and (.bar.trayMenuMaxHeight | floor) == .bar.trayMenuMaxHeight' "$ROOT/config/omarchy/shell.json" >/dev/null
pass "default shell.json has a positive integer tray menu height"

# Pinning the whole row made this fail every time an unrelated widget moved,
# so assert the adjacency the name is about and let the rest of the row change.
jq -e '
Expand Down