ActionUI demo apps v0.8 Pre-release
Pre-releaseCovers everything between tag v0.7 (2026-07-24) and v0.8 (2026-09-05).
Summary: out-of-process access: a new ActionUIRemote bridge lets short-lived child processes, test harnesses, and agents read and drive a running app's windows over JSON-RPC, with Python, Node, and shell clients. Navigation containers gain a persistent toolbar, stacks become tappable cells, hidden finally means the same thing on all three renderers, and Chat/RichText gain find plus new runtime states.
At a glance
| Area | Change |
|---|---|
| ActionUIRemote | New. JSON-RPC 2.0 bridge over Unix socket + Python, Node, /bin/sh + zsh clients, token auth (+11,800 lines) |
| Navigation | New persistentToolbar on NavigationStack / NavigationSplitView, all three hosts |
| Stacks | VStack / HStack / ZStack with actionID are tappable whole cells, all three hosts |
| ProgressView | New progressViewStyle: automatic / linear / circular |
hidden parity |
Web reserves space (visibility), Android stops leaking hit-test + TalkBack, toolbar hidden works |
| Chat add-on | states["append"], states["lead"], states["search"] + showFindBar, resumeCheckpointActionID, acp-remote transport |
| RichText add-on | showFindBar + host-driven states["search"] |
| Core | New public ActionUIJSON, window enumeration, runtime inserts respect :platform suffixes |
| Docs / tooling | JSON Guide + Architecture rewritten cross-platform, Skill/verifier updated, AGP 9.3.1 / Gradle 9.5.0 |
1. ActionUIRemote - drive a running app from another process
The largest piece in this release. A host that embeds ActionUI starts ActionUIRemoteServer on a Unix domain socket; any same-user process can then read and mutate its windows over newline-delimited JSON-RPC 2.0 with the same verbs and value encoding as the in-process C adapter. Built for hosts where the UI lives in the app and logic runs in short-lived child handlers (OMC applets), but equally usable for test automation and agents driving a running app.
actionui.*method table: values, strings, properties, state (with the engine's type-coercion rule), rows/selection,insertElement/insertRow/removeElement, modal/alert/confirm/toast,getElementInfo,listWindows, content-size limits. Setters returntrueso batch replies scan easily. Batches run in one main-thread turn (cap 4096).- Normative
PROTOCOL.md(v1) +README.md: transport, framing (64 MiB line cap), envelope rules (named params only, null id = notification), error codes (-32700...-32603plus app codes1001unknown window /1002unknown view /1003engine refusal /1004host refusal /1005main-thread timeout /1006unauthenticated), value encoding, environment contract (ACTIONUI_REMOTE_ENDPOINT,ACTIONUI_WINDOW_UUID,ACTIONUI_REMOTE_TOKEN). - Process-wide
startShared/stopSharedsingleton with environment export, C entry points (actionUIRemoteStartServer, mint/add/revoke tokens, unexport), auto-start fromActionUIAppKitApplicationand from the Python (app.start_remote_server()) and Node (app.startRemoteServer()) hosts. - Clients, all dependency-free: Python (
actionui_remote.py, importable +python3 -m actionui_remoteCLI with exit codes 0/1/2/3,FakeServertest double, 118 unit tests + live-server integration test), Node (actionui_remote.js, CommonJS, same surface), shell (actionui_remote.shfor/bin/sh+actionui_remote.zshwith persistent connection,awkhelpers in their own files, 358-checkps-visibility matrix). - Security, measured not assumed: 0600 socket,
getpeereiduid check,FD_CLOEXEC, optional per-work-unit token (required by default onstartShared). The docs state the limit honestly:python3/nodeexpose their environment to same-uidps(noCS_RESTRICT), so shell clients and theACTIONUI_REMOTE_TOKEN_FDdescriptor handoff exist to keep the token offps. No token-file form by design.
2. persistentToolbar on navigation containers
NavigationStack and NavigationSplitView gain a persistentToolbar array (same ToolbarItem / ToolbarItemGroup shapes) that stays in the bar on every screen inside the container and composes with each screen's own toolbar. A toolbar declared directly on either container is now a deprecated alias that warns once.
Implemented on all hosts with per-host mechanics (Apple: environment publish + per-screen apply, macOS window-toolbar shortcut; Android: LocalPersistentToolbarItems merge into each Scaffold; Web: container-owned nodes moved into the visible pane's bar so ids stay single). One documented divergence: on Web, persistent secondaryAction items get their own overflow menu rather than joining the screen's.
3. Tappable VStack / HStack / ZStack
A stack carrying actionID now dispatches as one tap target on all three hosts - the only way to author a rich cell (avatar + name + status), since Button renders title + systemImage only. In a data-driven template row it reports the owning container's id as viewID and the row index as viewPartID (the Button convention); otherwise its own id + 0. Innermost action wins, disabled is inert, cells are exposed as one button for accessibility (keyboard-reachable on Web). Also fixes a live Web bug where template clones all dispatched id 0.
4. ProgressView.progressViewStyle
automatic (default, preserves the old value-present = bar / absent = spinner pairing, and fixes macOS applying no style at all), linear (the only way to ask for an indeterminate linear bar), circular. Android matches; Web accepts the key for portability (circular warns, stays linear - native <progress> is linear in both states).
5. hidden means the same everywhere now
- Web:
hiddenwasdisplay: none(collapsed). Nowvisibility: hidden- reserves space like SwiftUI.hidden()and AndroidhiddenSubtree(). Also fixeshiddenon toolbar items doing nothing, and drops thedisplay-reset bug that could reveal hidden TabView / SplitView panes. - Android:
hiddenwasalpha(0)only - still hit-testable and TalkBack-visible. Now suppresses input via the enabled environment so taps pass through to what is behind (matching Apple/Web) instead of being swallowed. Nine gesture surfaces (Canvas, TabView, VideoPlayer, popover/context-menu/swipe/searchable, sidebar rows, etc.) now honorhiddenanddisabledfor the first time. - Skill now states it plainly:
hiddenreserves space on every platform; in a toolbar it removes the item instead. No collapse semantic - use the documented ZStack panel-switcher idiom.
6. Chat + RichText add-ons
- Both gain
showFindBar+ host-drivenstates["search"](String;""dismisses; re-set""then term to re-apply the same term). Chat defaults the bar on; RichText defaults it off. ChatCmd-Fopens the transcript bar,Cmd-Gwalks hits across messages (opening folded thoughts/tool cards), scope covers thoughts/tool calls. - Chat
states["append"]: add oneChatItemto the end with no transport traffic and no re-prime (vsstates["content"]which replaces + re-primes).states["lead"]: hold lines until the next user send, then place them in front of that message (session markers that must precede, not follow). - Chat
resumeCheckpointActionID: the second half of the incremental-persistence contractentryActionIDstarts. Emitted quiescent-only as{afterSeq, sessionId}; persist atomically with entries; feed back asacp-remote'sresumeAfterSeq. Only the newacp-remotetransport (ACP agent on another machine over WebSocket, no subprocess, all platforms) produces one. - Chat transports are re-configurable in place (identical re-inject = no-op; different viable config closes the in-flight turn, stops the old transport, attaches the new one primed from the on-screen transcript). ChatView bumped 0.2.3 -> 0.5.6 along the way (scroll-follow fix, checkpoint events, append/lead/search support).
7. Core fixes
- New public
ActionUIJSON: the C adapter's JSON conversion factored into core so the remote bridge encodes exactly the same way; adds insert-position and modal-style parsers;ActionUIModelgainswindowUUIDs/hasWindow/hasElementso unknown window/view report distinctly. - Runtime
insertElement/insertRowon Apple now run throughPlatformFilter-:platformoverrides resolve for runtime-inserted subtrees, closing an Apple-only gap (Android/Web already did). ContentUnavailableViewon Web honors thematerialNameescape hatch; Android lazy containers use bound-if-unbounded extents (fixes letterboxed full-screen grids) andScrollView > LazyVGriddefers the scroll axis; Web demo tree validates clean under--strict.
8. Docs, schemas, tooling
ActionUI-JSON-Guide.mdrewritten cross-platform (three renderers, same-intent-native-appearance,:platformoverride rules, graceful degradation);Architecture.mdgains Android + Web sections and a Remote Binding section;Comparison-vs-ReactNative.md/vs-tkinter/README.mdupdated for three renderers + add-ons + zero-dependency web demo; Skill documentshidden, whole-cell tap,persistentToolbar, correctedProgressViewkey; verifier schemas gainpersistentToolbar,progressViewStyle,browserHistory:web,role:web/systemImage:webonCommandMenu.- Android: AGP 9.2.1 -> 9.3.1, Gradle wrapper 9.4.1 -> 9.5.0 in lockstep.
Scripts/test-viewer.shrepointed atApps/ActionUIViewer(was silently testing a stale binary).
Breaking changes
- Web
hiddenno longer collapses layout. It reserves space like Apple/Android. If you relied on the collapse, switch to conditional structure instead ofhidden. toolbaronNavigationStack/NavigationSplitViewis deprecated (still works, warns once). Move screen toolbars onto the screen element; usepersistentToolbaronly for items that must persist.
Notable fixes
- Android hidden element staying hit-testable + TalkBack-visible; then hidden eating taps meant for behind it.
- Web
ContentUnavailableViewignoringmaterialName;hiddenon toolbar buttons doing nothing;display-reset revealing hidden TabView/SplitView panes. - macOS
ProgressViewapplying no style (fell back to spinner); no way to ask for indeterminate linear bar. - Apple runtime inserts ignoring
:platformsuffixes. - Web demo
framemixed fixed + flexible forms; missing verifier schemas for implementedbrowserHistory:web,role:web,systemImage:web. - Stale
ChatViewpin shipped inside AppletBuilder viaApps/ActionUIViewer/Package.resolved.
Platform support
| Platform | Minimum |
|---|---|
| macOS | 14.6+ |
| iOS / iPadOS | 17.6+ |
| watchOS | 10.6+ |
| tvOS | 17.6+ |
| visionOS | 2.6+ |
| Android | 12.0+ |
| Web | Any modern browser (ES modules, no build step) |
Known gaps
- ActionUIRemote is macOS-only (Unix domain socket server lives in the Apple host).
- ActionUIChat has not been ported to Android or Web.
- Web persistent
secondaryActionitems use their own overflow menu rather than joining the screen's (documented divergence).