How the Omarchy Menu's `checked` Field Wires to Shell Conditions
The checked: field in Omarchy's JSONC menu definition contains a Bash expression that gets evaluated once per menu opening, with the Boolean result stored in checkedResults and rendered as a "✓" glyph via MenuModel.labelFor().
The Omarchy desktop environment uses a dynamic menu system where check-marks reflect real-time shell state. This article explains how the checked field in menu definitions connects to underlying shell conditions, tracing the full execution pipeline from JSONC configuration to QML rendering.
Where Menu checked Fields Are Defined
Menu entries live in default/omarchy/omarchy-menu.jsonc. Each entry can specify a checked: property containing arbitrary Bash code.
The syntax is straightforward:
"setup.default.browser.firefox": {
"icon": "",
"label": "Firefox",
"checked": "[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]",
"action": "omarchy-default-browser firefox"
}
When this menu opens, the Firefox entry displays a check-mark only if omarchy-default-browser currently returns "firefox".
How MenuModel.js Processes checked Guards
The shell/plugins/menu/MenuModel.js module orchestrates evaluation. It collects guards, builds a Bash script, executes it, and maps results for UI consumption.
Step 1: Collect Guard Expressions
Lines 85-88 scan the parsed menu entries and accumulate checked: expressions (alongside when: and disabled:):
if (entry.checked) guards += guardLine(ids[i], "c", entry.checked)
The "c" tag identifies this as a checked guard versus "d" for disabled or "w" for when.
Step 2: Build the Guard Evaluation Script
The guardLine function (lines 69-71) generates Bash that runs each expression in a subshell:
function guardLine(id, tag, expression) {
return "if { " + substituteGuardReaders(expression) +
"; } >/dev/null 2>&1; then echo " + id + ":" + tag + ":1; " +
"else echo " + id + ":" + tag + ":0; fi\n"
}
This produces deterministic output: <id>:<tag>:1 for true, <id>:<tag>:0 for false.
Step 3: Execute and Parse Results
The combined guardScript runs once. Output lines populate two maps:
root.checkedResults— id → Boolean forcheckedguardsroot.disabledResults— id → Boolean fordisabledguards
This single batch evaluation minimizes shell fork overhead during menu rendering.
Step 4: Generate Display Labels with labelFor
The labelFor helper (lines 84-88) appends "✓" when checkedResults shows true:
function labelFor(entry, checkedResults, disabledResults) {
var marked = (entry.checked && checkedResults && checkedResults[entry.id]) ||
isDisabled(disabledResults, entry);
return marked ? entry.label + " ✓" : entry.label;
}
The check-mark is textual (Unicode U+2713), not a separate icon or widget state.
Complete Execution Example
Given the Firefox browser entry above, the generated guard script looks like:
# Prelude: cache expensive readers
__omarchy_read_2=$(omarchy-default-browser 2>/dev/null) || :
# Guard evaluation for checked state
if { [[ "$__omarchy_read_2" == "firefox" ]] ; } >/dev/null 2>&1; then
echo setup.default.browser.firefox:c:1
else
echo setup.default.browser.firefox:c:0
fi
The substituteGuardReaders() call replaces $(omarchy-default-browser) with the cached variable, eliminating redundant process spawns.
Parsed result stored in JavaScript:
root.checkedResults = {
"setup.default.browser.firefox": true // "Firefox ✓" displayed
}
QML binding for the menu row:
label: MenuModel.labelFor(entry, root.checkedResults, root.disabledResults)
Performance and Caching Characteristics
| Aspect | Implementation |
|---|---|
| Evaluation timing | Once per menu open, not per-frame |
| Reader caching | substituteGuardReaders() collapses duplicate $() calls |
| Output parsing | Line-oriented, delimiter-separated (:) |
| UI update | Reactive binding to root.checkedResults |
This design keeps menu opening latency low even with dozens of checked conditions.
Key Source Files
| File | Responsibility |
|---|---|
default/omarchy/omarchy-menu.jsonc |
JSONC menu definitions with checked: expressions |
shell/plugins/menu/MenuModel.js |
Guard collection, script generation, result parsing, labelFor() |
shell/plugins/menu/Menu.qml |
QML container consuming checkedResults for rendering |
Summary
checked:contains Bash expressions evaluated at menu-open timeMenuModel.jsbatches all guards into one script executionguardLine()generates tagged output (:c:1or:c:0)checkedResultsmap stores Boolean outcomes by entry idlabelFor()appends "✓" based oncheckedResults[entry.id]- The pipeline is single-batch, cached-reader, reactive-bound for performance
Frequently Asked Questions
What happens if a checked expression fails or returns non-zero?
The guardLine wrapper redirects stderr to /dev/null and treats non-zero exit as false (:0). The menu entry simply appears unchecked; no error propagates to the UI.
Can checked expressions reference environment variables or functions?
Yes. The guard script runs with full shell context, so exported variables, functions from ~/.bashrc, and Omarchy's own shell library are all available.
Why use textual "✓" instead of a QML checked property?
The Omarchy menu uses a unified list widget where styling (icons, labels, shortcuts) is controlled through labelFor(). Embedding the check-mark in the label string keeps the rendering pipeline simple and consistent across entry types.
How do I debug a checked expression that behaves unexpectedly?
Run the generated guard script manually. Extract it from MenuModel.js logging or temporarily add set -x to see actual values. The cached reader variables (like __omarchy_read_2) are visible in the generated output.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →