* Unify input methods with the installer keyboard selection * Preserve inherited input keys and later keyboard changes * Exercise input engines across desktop toolkits * Show input modes in the keyboard indicator * Keep Pinyin setup prompts visible * Remember input selections before an app gains focus * Remove the Japanese input welcome notification * Show explicit choices on notification cards * Suppress the Cloudpinyin first-use prompt * Show the input switching shortcut in keyboard hints * Verify input setup and composition stay quiet * Use Super+I for input switching independently of Fcitx keys * Restore linked input settings and attribute test notifications to Fcitx * Correct the input setup shortcut hint * Keep input tooltips to the name and switching shortcut * Place the input language selector with the right bar widgets * Show input switching only for multiple layouts or methods * Unify input method and keyboard layout switching * Select keyboard layouts and input methods in the Typing menu * Apply typing selections immediately from the menu * Use installer keyboard choices and keep typing selections accurate * Queue typing changes and wait for saved selections * Keep queued typing selections when reopening the menu * Cycle keyboard layouts alongside composition engines * Avoid shadowing Python typing and retain pending mixed input selections * Keep input clicks working through controller and compositor failures * Move the keyboard layout widget to the right side of existing bars Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Map keyboard choices to the layouts the installer sets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the switching order when changing input methods Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Set input method fonts only after the selection saves Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Restore confirmed picker checks when an immediate change fails Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Route Super+I through the shell's input reader so unfocused choices stick Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Restore the input group when the font preference cannot be written Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drop the azerty conversion now that the installer no longer offers it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Apply the owner's keyboard layout once before persisting the input choice A quattro merge kept a second apply_keyboard after the retry loop, which re-ran loadkeys and firstboot, ignored their failure, and overwrote the XKB layout setup supplies for keymaps kbd-model-map lacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give the Lao keyboard choice a Lao desktop layout The row used la-latin1, kbd's Latin American Spanish keymap, so Lao installs typed Spanish. There is no Lao console keymap; like Korean, the console stays US and the desktop gets la, which Omarchy keeps behind US as a non-Latin layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Load engines installed after Fcitx started when they are selected The Typing menu refused an engine whose package an update had installed, since Fcitx discovers engines only at startup. Restart input once and wait for the engine; name the package when it really is not installed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cycle input methods back with Super+Shift+I Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drop notification action buttons now that the Pinyin prompt is suppressed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cycle input only through the shell's reader Without the shell there is no bar or menu either, so the one-shot cycling path only duplicated the reader's logic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add engines from the command line through the Typing menu's path omarchy setup input <engine> had its own restart, append, select and live-rollback code in configure.py, parallel to the menu's. It now installs the packages and appends through typing_setup, leaving configure.py with install-time seeding and update defaults. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep Ctrl+Space out of Fcitx's toggles instead of probing for it The terminal prefix bind read the Fcitx config and profile on every press to avoid fighting a Ctrl+Space toggle. configure --defaults now removes that key from every trigger list, so the bind only checks for a terminal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the input-method scripts with the system Python A bare python resolves through PATH, so a mise, pyenv or venv interpreter could run them without the system's PyGObject. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
175 lines
9.9 KiB
Markdown
175 lines
9.9 KiB
Markdown
# The Omarchy menu
|
|
|
|
The menu is the `omarchy.menu` plugin of the Quickshell desktop, with its
|
|
content defined as data in `default/omarchy/omarchy-menu.jsonc` (read at
|
|
runtime from `$OMARCHY_PATH`) and overlaid by the user's
|
|
`~/.config/omarchy/extensions/omarchy-menu.jsonc`. The shell parses both files
|
|
at startup and watches them for changes, so the keybind → IPC → visible path
|
|
never shells out to parse anything, and edits to either file take effect
|
|
without restarting the shell. Rendering and behavior live in
|
|
`shell/plugins/menu/Menu.qml`; the pure logic lives in
|
|
`shell/plugins/menu/MenuModel.js`, which is plain JavaScript that Node can
|
|
also load — the shell tests in `test/shell.d/menu-test.sh` and
|
|
`menu-guards-test.sh` exercise it directly.
|
|
|
|
JSONC here means JSON plus comments and trailing commas, stripped by the
|
|
parser rather than a real JSONC grammar: only whole-line `//` comments are
|
|
removed, so an inline trailing comment breaks the parse. A file that fails to
|
|
parse contributes no entries — a broken user extension silently drops every
|
|
user entry while the shipped menu keeps working.
|
|
|
|
## Entry schema
|
|
|
|
Entries are object keys. The dotted id is the tree: `trigger.share.file` is a
|
|
child of `trigger.share`, and an id with no dot sits on the root menu. There
|
|
is no separate parent field to keep in sync — where an entry appears follows
|
|
from what it is called (an explicit `parent` is accepted but nothing shipped
|
|
uses one).
|
|
|
|
Kind is inferred rather than declared: an entry with `action` is an action,
|
|
one with `target` is a link to another submenu, and anything else is a
|
|
submenu. The fields:
|
|
|
|
| Field | Meaning |
|
|
|---|---|
|
|
| `icon` | Glyph in the icon column (usually Nerd Font) |
|
|
| `iconFont` | Font family for the glyph when it differs from the menu font — how the private `omarchy` font's brand glyphs render |
|
|
| `label` | Visible row title; defaults to the id |
|
|
| `title` | Header text when the submenu is open; defaults to `label`. Lets a row read "Browser" under Defaults while the open menu says "Default Browser" |
|
|
| `action` | Shell command to run, detached, when selected |
|
|
| `target` | Existing submenu id to open; makes the row a link |
|
|
| `provider` | Runtime row source for this submenu (see Providers) |
|
|
| `aliases` | Alternate `omarchy menu summon <name>` routes; also searchable |
|
|
| `description` | Subtitle shown while searching, and extra search text matched by whole word |
|
|
| `when` / `checked` / `disabled` | Shell conditions (see Guards) |
|
|
|
|
Do not add `aliases` to new entries. They are reserved for established
|
|
alternate names users already type (`power-menu`, `settings`), kept for
|
|
compatibility — see `AGENTS.md`. Search does not need them: labels, the last
|
|
id segment, and descriptions are all searchable.
|
|
|
|
## Load and merge
|
|
|
|
`mergeMenuSources` overlays user entries on the defaults per key: reusing a
|
|
shipped id replaces only the fields you declare, so an extension can retitle
|
|
or re-icon a row without re-declaring its action, and an overridden entry
|
|
keeps its original position in the list. New ids append. A `root` entry is
|
|
injected if neither file declares one.
|
|
|
|
The sample extension at `config/omarchy/extensions/omarchy-menu.jsonc`
|
|
(refreshed into `~/.config/`) documents the format in its header and ships
|
|
only comments, so the default state adds nothing.
|
|
|
|
## Guards
|
|
|
|
`when`, `checked`, and `disabled` are bash conditions. The shell never
|
|
evaluates them on the open path: all guards in the menu are batched into a
|
|
single bash process per (re)load and per open, reporting `<id>:<w|c|d>:<0|1>`
|
|
lines. The menu opens immediately on the previous evaluation's answers, so
|
|
the batch's runtime is exactly how long a row can contradict the state it
|
|
describes — which is why the batch works hard to be fast:
|
|
|
|
- Package and command presence (`omarchy-pkg-present` and friends) are
|
|
answered in-process from one `pacman -Q` snapshot instead of a fork per
|
|
row. The snapshot resolves provides too, so gvim answers for vim.
|
|
- Commands that several rows read a value from — every Defaults > Browser row
|
|
compares against `$(omarchy-default-browser)` — run once, with the captured
|
|
answer substituted into each expression. The reader list is
|
|
`GUARD_READERS` in `MenuModel.js`; a new `$(omarchy-...)` reader used by
|
|
more than one row must be added there, and `menu-guards-test.sh` fails the
|
|
build if it is not.
|
|
|
|
The three guards differ in what failure means:
|
|
|
|
- `when` hides the row when it fails. A submenu whose visible descendants
|
|
are all hidden disappears with them (provider-backed submenus stay, since
|
|
their rows load on demand).
|
|
- `checked` appends ✓ when it succeeds — the "this is the current choice"
|
|
marker on defaults, DNS, channel rows.
|
|
- `disabled` keeps the row listed but dims it, marks it ✓, and makes it
|
|
unselectable: cursor, pointer, and Enter all step over it, and search omits
|
|
it. The Install submenus use it so software already on the machine reads as
|
|
installed rather than vanishing from the list it was installed from — the
|
|
list stays a catalog of what Omarchy can install. Since a dimmed row means
|
|
"you already have this", it earns the same ✓ as `checked` does elsewhere.
|
|
|
|
Install rows should therefore carry `disabled:` with the presence check, not
|
|
`when:`; Remove rows are the opposite, hiding via `when:` what is not there
|
|
to remove. `menu-test.sh` enforces the Install side of this convention.
|
|
|
|
## Providers
|
|
|
|
A submenu with `provider: "name"` gets its rows at runtime instead of from
|
|
JSONC. The names are defined by the shell, not the menu file — an extension
|
|
can point a submenu at an existing provider but cannot declare a new one:
|
|
|
|
- `apps` is QML-native: rows come from the shared AppLibrary (desktop
|
|
entries), carrying image icons, launch feedback, and uninstall support like
|
|
the launcher. App rows are searchable by their desktop Keywords but never
|
|
routable, so an installed app cannot capture a menu route (htop ships
|
|
`Keywords=system;...`, and SUPER+ESCAPE must still open the system menu).
|
|
- `fonts` and `power-profiles` are bash one-liners in the `providers` map in
|
|
`Menu.qml`. The contract is one tab-delimited line per row:
|
|
`label\tvalue\tcurrent`. The row whose value equals `current` gets the ✓
|
|
icon, and selection runs the spec's `actionFor(value)`. Row ids are
|
|
`<menuId>.<slugify(value)>`, with a `-` appended on collision so two values
|
|
that slugify alike cannot silently drop a row.
|
|
|
|
A provider marked `volatile` re-runs every time its submenu is entered — a
|
|
font installed since the shell started shows up without a restart — but not
|
|
on search keystrokes, which would restart the same enumeration per key.
|
|
|
|
`swapProviderRows` in `MenuModel.js` merges the results: rows carry the id of
|
|
the submenu that produced them, so a provider that runs again drops its
|
|
previous batch without disturbing static children declared in JSONC. Both it
|
|
and the app merge return fresh item maps for the caller to assign in one go —
|
|
writing into a map held by a QML `var` property occasionally loses the write,
|
|
which used to duplicate launcher rows. Never mutate `root.items` in place.
|
|
|
|
Adding a provider means adding an entry to the `providers` map in `Menu.qml`
|
|
(script, icon, `actionFor`, optionally `volatile`) and pointing a submenu at
|
|
it with `provider:`.
|
|
|
|
## Driving the menu from the CLI
|
|
|
|
`bin/omarchy-menu` is a thin wrapper over the standard plugin IPC surface:
|
|
|
|
```bash
|
|
omarchy menu # toggle the root menu
|
|
omarchy menu toggle system # open at a route, or close if already open
|
|
omarchy menu summon style.theme # always open (no close-if-visible)
|
|
omarchy menu close
|
|
omarchy menu refresh # re-parse the JSONC files
|
|
omarchy menu ping
|
|
```
|
|
|
|
A route is an item id or a declared alias, case-insensitive, with
|
|
underscores normalized to dashes. An exact id beats any alias; empty input,
|
|
`go`, and `menu` mean root; an unknown string falls through as a literal id
|
|
so a misspelling still attempts to open that id. Summoning a route that
|
|
resolves to an action — an alias for a leaf, like `screenrecord-stop` — runs
|
|
the action directly instead of opening an action with no children, and a
|
|
link is followed to its target. The default Hyprland bindings in
|
|
`default/hypr/bindings/utilities.lua` all go through this surface
|
|
(SUPER+SPACE toggles root, SUPER+ESCAPE the system menu, and so on).
|
|
|
|
## Select and input modes
|
|
|
|
The same plugin doubles as the system's dmenu. `omarchy-menu-select` and
|
|
`omarchy-menu-input` summon it with a `mode: select` or `mode: input`
|
|
payload, then block on a tempfile handshake: the shell writes the selection
|
|
to `selectionFile` and touches `doneFile`, and cancellation (empty
|
|
selection) exits 1. A select option is `label`, `glyph\tlabel`, or
|
|
`glyph\tlabel\tsubtext` — the glyph shows but never returns, the subtext
|
|
renders under the label, filters with it, and comes back as
|
|
`label\tsubtext` so callers with same-named rows get a stable key. This is
|
|
how the pickers behind menu actions (`omarchy-menu-plugin`,
|
|
`omarchy-menu-timezone`, ...) present lists without owning any UI.
|
|
|
|
|
|
Pass `-- --multiple` to `omarchy-menu-select` for a multi-select request. Repeat `--selected <value>` to preselect returned values (labels plus any subtext, without icons). Clicking or pressing Enter toggles an option without closing the menu; Apply returns a JSON array in option order, including `[]` when all choices are deselected. Cancellation still exits 1. Selections survive filtering.
|
|
|
|
With `--on-change <JSON command array>`, each toggle runs that command with the proposed selection JSON appended as its final argument, and no Apply row is shown. The checkbox changes only after the command succeeds; a failed command retains the previous selection. Changes persist when the menu closes. The typing selectors use this mode.
|
|
|
|
For immediate multi-select requests, `--change-key <key>` identifies the setting being changed across picker openings. The menu keeps queued selections for that key when reopened, so a new toggle cannot discard a change still being saved. Without a key, the command array identifies the setting.
|