Skip to content

feat: Omarchy's app grid behind the Omarchy button (Omarchy 4.5) - #73

Merged
thepathless merged 1 commit into
thepathless:experimentalfrom
priard:feat/app-grid
Oct 20, 2026
Merged

thepathless merged 1 commit into
thepathless:experimentalfrom
priard:feat/app-grid

Conversation

@priard

@priard priard commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Why

Omarchy 4.5 ships an app grid with its own dock: every app on one searchable, keyboard-driven grid, with Keep in dock and Uninstall in its menu, and apps can be dragged out of it onto the dock. OmaDock's Omarchy button can only open the Omarchy menu.

What

A new Settings → Appearance → Omarchy button opens choice: Omarchy menu (default, unchanged) or App grid. It is offered only when $OMARCHY_PATH/shell/plugins/dock/AppGrid.qml and shell/services/AppLibrary.qml exist (feature detection, so Omarchy 4.0 sees no change). Config key appsButtonAction ("menu" / "grid"), documented in the README.

With the grid, an app dragged over the dock on the grid's monitor reveals the dock (autohide included) and shows the drop line in the pinned run; letting go pins the app there, or moves it there when it is pinned already. One pinned inside a group stays in its group; a drop above the dock does nothing.

How

  • Nothing is copied. components/AppGridHost.qml (one for all docks, as the grid opens on the focused monitor) loads Omarchy's AppGrid.qml and AppLibrary.qml with a Loader on the first open, so the grid keeps Omarchy's look, search ranking and translations, and costs nothing until used. LocalAppLibrary could not stand in: it has no search (sortedEntries ignores the query and returns entries, not rows) and no remove. If either file fails to load, the button opens the menu.
  • The grid asks its dock for six functions; AppGridHost provides them: launchEntry through launchApp, isPinned / setPinned through the dock's pins, and moveExternalDrag / dropExternalDrag / endExternalDrag. The drag point is mapped into the pinned row and placed with the existing rowInsertIndex; dropRowIndex draws the usual drop line. The grid draws its own dragged icon, so the gap is the line rather than a full slot.
  • Where the app lands is pure logic in DockGridDrop.js (tests/unit/griddrop.test.mjs). An app that is not installed goes through togglePin, which refuses it and says so.
  • Docks reach the host through sharedState.appGrid, so Dock.qml is untouched (it sits at its ceiling).
  • The file check and the reads after omarchyPath changes are deferred a turn, because a Process command bound to the path may not have caught up with it yet.

