---
name: hydrogen-ui-mac
description: >
  macOS platform companion to hydrogen-ui — the Apple-specific half of DRIVING Hydrogen's UI.
  Every menu/dialog/panel id, the GET /ui/actions → POST /ui/invoke contract, and
  registerUiAction live in hydrogen-ui (adom/hd-bootstrap); THIS skill carries only what's
  macOS-specific: Hydrogen's webview is WKWebView with NO Chrome DevTools Protocol, so the
  legacy CDP .click() path is dead — you drive the UI via the platform-agnostic
  command bus (hd-ui-command → /ui/_result) and the backend invoke_ui_action RPC
  (e.g. dom.measure resolves a selector's rect WITHOUT CDP). Trigger words — drive Hydrogen UI macos,
  WKWebView, no CDP, dom.measure, hd-ui-command, invoke_ui_action, ui/invoke macos,
  click Hydrogen menu mac, open dialog mac.
---

# hydrogen-ui (macOS) — driving the UI without CDP

This is the **macOS half** of `hydrogen-ui`. The window layout, the menu/dialog catalog, the
list of action ids, and the whole `GET /ui/actions` → `POST /ui/invoke` workflow are
host-agnostic and live in the generic **hydrogen-ui** skill — read that first. This skill only
covers what changes on a Mac: **how clicks reach the webview when there is no CDP.**

## The macOS reality — WKWebView has no CDP

On macOS Hydrogen's webview is **WKWebView**, which exposes **no Chrome DevTools Protocol
port**. Selector-based CDP `.click()` driving therefore does not work here — there is no
port to attach to (the same limitation that makes `/eval` effectively fire-and-forget on
macOS). So on macOS the
command bus isn't merely *preferred*, it's the **only** way to operate Hydrogen's UI. For an
element that isn't on the bus yet, **register it** (a 2-line `registerUiAction(...)` —
see hydrogen-ui) rather than reaching for a selector click; there is nothing to click *through*.

## How the bus crosses to WKWebView — `hd-ui-command` → `/ui/_result`

`POST /ui/invoke {id}` is platform-agnostic precisely because it does **not** depend on
CDP. The backend (`src-tauri/crates/hd-control/src/lib.rs`, `invoke_ui_action`) emits a
Tauri event **`hd-ui-command` `{id, args, request_id}`**; the frontend dispatcher runs
the registered handler and reports back via **`POST /ui/_result` `{request_id, ok,
value}`**. The call blocks until that result lands (or the timeout fires). This is the
identical round-trip on every host — but on macOS it's load-bearing, since there is no
CDP alternative.

## `dom.measure` — resolve a selector's rect without CDP

Geometry needed its own plumbing. WKWebView cannot crop a region screenshot
server-side via CDP `Page.captureScreenshot` with a clip — so the capture path calls
**`invoke_ui_action(app,
"dom.measure", {selector}, 3000)`** to ask the *frontend* for the element's rect, then
`screencapture`s the webview content area and crops the PNG to that rect
(`hd-control/src/lib.rs` ~5193). `dom.measure` returns `{left, top, width, height,
viewport:{width,height}}` in CSS px. It's a normal bus action — the bus's answer to
"where is this element," reusable by any caller that needs a selector's geometry
without CDP.

On macOS there is no CDP machinery at all: drive everything through the command bus
(`/ui/actions` → `/ui/invoke`) and register anything that's missing.
