skill
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, clickable-paths).
← Commit history
Publish 3.1.0
8 files changed
+117−29
SKILL.md+6−10@@ -4,7 +4,7 @@ user-invocable: true description: >- The Adom UI Design System: how the AI builds AND tests interfaces people love to use in the Adom ecosystem. The AI writes the app, shows it to the user (in the user's own already-signed-in native- browser via Adom Bridge, in pup for the logged-out experience, or in a Hydrogen webview), drives+ browser via Adom Desktop, in pup for the logged-out experience, or in a Hydrogen webview), drives the front-end over a live SSE/WebSocket channel to exercise every path (auto-click buttons, show/hide floaty toolbars, load a different symbol or footprint, click send-to-kicad), and ralph-loops with screenshots until the UI is perfect. Covers the user-first interaction rules@@ -67,8 +67,8 @@ The AI picks the surface by what it needs to test, and it can use more than one | Surface | What it is | Use it for | |---|---|---|-| **Native browser** (**nb**) | The user's own Chrome or Edge, driven through Adom Bridge | The real, signed-in experience. The browser is already the user's, so the app opens **logged in with zero friction**. Huge for demoing and testing anything behind a login. |-| **pup** | A clean, scriptable Chrome the AI fully owns, via Adom Bridge's pup bridge | The **fresh, logged-out, first-time-user** experience, and automated screenshot loops that should not fight the user's own tabs. |+| **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**. Huge for demoing and testing anything behind a login. |+| **pup** | A clean, scriptable Chrome the AI fully owns, via `adom-desktop` | 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. | The native-browser and pup split is the point: the AI can open the **logged-in path in the native browser** (real cookies) and the **logged-out path in pup** at the same time, and verify both experiences. Choose deliberately, and tell the user which one and why.@@ -158,7 +158,6 @@ When several iterations of the same animation each help only a little, stop tuni The same app should feel native on a 32 inch monitor and on a phone. -- **Fill the window.** An app gets 100% of the width it is given. A centered column with a `max-width` leaves dead margin on a wide monitor, and on a dashboard that margin is columns the user paid for: a card grid on `repeat(auto-fill, minmax(Npx, 1fr))` should buy MORE columns as the window grows, never a wider gutter. Cap the measure only where a cap is the point, which is running prose, and cap that element, never the page. The linter's fluid-layout check (UI-09) flags a large fixed width; the fix is to remove the cap, not to hide the number in a variable. - **Design the layout to reshape, not to shrink.** Define a few breakpoints (phone, tablet, desktop) and change the layout at each, rather than scaling one fixed desktop layout down until it is unusable. - **Rich on desktop, focused on mobile.** A desktop has room for side panels, dense tables, persistent toolbars, secondary stats. On a phone, collapse those into a single column, move actions into a bottom bar or a menu, and reveal secondary detail on demand. The header models this already: the subject meta line hides below roughly 720px and returns on request. - **Touch targets are at least 44 by 44 px.** Fingers are not cursors.@@ -418,9 +417,7 @@ slip straight through it, and all three have shipped: ## 13. The skill map -This whole system ships as **one installable skill**. `adom-wiki pkg install adom/adom-ui-design` drops the design-system skill **plus every sub-skill below** onto your container as separate skill files, so the AI loads each by its own trigger words. The rows that link into `files/skills/` live in **this** repo; click to read them. The rest are existing platform skills.--If you add a sub-skill to `skills/`, ADD ITS ROW HERE in the same commit. A skill that ships but is not listed is a skill nobody loads: `scrollbars` sat in this repo unlisted, and apps kept shipping the default OS scrollbar because the AI had no way to discover it.+This whole system ships as **one installable skill**. `adom-wiki pkg install adom/adom-ui-design` drops the design-system skill **plus all seven skills below** onto your container as separate skill files, so the AI loads each by its own trigger words. The first seven live as files in **this** repo; click to read them. The rest are existing platform skills. | Skill | Governs | |---|---|@@ -435,7 +432,6 @@ If you add a sub-skill to `skills/`, ADD ITS ROW HERE in the same commit. A skil | [**status-leds**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/status-leds/SKILL.md) | One status language: a compact LED pill row (the chip-fetcher pattern canonized), prose on tap never by default, red LEDs always carry their next step, staleness escalates, and the row never disagrees with the dock card's LED | | [**newbie-graduation**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/newbie-graduation/SKILL.md) | The scaffolding-fade contract: Fresh, Graduated, Recallable states per app; triggers are dismissal, 3 green launches, or the first AI-driven action the bridge sees; the say strip folds last because handing the user to the AI is the point | | [**tooltips**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/tooltips/SKILL.md) | Great desktop tooltips: 600ms reveal, the fixed-div renderer, placement that never covers what you hover, edge-aware flipping, the z-index ladder |-| [**scrollbars**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/scrollbars/SKILL.md) | The pretty scrollbar and only the pretty one: a thin translucent pill on a transparent track, teal only on hover, the `background-clip: padding-box` trick that floats it off the edge, the inset track that stops a bar slicing a rounded container's corners, and never a page-level horizontal scrollbar | | [**floating-windows**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/floating-windows/SKILL.md) | Draggable floaty tool windows and HUDs that never get in the way | | [**toasts**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/toasts/SKILL.md) | Non-blocking confirmations, and the AI's live narration channel to the user | | [**responsive-ui**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/responsive-ui/SKILL.md) | Phone-to-desktop layouts, touch targets, and tooltips on touch where hover does not exist |@@ -446,7 +442,7 @@ If you add a sub-skill to `skills/`, ADD ITS ROW HERE in the same commit. A skil | [app-creator](https://wiki.adom.inc/adom/app-creator) | Scaffolding a webview app: two-way comms, proxy URL, favicon, publish flow | | `adom-app-header` | The 44px three-zone header and the app-info popover | | [adom-screenshot](https://wiki.adom.inc/adom/adom-screenshot) | Ralph loops and the visual feedback loop for debugging UI |-| `pup-vs-webview` / the pup bridge skill | Choosing and driving pup, webview, or the user's native browser |+| `pup-vs-webview` / `adom-desktop-pup` | Choosing and driving pup, webview, or the user's native browser | | [adom-cli-design](https://wiki.adom.inc/adom/adom-cli-design) | The `OK:`/`ERROR:`/`Hint:` CLI contract, in Rust | | `ai-hints-in-clis` / `relay-hint-pattern` | Writing hints, and redirecting direct HTTP callers to the CLI | | [3d-viewer-design](https://wiki.adom.inc/adom/3d-viewer-design) / `measure-tool-design` | Every rule for Adom 3D viewports and measurement |@@ -456,7 +452,7 @@ If you add a sub-skill to `skills/`, ADD ITS ROW HERE in the same commit. A skil | `adom-wiki-hero` | Rendering a billboard-grade hero image | | [adom-ui-linter](https://wiki.adom.inc/adom/adom-ui-linter) | The deterministic check suite that enforces this page | -*(Rows linking into `files/skills/` are files in this repo and install together with the design system. The other linked skills are existing platform skills with their own pages; plain-text ones have no standalone page yet.)*+*(The first seven are files in this repo and install together with the design system. The other linked skills are existing platform skills with their own pages; plain-text ones have no standalone page yet.)* ---
assets/module/newbie-panel.js+84−7@@ -112,6 +112,23 @@ var TWO_COL_MIN = 700, PHRASE_BUDGET = 336; var MIN_API = [1, 0, 142]; + /* Hydrogen 1.0.187+ answers a send with a JOB and reports progress on+ * GET /ai-threads/run-prompt/status?job=<uuid>. Measured on the VM: tab visible ~0.1s,+ * session id ~2.5s, sent by 3-5s, a trivial answer done ~8s. One poll a second is the+ * right cadence for that shape, and it is an in-memory read on the other end. */+ var PHASE = {+ opening: 'Opening a new AI chat…',+ prefilled: 'Prompt ready…',+ submitted: 'Sent, waiting for the AI…',+ working: 'The AI is working…',+ needs_you: 'The AI needs you. Open the tab.',+ done: 'Done.',+ submit_failed: 'The prompt is in the tab. Press Enter there to send it.',+ submitted_unidentified:'Sent, but the AI never checked in.',+ submit_unconfirmed: 'Sent, but delivery was not confirmed.',+ still_working: 'The AI is still working. Open the tab.'+ };+ function css() { if (document.getElementById('anp-css')) return; var s = document.createElement('style');@@ -174,6 +191,9 @@ if (!toastHost) { toastHost = el('div', 'anp-toasts'); document.body.appendChild(toastHost); } var t = el('div', 'anp-toast ' + (kind || 'info'), esc(msg)); toastHost.appendChild(t);+ /* retextable, so a long send can narrate its phases in ONE toast instead of+ * stacking six of them on top of each other. */+ t.setText = function (next) { t.innerHTML = esc(next); return t; }; setTimeout(function () { t.remove(); }, ms || (kind === 'err' ? 7000 : 4000)); return t; }@@ -224,7 +244,7 @@ }) .catch(function () { return { ok: true, version: null, open: true }; }); }- function send(base, phrase, appName, slug) {+ function send(base, phrase, appName, slug, onProgress) { return gate(base).then(function (g) { if (!g.ok) throw new Error('Adom needs updating to send prompts (found ' + g.version + ', needs 1.0.142). Run: adom update'); var ctl = new AbortController(), timer = setTimeout(function () { ctl.abort(); }, 120000);@@ -241,12 +261,55 @@ .then(function (res) { clearTimeout(timer); var j = res.j || {};- /* THREE states, never two. delivered_to is an ARRAY; a thread that opened but did- * not confirm delivery is NOT a failure, and retrying it double-sends. */+ if (!(res.status >= 200 && res.status < 300))+ throw new Error((j._hints && j._hints.note) || j.error || ('AI threads returned ' + res.status));+ /* PRE-1.0.187 SHAPE, kept because older Hydrogen builds still answer this way.+ * delivered_to is an ARRAY; a thread that opened but did not confirm delivery is+ * NOT a failure, and retrying it double-sends. */ var dt = j.delivered_to, arr = Array.isArray(dt) ? dt : (dt ? [dt] : []); if (arr.length || (j.inject && j.inject.delivered_in_conversation)) return { state: 'delivered' };- if (res.status >= 200 && res.status < 300) return { state: 'opened', hint: j._hints && j._hints.note };- throw new Error((j._hints && j._hints.note) || j.error || ('AI threads returned ' + res.status));+ /* 1.0.187+ returns in well under a second with a JOB, not a delivery. ok:true means+ * the job STARTED. delivered_to is never filled on this path any more, so without+ * the poll below every send would report "delivery not confirmed" forever. */+ if (!j.job) return { state: 'opened', hint: j._hints && j._hints.note };+ var started = Date.now(), missing = false;+ return new Promise(function (resolve) {+ (function tick() {+ /* the deadline is checked in BOTH paths. Checking it only after a successful+ * poll leaves a dashboard whose server stops answering polling forever. */+ function again() {+ if (Date.now() - started > 120000)+ return resolve({ state: 'opened', hint: 'no confirmation within 120s' });+ setTimeout(tick, 1000);+ }+ fetch(base + '/ai-threads/run-prompt/status?job=' + encodeURIComponent(j.job))+ .then(function (r) {+ /* THE STATUS ROUTE IS A THIRD PROXY ROUTE. Dashboards proxy /updater/status+ * and POST /ai-threads/run-prompt only. If this one was never added, every+ * poll 404s and the user watches "Opening…" for the full 120s before a+ * hedge, which is worse than the behaviour this replaced. Fail FAST and say+ * exactly which route is missing instead of hanging. */+ if (r.status === 404) { missing = true; return null; }+ return r.json();+ })+ .then(function (st) {+ if (missing) return resolve({ state: 'opened', phase: 'no_status_route',+ hint: 'Your AI chat opened. This app does not proxy GET ' ++ '/ai-threads/run-prompt/status yet, so progress cannot be shown.' });+ var state = (st.job && st.job.state) || '';+ if (typeof onProgress === 'function') { try { onProgress(state, st.job); } catch (e) {} }+ /* working or later IS sent, for the user-facing toast. */+ if (state === 'working' || state === 'done' || state === 'needs_you' ||+ state === 'still_working') return resolve({ state: 'delivered', phase: state });+ if (state === 'submit_failed' || state === 'submitted_unidentified' ||+ state === 'submit_unconfirmed')+ return resolve({ state: 'opened', phase: state,+ hint: (st.job && (st.job.error || st.job.note)) || state });+ again();+ })+ .catch(again);+ })();+ }); }) .catch(function (e) { clearTimeout(timer);@@ -316,7 +379,10 @@ if (sd.disabled) return; sd.disabled = true; var pending = toast('Opening a new AI thread…', 'info', 120000);- send(base, phrase, appName, slug).then(function (r) {+ send(base, phrase, appName, slug, function (state, job) {+ if (PHASE[state]) pending.setText(PHASE[state]);+ if (typeof cfg.onProgress === 'function') { try { cfg.onProgress(state, job); } catch (e) {} }+ }).then(function (r) { pending.remove(); if (r.state === 'delivered') toast('Sent. Your AI thread has the prompt.', 'ok'); else toast('Thread opened, delivery not confirmed. Check your AI threads before sending again.', 'info', 8000);@@ -605,10 +671,21 @@ real !== 'ERR' && real !== 404 && real !== fake); }); })+ .then(function () {+ /* THE STATUS ROUTE, added for Hydrogen 1.0.187+. It is a THIRD proxy route and no+ * dashboard had it before 2.22.0, so it is the row most likely to be red. A GET with+ * a job id that cannot exist must still be ANSWERED (any non-404), because a 404 here+ * means the route was never proxied and progress can never be reported. */+ return fetch(base + '/ai-threads/run-prompt/status?job=__anp_probe__')+ .then(function (r) { return r.status; }, function () { return 'ERR'; })+ .then(function (st) {+ add('progress route wired', 'answers (not 404)', String(st), st !== 'ERR' && st !== 404);+ });+ }) .then(function () { return {ok: ok, rows: rows}; }); } root.AdomNewbiePanel = { mount: mount, toast: toast, selfCheck: selfCheck, copyText: copyText,- checkSend: checkSend, version: '2.21.0',+ checkSend: checkSend, version: '2.22.0', TWO_COL_MIN: TWO_COL_MIN, PHRASE_BUDGET: PHRASE_BUDGET }; })(typeof window !== 'undefined' ? window : this);
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-ui-design",- "version": "3.0.2",+ "version": "3.1.0", "type": "skill", "description": "The Adom UI Design System plus a bundle of focused UI skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps, clickable-paths, dark-default, input-feedback, settings-layout). One install drops the whole set of skill files onto your container, plus shared icon assets (the settings cog) so every Adom app draws the same glyph.", "dependencies": {},
page.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-ui-design",- "version": "3.0.2",+ "version": "3.1.0", "type": "skill", "description": "The Adom UI Design System plus a bundle of focused UI skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps, clickable-paths, dark-default, input-feedback, settings-layout). One install drops the whole set of skill files onto your container, plus shared icon assets (the settings cog) so every Adom app draws the same glyph.", "dependencies": {},
skills/ai-driven-apps/SKILL.md+3−3@@ -3,7 +3,7 @@ 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 Bridge, in pup for the+ 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,@@ -70,10 +70,10 @@ Pick the surface by what you need to test, and use more than one at once when it | Surface | What it is | Use it for | |---|---|---|-| **Native browser** (**nb**) | The user's own Chrome or Edge, driven through Adom Bridge | 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. |+| **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** (Hydrogen Browser Window) | Under Hydrogen Desktop: a standalone Tauri browser window Hydrogen manages directly (window label `hd-browse-*`, destination id `webview-window`) | A dedicated app window beside the Hydrogen shell — bigger than a pane tab, still Hydrogen-managed. Note: the in-Hydrogen *pane tab* is the wv surface (the `webview` skill routes there inside Hydrogen). |+| **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
skills/control-api-proxy/SKILL.md+20−5@@ -28,15 +28,30 @@ and needs no access to anyone's machine. ## PROXY THE TWO API CALLS THROUGH YOUR OWN SERVER -`apiBase` defaults to `''`, same origin, and it should stay that way. Add two routes to your-own server and forward them to the Hydrogen control API using whatever discovery you already-have (`~/.adom/hydrogen-control-url`, 47084 as the fallback):+`apiBase` defaults to `''`, same origin, and it should stay that way. Add **three** routes to+your own server and forward them to the Hydrogen control API using whatever discovery you+already have (`~/.adom/hydrogen-control-url`, 47084 as the fallback): ```-GET /updater/status -> control API /updater/status-POST /ai-threads/run-prompt -> control API /ai-threads/run-prompt (120s, matching the module)+GET /updater/status -> control API /updater/status+POST /ai-threads/run-prompt -> control API /ai-threads/run-prompt (120s, matching the module)+GET /ai-threads/run-prompt/status -> control API /ai-threads/run-prompt/status (query string INTACT) ``` +⚠ **The third route is new for Hydrogen 1.0.187+ and it is the one apps forget.** On 1.0.187++the POST no longer reports delivery: it returns in well under a second with a **job**+(`{ok:true, job:"<uuid>", state:"opening"}`) and `delivered_to` is never filled on that path+again. Progress lives entirely on the status route, polled once a second. Two traps come with+it:++- **Forward the query string.** The route is called as `?job=<uuid>`. A proxy that forwards+ only the path answers about no job at all.+- **A missing route costs 120 seconds, not zero.** If it is not proxied, every poll 404s and+ the user watches "Opening a new AI chat" for the full timeout before a hedge, which is worse+ than the behaviour it replaced. `newbie-panel.js` 2.22.0+ fails fast on a 404 and says which+ route is missing, and `checkSend()` carries a `progress route wired` row so you can see it+ before a user does. Neither is a substitute for adding the route.+ Three reasons, and parts-search, nb and pup each found them separately: it removes any CORS dependency on ah; it keeps exactly ONE copy of the send logic in the fleet rather than one per app; and it keeps working in both deployment shapes, the container-served dashboard and
skills/dark-default/SKILL.md+1−1@@ -6,7 +6,7 @@ description: >- writes defaults to dark mode — light is the opt-in override, never the base. Read this the instant you are about to write ANY HTML or CSS, even a throwaway cheat sheet or a two-minute static page that does not feel like "an app". The classic mistake is styling :root light and adding dark as a- prefers-color-scheme override — that is BACKWARDS for Adom, whose surfaces (wv tabs, ah panels)+ prefers-color-scheme override — that is BACKWARDS for Adom, whose surfaces (wv tabs, hd panels) render dark by default, so a light-base page flashes white and reads as off-brand. This is the 30-second rule; the parent adom-ui-design skill is the full design system (tokens, header, linter). Read BEFORE writing any HTML/CSS. Trigger words: html, write html, static page, static html, css,
skills/settings-layout/SKILL.md+1−1@@ -430,7 +430,7 @@ IntersectionObserver unless you have hundreds of sections. John asked for this directly: "put what the defaults are in the settings window." 4. **Put the full key in `title`** as a tooltip. That is how a user reports a setting precisely and how an AI finds it.-5. Fix initialisms your case helper mangles: `Wsl` to `WSL`, `Hd` to `Hydrogen`, `Api` to `API`.+5. Fix initialisms your case helper mangles: `Wsl` to `WSL`, `Hd` to `HD`, `Api` to `API`. ## Type scale