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/actionsPOST /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 screencaptures 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.