Tested

  • Omarchy 4.5.0rc1, Hyprland 0.56.2, one monitor, through temporary IPC calls (removed): the grid opens (in the session's language), a drag point mid-row opens the line between the right two apps, a drop pins foot there, a second drop moves it to the front, a drop above the dock changes nothing.
  • node --test, structure-check.py, security-grep.py, smoke-test.sh.
  • Not yet tested by hand: a real mouse drag out of the grid, Uninstall from the grid's menu, multi-monitor.

Overlaps #72 only in ci.yml (both add a node --check), the README options table and SettingsSearch.js; keep both sides.

Omarchy 4.5 ships an app grid with its own dock: every app on one
searchable grid, keyboard driven, with Keep in dock and Uninstall, from
which an app can be dragged onto the dock. A new Appearance choice,
"Omarchy button opens": the Omarchy menu (default, as before) or the
app grid, offered only when AppGrid.qml and AppLibrary.qml exist.

The grid and its library are Omarchy's files, loaded on the first open
instead of copied, so they follow Omarchy's look, search ranking and
translations, and cost nothing until used. LocalAppLibrary could not
stand in: it has no search and no remove. If either file fails to load
the button opens the menu.

The grid asks its dock for six functions. AppGridHost provides them for
all docks: launch through launchApp, pin state through dock.json, and
the drags: over a dock on the grid's monitor the dock reveals and its
pinned run shows the drop line at rowInsertIndex; letting go pins the
app there, or moves it when it is pinned already. The grid draws its own
dragged icon, so the gap is the line, not a full slot.
priard added a commit to priard/omadock that referenced this pull request Oct 20, 2026
Omarchy's app grid behind the Omarchy button is open upstream as thepathless#73
and runs in priard.
thepathless pushed a commit that referenced this pull request Oct 20, 2026
Omarchy 4.5 adds a shell-wide display language (omarchy language) and
Commons.Translations for plugins: flat catalogs in i18n/, the language
followed live. This prepares the dock for it and ships Polish.

components/strings/Tr.qml is a singleton that creates
Commons.Translations by name, as a shell from before 4.5 has no such
type and naming it outright would stop the dock loading there. Without
it every call returns the English text it carries, so 4.0 is unchanged.

Settings rows translate their own texts from their search key
(setting.<key>.label / .hint / .option.<value>) and SectionLabel its
heading (section.<slug>), so the pages only touch rows whose text
changes at run time. The settings search matches the translated label
as well as the English one and shows the hit in the dock's language.
Menus, tooltips, popups, notifications and new group and preset names
go through Tr.t / Tr.tn. JS modules take the translator as an argument
(DockTasks.chipLabel). Dock.qml gains no line: notifyAppMissing takes a
reason and the logic module words it.

i18n/en.json holds every key; tests/unit/i18n.test.mjs checks that each
Tr.t call has its English entry with the same text, and that every
catalog uses only en.json's keys with the same placeholders. The Polish
catalog also covers the Floating workspaces (#72) and Omarchy button
opens (#73) rows.
@thepathless
thepathless merged commit df0ff91 into thepathless:experimental Oct 20, 2026
5 checks passed
@thepathless

Copy link
Copy Markdown
Owner

Merged into experimental as of da2def4 — thank you, Lukasz! 🎉

The "nothing is copied" design is the standout here: loading Omarchy's AppGrid.qml/AppLibrary.qml through Loaders on first open keeps Omarchy's look, search ranking and translations for free, and the cost-until-used property matters in a zero-CPU dock. And the honest fallback chain (file check → Loader.Error → the menu) means an Omarchy refactor degrades gracefully instead of leaving a dead button.

Also worth naming: DockGridDrop.js as pure logic with the pinned-inside-a-group case handled (from < 0 && pinned → the group keeps it) — that's the kind of rule that gets lost when it lives inside signal handlers. AppGridHost reaching the docks through sharedState.appGrid kept Dock.qml at its ceiling, which I know took extra care.

Verified on experimental against 268 Node + 116 Python unit tests (griddrop.test.mjs included), qmllint 0 errors, structure/security/manifest gates, smoke-test.sh PASSED, and the live probe suites green. Only ci.yml conflicted with #72 (both adding a node --check) — kept both sides, plus closed the pre-existing gap for Buttons.js/Plan.js/SettingsSearch.js while I was in there.

Shipped in v4.8.0.

@thepathless thepathless left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Late protocol approval: verified on experimental against 268 Node + 116 Python unit tests, qmllint 0 errors, all static gates, smoke (incl. --restart) PASSED, and the live probe suites green. The app-grid flow's drop logic is pinned by griddrop.test.mjs; the Omarchy button opens row renders in the settings panel (confirmed on the Appearance page capture).

@thepathless

Copy link
Copy Markdown
Owner

Correction to my review note above: the "Omarchy button opens" row itself was not visually confirmed — it sits below the settings panel's hard height cap (Style.space(600)) and no reachable scroll path let me bring it into a captured frame. What the captures do show: the Appearance page renders correctly down to the fold (every row above it reads cleanly, same SettingRow component the new row uses), and the #72 row on the Behavior page was confirmed in full. The row's wiring is covered by griddrop.test.mjs and the live grid behaviour; the one thing still owed is a human glance at the row on screen.

@thepathless

Copy link
Copy Markdown
Owner

Update — the visual proof is now complete, the earlier gap is closed. The "Omarchy button opens" row was captured whole on a probe dock (headless output, disposable config copy): label, hint ("The app grid searches every app and lets you drag one onto the dock to pin it."), and both option pills read back from the pixels — "Omarchy menu" (selected) and "App grid". What made the difference: the row's visible gate needs sharedState.appGrid.available, which comes from omarchyPath — set by the real shell's plugin loader, so a bare DockHost in a probe must inject it, and the settings panel's height cap (Style.space(600)) only admits a fixed number of rows, so the rows above were collapsed in the disposable copy rather than scrolling. Both #72 and #73 rows are visually verified now; the owner's desktop and config were untouched throughout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants