Adom UI Design System
Public Made by Adomby adom
How the AI builds and tests interfaces people love to use in Adom. Ships as one installable skill that bundles focused skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps).
name: ai-driven-apps user-invocable: true description: >- How the AI builds, shows, drives, and tests an Adom app. In Adom the AI writes the app, shows it to the user (in their own already-signed-in native browser via Adom Desktop, in pup for the logged-out experience, or in a Hydrogen webview), drives the front-end over a live SSE or WebSocket channel to exercise every path (auto-click buttons, show and hide floaty toolbars, load a different symbol or footprint, click send-to-kicad), reads back state, console, and screenshots, and ralph-loops until the UI is right. Covers state-on-the-server, GET /state, GET /console, the live channel, the CLI driving commands, the three display surfaces and when to use each, and the ralph-test loop. Read BEFORE building or testing any Adom web app. Trigger words: ai-drivable, ai driven app, drive the front-end, drive the ui, live channel, SSE, server-sent events, websocket, GET /state, GET /console, ralph test, ralph loop, auto-click buttons, test the ui, show app in native browser, show app in pup, webview vs pup, native browser auth, logged out testing, ui click.
AI-driven apps
This is the loop the whole Adom UI system is designed around. The AI is both the builder and the tester; the user watches and steers. An app is only as good as the AI's ability to drive and observe it, so build for that from the first commit.
The loop
- The AI builds the app: a small server plus a webview UI, made drivable from day one (state on the server, a live channel, a CLI).
- The AI shows it to the user in one of three surfaces (below), chosen for what is being tested.
- The AI drives the front-end over the live channel to exercise every path: click each button, open and close the floating toolbar, load a different symbol or footprint, hit send-to-kicad, and read back state, the console buffer, and screenshots.
- The AI ralph-loops: screenshot, analyze what looks wrong, fix the code, reload, repeat, until the UI is correct and a pleasure to use.
- The AI narrates through toasts and chat so the user can follow along.
State on the server, observed two ways
- The server owns the truth. The UI never keeps its own copy of "am I recording?" or "which symbol is loaded?". Buttons fire commands to the server; the server holds state.
GET /statereturns the current state as JSON. The UI reflects it; the AI reads it to check progress without scraping HTML.GET /consoleexposes the app's log: overrideconsole.log/warn/errorto also post to the server, keep a ring buffer, and catchwindow.onerrorandunhandledrejection. Now the AI reads what the UI is doing without DevTools.
The live channel
- Expose Server-Sent Events for one-way push, or a WebSocket for two-way. The UI subscribes
and updates the instant anything changes; the CLI subscribes too. A 300 to 500ms poll of
GET /stateis the minimum fallback, but the live channel is what makes driving feel instant. - Because the channel is two-way, the AI drives the front-end from the CLI. Every user-visible
action has a command that does exactly what the click does:
Each command drives the real UI through the same path a user click would, so the AI can walk every interaction, assert the resulting state, screenshot it, and move on. This is the test harness and the demo driver in one.mytool ui click "send-to-kicad" # press a button mytool ui toolbar show # reveal the floating toolbar, then `hide` mytool ui load-symbol LM358 # swap the loaded symbol or footprint mytool ui toast "file downloaded" # raise a toast (also the AI's narration channel) - Acceptance test: an AI driving the app over the channel and a person clicking the UI produce identical visible results.
The three display surfaces
Pick the surface by what you need to test, and use more than one at once when it helps.
| Surface | What it is | Use it for |
|---|---|---|
| Native browser (nb) | The user's own Chrome or Edge, driven through Adom Desktop | The real, signed-in experience. The browser is already the user's, so the app opens logged in with zero friction. Use it for anything behind a login. |
| pup | A clean, scriptable Chrome the AI fully owns | The fresh, logged-out, first-time-user experience, and automated screenshot loops that should not fight the user's own tabs. |
| Hydrogen webview | An in-workspace browser tab | Showing a result inside the workbench, next to the code and the chat. |
| hdbw (HD Browser Window) | Under Hydrogen Desktop: a standalone Tauri browser window HD manages directly (window label hd-browse-*, destination id webview-window) |
A dedicated app window beside the HD shell — bigger than a pane tab, still HD-managed. Note: the in-HD pane tab is the wv surface (the webview skill routes there inside HD). |
VS Code's "Simple Browser" is NOT a surface
NEVER show an Adom app in VS Code's built-in "Simple Browser". It is none of the Adom
surfaces: no live channel, no console read-back, no AI drivability, wrong pane semantics.
If you are about to paste an app URL into Simple Browser, stop and load the webview skill
instead.
Every app's SKILL.md must carry a display section (MANDATE)
When you write or update an app's SKILL.md, include a section titled
"Displaying this app (REQUIRED)" that (a) names the app's own show/open verb if it has
one, (b) instructs loading the webview skill for manual display (pup / native browser / hdbw per
the table above), and (c) states the Simple Browser ban verbatim. Skill discovery from
trigger words alone is probabilistic; the app skill IS reliably loaded when the app is
named, so it must carry the display rules itself. (Decision John 2026-07-17, after a fresh
workspace showed adom-parts-search in Simple Browser.)
The native-browser and pup split is the point: open the logged-in path in the native browser and the logged-out path in pup at the same time, and verify both. Tell the user which surface and why.
Ralph-testing the UI
The visual feedback loop that turns "it runs" into "it is right": edit, reload, drive a step over the channel, screenshot, read the console buffer, analyze, fix, repeat. Drive it in pup (or the native browser), capture each frame to the screenshot log, and loop until the UI looks and behaves correctly. This is how the AI debugs UI with no human in the loop, and it is the proof the app is genuinely drivable.
Reaching a container app from outside
- A webview and a browser window both render outside the container, so they cannot reach
http://localhost:<port>orhttp://127.0.0.1:<port>. Always use the public proxy URL:https://<slug>.adom.cloud/proxy/<port>/. - Inside a webview, always use relative URLs. A leading slash bypasses the proxy and hits the
wrong origin. Every
fetch,href,src, and CSSurl()must be relative.
Boundary with app-creator (do not blur it)
adom/app-creator owns CREATING the app: scaffolding, project layout, server + webview skeleton,
wiring the first commit. THIS skill owns everything after: SHOWING the app, DRIVING its front-end,
and RALPH-TESTING the UI until it is right. If you are adding "how to build/scaffold" content, it
goes in app-creator; if you are adding "how to drive/verify/test the UI", it goes here. Keeping the
line crisp is what stops the two skills drifting into contradictory duplicates.
Related skills
app-creator (scaffolding the app), adom-screenshot (the ralph loop and shotlog),
adom-cli-design and ai-hints-in-clis (the CLI the driving commands live in), toasts (the
narration channel), app-shell (the layout being driven), and adom-ui-design (the hub).
---
name: ai-driven-apps
user-invocable: true
description: >-
How the AI builds, shows, drives, and tests an Adom app. In Adom the AI writes the app, shows it to
the user (in their own already-signed-in native browser via Adom Desktop, in pup for the
logged-out experience, or in a Hydrogen webview), drives the front-end over a live SSE or WebSocket
channel to exercise every path (auto-click buttons, show and hide floaty toolbars, load a
different symbol or footprint, click send-to-kicad), reads back state, console, and screenshots,
and ralph-loops until the UI is right. Covers state-on-the-server, GET /state, GET /console, the
live channel, the CLI driving commands, the three display surfaces and when to use each, and the
ralph-test loop. Read BEFORE building or testing any Adom web app. Trigger words: ai-drivable, ai
driven app, drive the front-end, drive the ui, live channel, SSE, server-sent events, websocket,
GET /state, GET /console, ralph test, ralph loop, auto-click buttons, test the ui, show app in
native browser, show app in pup, webview vs pup, native browser auth, logged out testing, ui click.
---
# AI-driven apps
This is the loop the whole Adom UI system is designed around. The AI is both the builder and the
tester; the user watches and steers. An app is only as good as the AI's ability to drive and observe
it, so build for that from the first commit.
## The loop
1. **The AI builds the app:** a small server plus a webview UI, made drivable from day one (state on
the server, a live channel, a CLI).
2. **The AI shows it to the user** in one of three surfaces (below), chosen for what is being tested.
3. **The AI drives the front-end** over the live channel to exercise every path: click each button,
open and close the floating toolbar, load a different symbol or footprint, hit send-to-kicad, and
read back state, the console buffer, and screenshots.
4. **The AI ralph-loops:** screenshot, analyze what looks wrong, fix the code, reload, repeat, until
the UI is correct and a pleasure to use.
5. **The AI narrates** through toasts and chat so the user can follow along.
## State on the server, observed two ways
- **The server owns the truth.** The UI never keeps its own copy of "am I recording?" or "which
symbol is loaded?". Buttons fire commands to the server; the server holds state.
- **`GET /state`** returns the current state as JSON. The UI reflects it; the AI reads it to check
progress without scraping HTML.
- **`GET /console`** exposes the app's log: override `console.log/warn/error` to also post to the
server, keep a ring buffer, and catch `window.onerror` and `unhandledrejection`. Now the AI reads
what the UI is doing without DevTools.
## The live channel
- Expose **Server-Sent Events** for one-way push, or a **WebSocket** for two-way. The UI subscribes
and updates the instant anything changes; the CLI subscribes too. A 300 to 500ms poll of
`GET /state` is the minimum fallback, but the live channel is what makes driving feel instant.
- **Because the channel is two-way, the AI drives the front-end from the CLI.** Every user-visible
action has a command that does exactly what the click does:
```
mytool ui click "send-to-kicad" # press a button
mytool ui toolbar show # reveal the floating toolbar, then `hide`
mytool ui load-symbol LM358 # swap the loaded symbol or footprint
mytool ui toast "file downloaded" # raise a toast (also the AI's narration channel)
```
Each command drives the real UI through the same path a user click would, so the AI can walk every
interaction, assert the resulting state, screenshot it, and move on. This is the test harness and
the demo driver in one.
- **Acceptance test:** an AI driving the app over the channel and a person clicking the UI produce
identical visible results.
## The three display surfaces
Pick the surface by what you need to test, and use more than one at once when it helps.
| Surface | What it is | Use it for |
|---|---|---|
| **Native browser** (**nb**) | The user's own Chrome or Edge, driven through Adom Desktop | The real, signed-in experience. The browser is already the user's, so the app opens **logged in with zero friction**. Use it for anything behind a login. |
| **pup** | A clean, scriptable Chrome the AI fully owns | The **fresh, logged-out, first-time-user** experience, and automated screenshot loops that should not fight the user's own tabs. |
| **Hydrogen webview** | An in-workspace browser tab | Showing a result inside the workbench, next to the code and the chat. |
| **hdbw** (HD Browser Window) | Under Hydrogen Desktop: a standalone Tauri browser window HD manages directly (window label `hd-browse-*`, destination id `webview-window`) | A dedicated app window beside the HD shell — bigger than a pane tab, still HD-managed. Note: the in-HD *pane tab* is the wv surface (the `webview` skill routes there inside HD). |
### VS Code's "Simple Browser" is NOT a surface
**NEVER show an Adom app in VS Code's built-in "Simple Browser".** It is none of the Adom
surfaces: no live channel, no console read-back, no AI drivability, wrong pane semantics.
If you are about to paste an app URL into Simple Browser, stop and load the `webview` skill
instead.
### Every app's SKILL.md must carry a display section (MANDATE)
When you write or update an app's SKILL.md, include a section titled
**"Displaying this app (REQUIRED)"** that (a) names the app's own show/open verb if it has
one, (b) instructs loading the `webview` skill for manual display (pup / native browser / hdbw per
the table above), and (c) states the Simple Browser ban verbatim. Skill discovery from
trigger words alone is probabilistic; the app skill IS reliably loaded when the app is
named, so it must carry the display rules itself. (Decision John 2026-07-17, after a fresh
workspace showed adom-parts-search in Simple Browser.)
The native-browser and pup split is the point: open the **logged-in path in the native browser** and
the **logged-out path in pup** at the same time, and verify both. Tell the user which surface and
why.
## Ralph-testing the UI
The visual feedback loop that turns "it runs" into "it is right": edit, reload, drive a step over
the channel, screenshot, read the console buffer, analyze, fix, repeat. Drive it in pup (or the
native browser), capture each frame to the screenshot log, and loop until the UI looks and behaves
correctly. This is how the AI debugs UI with no human in the loop, and it is the proof the app is
genuinely drivable.
## Reaching a container app from outside
- A webview and a browser window both render **outside** the container, so they cannot reach
`http://localhost:<port>` or `http://127.0.0.1:<port>`. Always use the public proxy URL:
`https://<slug>.adom.cloud/proxy/<port>/`.
- **Inside a webview, always use relative URLs.** A leading slash bypasses the proxy and hits the
wrong origin. Every `fetch`, `href`, `src`, and CSS `url()` must be relative.
## Boundary with app-creator (do not blur it)
`adom/app-creator` owns CREATING the app: scaffolding, project layout, server + webview skeleton,
wiring the first commit. THIS skill owns everything after: SHOWING the app, DRIVING its front-end,
and RALPH-TESTING the UI until it is right. If you are adding "how to build/scaffold" content, it
goes in app-creator; if you are adding "how to drive/verify/test the UI", it goes here. Keeping the
line crisp is what stops the two skills drifting into contradictory duplicates.
## Related skills
`app-creator` (scaffolding the app), `adom-screenshot` (the ralph loop and shotlog),
`adom-cli-design` and `ai-hints-in-clis` (the CLI the driving commands live in), `toasts` (the
narration channel), `app-shell` (the layout being driven), and `adom-ui-design` (the hub).