Skip to content

Keep the launcher surface mapped: ~24 ms to first frame - #37

Open
JonasThowsen wants to merge 3 commits into
typecraft-dev:mainfrom
JonasThowsen:perf/persistent-surface
Open

JonasThowsen wants to merge 3 commits into
typecraft-dev:mainfrom
JonasThowsen:perf/persistent-surface

Conversation

@JonasThowsen

Copy link
Copy Markdown

Summary

Makes opening oma-cast and typing into it noticeably faster. Search results and rankings don't change.

  • Persistent surface. LauncherWindow is now mapped once and kept. Mapping a fresh PanelWindow on every open cost about 150 ms of scene-graph setup. When hidden, the window is 1×1 px with an empty input mask and no keyboard focus. When shown, it grows to cover the monitor and takes Exclusive focus. While hidden it follows Hyprland's focused monitor, so it's already in place when you open it.
  • Survives monitors going away. An output can disappear and come back under the same name, for example on lid close, suspend, or DPMS while locked. The window remaps when Quickshell.screens changes, so it doesn't get stuck on the dead screen, reporting "shown" but never drawing. I hit this on a laptop after a long lock.
  • In-place row updates. rebuild() now diffs against a plain rowMirror and only sets the rows that changed, instead of clear() + append on every keystroke. Selection lookups read the mirror instead of rows.get().
  • Ranker fast paths. These are memoized normalize/transliterate, per-candidate prepared text, a sparse fuzzy DP, a one-character shortcut, and a canSkip prefilter that RootSearch uses before decorating candidates. Scores and order are unchanged. The new tests/ranker-fast.test.js checks them against a dense reference DP. The new Ctrl+P root filter is applied in the prefiltered path too.
  • Diagnostics. The new lastFrameAt IPC function returns the time of the first frame after the last show or hide. tests/smoke.sh uses it to time the open path end to end.
  • Small fixes. oma-cast doctor no longer checks for socat, because omarchy-shell in 4.0.6 only uses qs ipc. The docs that described a socket fast path are corrected.

The branch merges current main and keeps the calculator, agent hand-off, vim/emacs bindings, argument pills, alerts, About panel and clipboard paste unchanged.

Numbers (Omarchy 4.0.6, Quickshell 0.3.2, Hyprland 0.56.2)

before after
show → first frame 59–66 ms (README) 24–41 ms
hide 24–42 ms 12–25 ms

Testing

  • node --test tests/*.test.js: 108/108 pass
  • tests/smoke.sh: all checks pass (doctor, IPC, root search, toggle latency under the 100 ms budget)
  • Checked calculator (5 ft to cm), search, and the commands filter through oma-cast call rootSearch.search
  • Not covered by an automated test: the monitor-dropout remap. The repro is to lock until the screen powers off, unlock, then open the launcher.

🤖 Generated with Claude Code

JonasThowsen and others added 3 commits October 21, 2026 09:57
Omarchy 4.0.4 removed ShellIpc and OverlayWindow, so the plugin no longer
loaded. Use Quickshell's IpcHandler and a PanelWindow instead.

Open: every show rebuilt the window's scene graph (~150 ms). The surface
is now mapped once and kept: 1x1 with no input or focus while hidden,
full monitor with exclusive focus when shown, following Hyprland's
focused monitor in the background. Hotkey to first frame: ~190 ms -> ~25 ms.

Typing: root search re-normalized every string and ran a quadratic DP on
each keystroke. Add string and per-candidate caches, a sparse DP, a
one-character fast path and a prefilter that skips candidates rank()
would drop; update the list model in place instead of clearing it.
Keystroke to frame: ~80-170 ms -> ~13-23 ms. Rankings are unchanged;
tests/ranker-fast.test.js checks the fast paths against a dense reference.

Also: smoke test times the hotkey path via the plugin's lastFrameAt,
doctor no longer checks for socat, and the unit-test command works on
Node 26.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An output that disappears and returns under the same name (lid close,
suspend, DPMS while locked) left the persistent surface on a dead
screen; it reported shown but never drew again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Upstream's calculator, agent hand-off, vim/emacs bindings, inline
arguments, alerts, About panel and clipboard paste are kept as they
are. On top of them:

- LauncherWindow stays a single surface that is mapped once and kept
  (the 150 ms map cost per open is gone), follows the focused monitor
  and remaps when outputs change; rows update in place via rowMirror.
- Ranker keeps the memoized/sparse fast paths and canSkip prefilter;
  RootSearch applies the new root filter in both the prefiltered query
  path and the empty root.
- lastFrameAt IPC for tests/smoke.sh; doctor no longer checks for
  socat, since omarchy-shell only uses qs ipc.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

1 participant