Skip to content

Prewarm the history panel after launch - #18

Merged
mobrava merged 1 commit into
mainfrom
fix/panel-first-open-latency
Aug 29, 2026
Merged

Prewarm the history panel after launch#18
mobrava merged 1 commit into
mainfrom
fix/panel-first-open-latency

Conversation

@mobrava

@mobrava mobrava commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Problem

After quitting and relaunching, the panel appeared to need three presses of Cmd+Shift+V.

Measured with os_log timestamps on a real launch:

35.632  hotkey fired  -> isVisible=false -> show
35.802  cold render done (fetchCount 5.6ms, hosting 50ms, layout 76ms = 168ms total)
        ... 250ms reveal animation starts here
36.026  hotkey fired  -> isVisible=true  -> hide   (panel was still sliding in)
36.538  hotkey fired  -> show (13ms, warm) -> finally visible

Every press registered correctly, so this was never a hotkey problem. The panel window is transparent until SwiftUI renders its first frame, so for the first ~170ms nothing was on screen. Total time to a visible panel was about 420ms, longer than the interval between two presses, so the second press cancelled the reveal.

Fix

Render the panel once off screen 300ms after launch (alphaValue = 0, ordered front, laid out, ordered out), so the first press only pays for the animation.

Verification

Rebuilt and tested on macOS with nine real key presses:

30.842  prewarm-end (147ms, does not block the main thread)
31.913  fired -> isVisible=false
31.974  panel ordered in    <- 61ms, down from 168ms
33.127  fired -> isVisible=true -> closes
33.982  fired -> opens

First press opens it, and repeated presses alternate open/close correctly.

An earlier attempt also added a guard that ignored toggles during the reveal animation. That was dropped: with the prewarm in place the panel is visible within ~60ms, so the guard only made the panel refuse to close on a fast second press.

The first Cmd+Shift+V after a relaunch had to build the panel and its
NSHostingView from scratch. Measured on a real launch, that took 140-180ms
before the 250ms reveal animation could even start. The panel window is
transparent until SwiftUI renders its first frame, so nothing was on screen
during that window: a second press landed mid-reveal, read isVisible as true,
and cancelled the reveal. The panel only appeared to open on the third press.

Render the panel once off screen 300ms after launch so the first press only
pays for the animation. Same measurement after the change: 61ms from hotkey
to the panel being ordered in, down from 168ms.
@mobrava
mobrava merged commit c4edf40 into main Aug 29, 2026
1 check passed
@mobrava
mobrava deleted the fix/panel-first-open-latency branch August 29, 2026 02:20
cfprabhu21 added a commit to cfprabhu21/Clipbara that referenced this pull request Aug 29, 2026
Completes the set: tags, prompt library, ⌘1–9, sequential paste, and now the
Markdown transformation.

- ClipTransformation protocol + TransformationRegistry, so the clip menu is
  driven by a registry rather than a growing switch — the shape the
  architecture notes call for, with markdown as the first entry.
- swift-markdown (pre-approved in the working agreements) parses; a MarkupWalker
  builds an NSAttributedString so nesting comes out right — a link inside a list
  item inside a quote. Regex substitution is exactly what loses fenced code
  blocks and tables, which is why the roadmap named the dependency.
- Both RTF and a plain-string fallback go on the pasteboard, so apps that cannot
  take rich text still receive usable text rather than nothing. A test round
  trips the RTF back through NSAttributedString to prove nothing is lost.
- Tables become tab-separated rows with a bold header: RTF tables are patchily
  supported, while tabs paste usefully everywhere including spreadsheets.
- Detection is conservative and tested from both sides — offering "Paste as Rich
  Text" on prose it would not change makes the menu look broken, so "a - b - c"
  and "2 * 3 * 4" must not match.

Two Swift Regex limits shaped the detector, both noted in the code: there is no
lookbehind, and regex literals each carry their own output type so they cannot
share an array — hence patterns compiled from strings.

174 tests pass (20 new). Both schemes build; string catalog at 192 keys, 0 stale.
Roadmap and DECISIONS mobrava#18–#021 updated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A2x9eoeoTVeB5EurzNWBw
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