← Commit history

dashboard contract: dock-dashboard + dash-header + say-strip + status-leds + newbie-graduation sub-skills (1.8.0)

John Lauer ·172aa45878 ·1mo ago ·parent b97fbfd
7 files changed +398−1
SKILL.md+5
@@ -422,6 +422,11 @@ This whole system ships as **one installable skill**. `adom-wiki pkg install ado |---|---| | [**input-feedback**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/input-feedback/SKILL.md) | The acknowledgment doctrine: hover, mousedown/:active, and focus-visible states on EVERY interactive element - the UI answers 'yes, you just hovered me / pressed me' before the action even runs | | [**settings-layout**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/settings-layout/SKILL.md) | The settings surface every Adom app shares: a cog in the header opening a dismissible MODAL, backdrop dimmed 20-30% and NEVER blurred (settings often change what is behind them), teal-accent cards that explain themselves, toggles not checkboxes, defaults shown on every row, two-click Restore defaults, and a category sidebar with jump-to and scroll-spy once settings outgrow a screen |+| [**dock-dashboard**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/dock-dashboard/SKILL.md) | THE parent contract for every Dock Bar launch dashboard: one anatomy (house header, say strip, status pill row, then the app's own content), the two legitimate species (bridge launch page vs full app whose main UI is the dock target), and the shared token stub - ends the seven-dashboards-seven-designs era |+| [**dash-header**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/dash-header/SKILL.md) | One header grammar for dashboards: human name never a slug echo, version NEVER inline in the bar (popover only), the standard target box chip, and the ? that recalls the newbie scaffolding |+| [**say-strip**](https://wiki.adom.inc/adom/adom-ui-design/files/skills/say-strip/SKILL.md) | The ONE say strip: "Say it" heading, quoted phrases with the noun in teal, exactly one Try-it treatment (ghost pill, copy to clipboard, the fleet toast "Copied to clipboard. Paste it into any AI thread."), with the unfocused-window clipboard recipe |+| [**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 | | [**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 |
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-ui-design",-  "version": "1.7.2",+  "version": "1.8.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/dash-header/SKILL.mdadded+52
@@ -0,0 +1,52 @@+---+name: dash-header+user-invocable: true+description: >-+  One header grammar for every dock bar dashboard, extending the 44px three-zone+  adom-app-header. Human name never a slug echo, lowercase brand wordmarks live in the icon+  not the name, version NEVER inline in the bar (hover popover only, tap on mobile), the+  target box chip (which machine the bridge answers on) standard and in the same place on+  every app, gear upper right per settings-layout. Read when building or fixing a dashboard+  header. Trigger words: dashboard header, dash header, app name slug, version in header,+  target chip, box chip, header grammar, dock header.+---++Parent skill: **adom-ui-design** (extends the app-header rules in section 5)++# The dashboard header++The 44px three-zone header from adom-ui-design section 5 applies to dashboards unchanged.+This skill adds the dashboard-specific rulings the 2026-08-24 audit showed being broken.++## The grammar++```+[ icon  App Name · one-line subtitle ] | [ target: ConfRoomROG ]      [ ? gear ]+```++- **Human name, never a slug.** "Adom LBR", never "adom-lbr". "Parts Search", never+  "adom-parts-search". The wiki page title is usually the right name.+- **Lowercase brands keep their case in the ICON, not the name.** pup and nb are lowercase+  wordmarks; the header still writes "Pup" and "Native Browser". The 16x16 icon carries the+  brand's own styling.+- **Version NEVER in the bar.** kicad and fusion shipped "bridge 0.9.249 | dashboard 2.2.7"+  inline; that is meta, not identity. It moves into the brand-zone hover popover (600ms+  delay, tap on mobile), alongside build date and the wiki link. One version line per+  component is fine THERE.+- **The target chip is standard.** Bridge-backed dashboards show which box answers:+  a small mono chip, `target: <BoxName>`, subject zone, with the connection LED inside it.+  Same spot on every app so a user's eye learns it once.+- **The ? lives left of the gear** and recalls the newbie scaffolding - see+  **newbie-graduation**. Gear opens settings per **settings-layout**.++## Subtitle++Two to four words that say what the page is, in plain language: "the KiCad bridge",+"AI browser control", "parts across three vendors". Not marketing copy, not the slug, not+"bridge launch page" (a phrase only the builder understands).++## Ralph-test++Screenshot the header at 1400px and at 390px. Checks: name is the human name; no digit+sequence resembling a version anywhere in the bar; target chip present iff there is a+bridge; popover reachable and carrying the versions; subject meta hides on the narrow shot.
skills/dock-dashboard/SKILL.mdadded+92
@@ -0,0 +1,92 @@+---+name: dock-dashboard+user-invocable: true+description: >-+  THE parent contract for every Hydrogen Dock Bar launch dashboard - the page a dock icon+  opens. One anatomy top to bottom (house header, say strip, status pill row, then the app's+  own content), two legitimate species (bridge launch pages vs full apps whose main UI is the+  dock target), and the shared token stub every dashboard copies. Born from the 2026-08-24+  audit where seven dashboards shipped seven say-strips, five status dialects, and five+  version placements. Read BEFORE building or restyling any dock bar dashboard, and route to+  the sub-skills dash-header, say-strip, status-leds, newbie-graduation for each region.+  Trigger words: dock dashboard, launch dashboard, dock bar ui, dockbar dashboard, bridge+  launch page, dashboard anatomy, dashboard contract, dock bar consistency, dashboard tokens,+  brother from another mother.+---++Parent skill: **adom-ui-design**++# The dock dashboard contract++Seven dock icons used to open seven unrelated-looking pages. Individually decent,+collectively a mess: three say-strip headings, four Try-it button styles, five status+dialects, five places the version lived, one app with no header at all. This skill is the+contract that ends that. A user who has seen ONE Adom dashboard has seen them all.++## The anatomy, top to bottom++Every launch dashboard renders these regions in this order:++1. **House header** (44px, three zones) - see **dash-header**. Human app name, target box+   chip, gear. Version NEVER in the bar.+2. **Say strip** - see **say-strip**. The one component that teaches the user to talk to+   their AI. One heading, one chip style, one Try-it.+3. **Status pill row** - see **status-leds**. The whole app's health in one compact line,+   prose on demand, never by default.+4. **The app's own content** - actions, demos, consoles, whatever the app is. This region+   is the app's to design; the three above are not.++Newbie scaffolding in regions 2 and 3 fades as the user levels up - see+**newbie-graduation**.++## The two species (so nobody argues later)++- **Bridge launch page** (kicad, fusion, pup, nb): the dashboard IS a control panel for a+  bridge. Full anatomy, nothing else. Keep it one screen where possible.+- **Full app** (chip-fetcher, adom-lbr, parts-search): the app's main UI is the dock target,+  per John's standing ruling ("don't ask it to make a separate one"). These apps embed the+  SAME say-strip and status primitives inside their own layout - same markup, same tokens -+  instead of bolting a second page on. The header is the app's real header, brought up to+  the dash-header grammar.++There is no third species. A dashboard that is "mostly a launch page but with its own status+idiom" is a launch page that has not adopted the contract yet.++## The token stub (copy-paste, then never hardcode again)++```css+:root {+  --bg: #0d1117; --surface: #161b22; --surface2: #1c232c; --line: #262d36;+  --text: #e6edf3; --muted: #8b949e; --teal: #00b8b1;+  --led-green: #3fb950; --led-amber: #d29922; --led-red: #f85149;+  --sans: "Familjen Grotesk", system-ui, sans-serif;   /* brand, headings */+  --body: system-ui, -apple-system, "Segoe UI", sans-serif;+  --mono: "JetBrains Mono", ui-monospace, Consolas, monospace; /* meta, labels */+}+```++Every color in the dashboard resolves to one of these tokens. Brand colors of third parties+(Mouser blue, DigiKey red) may decorate a logo or a border accent, but meaning - ok, busy,+broken - is ALWAYS carried by the led tokens, never by a brand color alone.++## What already exists (reference, never duplicate)++- **scrollbars** - the house pill, mandatory on every scrolling surface.+- **settings-layout** - the cog-opens-a-modal settings surface.+- **toasts** - bottom-center, success green / error red / info teal.+- **pup-taskbar-progress** (bridge-side) - long AI work shows an indeterminate taskbar bar.++## Ralph-test the contract++Open the dashboard cold from its dock icon next to kicad's, screenshot both, and LOOK: same+header grammar, same say strip, same status row shape? A stranger should not be able to tell+which team built which. Then check phone width (the responsive-ui rules apply to dashboards+too).++## Anti-patterns (all shipped in 2026, all now banned)++- A dashboard with no header, or a giant lowercase wordmark as the header.+- The slug as the displayed name ("adom-lbr").+- Status LEDs hiding in header nav dots or a page footer.+- Version strings inline in the header bar.+- A second "dashboard" page bolted onto a full app for the dock to open.
skills/newbie-graduation/SKILL.mdadded+75
@@ -0,0 +1,75 @@+---+name: newbie-graduation+user-invocable: true+description: >-+  The scaffolding-fade contract for dock dashboards: newbie helpers (expanded say strip,+  getting-started panel, prominent demos) exist in three states per app - Fresh (full+  scaffolding), Graduated (collapsed to one quiet line), and Recallable (the header's ?+  restores everything for a session). Graduation triggers: the user dismissed it, or N+  successful launches (default 3), or the bridge saw the user's AI drive the app once.+  Tracked in localStorage (each dashboard port is its own origin, so state is naturally+  per-app). The say strip collapses LAST because handing the user to the AI is the whole+  point; users graduating past the dock into pure AI prompts is success, not churn. Read+  when adding any onboarding/newbie UI to a dashboard. Trigger words: newbie, onboarding,+  getting started, graduation, scaffolding fade, progressive disclosure, first run, tour+  panel, dismiss forever, bring back help.+---++Parent skill: **adom-ui-design**++# Newbie graduation - scaffolding that knows when to leave++John's ruling (2026-08-24): "i'd prefer for the newbie part to possibly go away once the+user is used to the app... the newbie part might go away by folks simply not using the dock+bar launcher anymore and just doing ai prompts." Both halves are the design: scaffolding+fades with familiarity, and a user who stops needing the dashboard entirely is a WIN. The+dashboard's newbie job is to hand the user to their AI.++## The three states++1. **Fresh** (default on first visit): say strip expanded, getting-started panel open,+   demo buttons prominent. The dashboard teaches.+2. **Graduated**: each scaffold collapses to a single quiet line ("Say it - 6 phrases",+   one row, tap to expand once). Getting-started and demo sections fold to their headings.+   The app's real content moves up.+3. **Recallable**: the header's **?** (left of the gear, see dash-header) restores Fresh+   for the current session, then the page folds back. Nothing is ever unreachable.++## Graduation triggers (any one graduates that scaffold)++- The user dismissed it (the X). Immediate, it was their choice.+- **N successful launches**, default 3. Count a launch as successful when the dashboard+  loads AND its status row is green within the first minute.+- **The user's AI drove the app once.** The bridge knows when a verb ran that a human did+  not click (chip-fetcher fetches, kicad opens, pup windows). First AI-driven action =+  the user already knows the real interface; fold the teaching.++The say strip graduates LAST: it stays one state fresher than the rest (when everything+else folds at 3 launches, it folds at 5, or on dismissal/AI-driven-action).++## Storage++localStorage, no server, no sync - each dashboard's port is its own origin so keys are+naturally per-app:++```js+const GRAD_KEY = 'adom-dash-graduation';   // {launches: n, aiDriven: bool, dismissed: {say: bool, start: bool}}+function gradState() { try { return JSON.parse(localStorage.getItem(GRAD_KEY)) ?? {} } catch { return {} } }+function gradSave(patch) { try { localStorage.setItem(GRAD_KEY, JSON.stringify({ ...gradState(), ...patch })) } catch {} }+```++Wrap every read/write in try/catch (private windows, cleared site data) and render Fresh+when storage is empty or unreadable - the safe default is teaching.++## What never fades++- The status LED row (see status-leds): health is not scaffolding.+- The header, the gear, the ?.+- The say strip's collapsed one-liner: even a graduated page keeps one visible thread back+  to "you can just say it".++## Ralph-test++Clear storage, load: Fresh. Simulate 3 green launches (or dismiss): page folds except the+say strip. Two more: say strip folds to its one-liner. Press ?: everything returns; reload:+folded again. Verify in pup with a REAL localStorage, not just code reading.
skills/say-strip/SKILL.mdadded+98
@@ -0,0 +1,98 @@+---+name: say-strip+user-invocable: true+description: >-+  The ONE say strip every dock dashboard renders - the component that teaches a user to+  drive the app by talking to their AI instead of clicking. Canonical heading ("Say it"),+  one explainer sentence naming the app's noun, quoted phrases with the noun in teal, chips+  never monospace, exactly ONE Try-it treatment (ghost pill, copies to clipboard, success+  toast "Copied to clipboard. Paste it into any AI thread."), dismissible and wired to+  newbie-graduation. Ships the full copy-paste recipe including the unfocused-window+  clipboard fallback. Read BEFORE building or restyling any say strip / Try it row.+  Trigger words: say strip, say it, try it button, ai phrases, prompt chips, say it to your+  ai, copy prompt, teach the noun, dockbar say.+---++Parent skill: **adom-ui-design**++# The say strip++The audit found three headings, four Try-it styles, quoted and unquoted phrases, monospace+and sans chips. From now on there is one strip. Its job: hand the user to their AI. It is+the last scaffolding to fade (see **newbie-graduation**) because it IS the graduation path.++## The canonical strip++- **Heading:** the literal string **"Say it"**. Not "Say it to your AI", not all-caps.+- **Explainer:** ONE sentence, naming the noun: `You never need this page's buttons: just+  tell your AI what you want, using the word <b>kicad</b>. Click a phrase to copy it.`+- **Phrases:** 3 to 6, straight from the app's dockbar.json `say` array, ALWAYS in curly+  quotes, the noun highlighted teal: `"open the symbol in <em>kicad</em>"`. Body face,+  never monospace.+- **Try it:** ONE style - a small ghost pill after the phrase chip. Clicking the phrase or+  the pill does the same thing: copy, then toast.+- **Dismiss:** an X on the strip's corner. Dismissal is a graduation trigger; the ? in the+  header brings it back. Never gone forever.++## The recipe++```html+<section class="say" id="say">+  <div class="say-head"><h2>Say it</h2>+    <p>You never need this page's buttons: just tell your AI what you want,+       using the word <b class="noun">kicad</b>. Click a phrase to copy it.</p>+    <button class="say-x" aria-label="Collapse">&times;</button></div>+  <div class="say-chips">+    <span class="say-chip" data-say="open the symbol in kicad">+      &ldquo;open the symbol in <em>kicad</em>&rdquo;<button class="try">Try it</button>+    </span>+    <!-- ...one per phrase -->+  </div>+</section>+```++```css+.say { background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--teal); border-radius: 10px; padding: 14px 16px; }+.say h2 { font: 600 15px var(--sans); margin: 0; }+.say-head p { color: var(--muted); font-size: 13.5px; margin: 4px 0 0; }+.say-chip { display: inline-flex; align-items: center; gap: 8px; background: var(--surface2); border: 1px solid var(--line); border-radius: 999px; padding: 5px 6px 5px 12px; font-size: 13.5px; margin: 4px 6px 0 0; cursor: pointer; }+.say-chip em { color: var(--teal); font-style: normal; }+.say-chip .try { font: 500 11px var(--body); color: var(--muted); background: none; border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px; cursor: pointer; }+.say-chip:hover .try { color: var(--teal); border-color: var(--teal); }+```++```js+// Copy that survives an UNFOCUSED window (the parts-search 1.1.2 recipe):+// navigator.clipboard.writeText hangs without focus, so check synchronously+// and fall back to an in-gesture execCommand copy.+function copyPhrase(text) {+  const done = () => toast('success', 'Copied to clipboard. Paste it into any AI thread.');+  if (document.hasFocus()) {+    navigator.clipboard.writeText(text).then(done, () => legacyCopy(text) && done());+  } else if (legacyCopy(text)) done();+}+function legacyCopy(text) {+  const ta = Object.assign(document.createElement('textarea'), { value: text });+  ta.style.cssText = 'position:fixed;opacity:0'; document.body.append(ta);+  ta.select(); const ok = document.execCommand('copy'); ta.remove(); return ok;+}+document.querySelectorAll('.say-chip').forEach(c =>+  c.addEventListener('click', () => copyPhrase(c.dataset.say)));+```++The toast is the standard toasts-skill toast: success green, bottom center, ~4s. The copy+string is EXACTLY `Copied to clipboard. Paste it into any AI thread.` - fleet-wide, so the+behavior reads as one product.++## Live-thread staging (currently parked)++When AI-thread staging is certified and un-parked, Try it MAY stage the phrase into the+app's AI thread instead of copying - behind the app's staging flag AND the dock's aiThreads+setting, and the copy+toast path remains the fallback forever. Until the un-park broadcast:+copy+toast only, and POST /api/say-try answers `{parked:true, why}` per the dockbar guide.++## Ralph-test++Click a chip with the window UNFOCUSED (drive it via the bridge, not your own mouse):+toast appears, clipboard holds the exact phrase. Then check the strip at phone width: chips+wrap, nothing overflows the page body.
skills/status-leds/SKILL.mdadded+75
@@ -0,0 +1,75 @@+---+name: status-leds+user-invocable: true+description: >-+  One status language for every dock dashboard: a single compact LED pill row (the+  chip-fetcher pattern, canonized) instead of prose-heavy card grids, stacked rows, header+  nav dots, footer strips, or layer tables. LED dot + label + short state per pill;+  paragraph explainers move into tap-to-expand detail; a red LED always carries its next+  step (the UI cousin of the CLI Hint: rule); semantic color comes only from the led tokens,+  never from third-party brand colors. This row is ALSO what the dock bar's own health poll+  and hover preview mirror, so keeping it honest keeps the dock honest. Read BEFORE building+  any dashboard status area. Trigger words: status leds, led row, status pills, health row,+  dashboard status, led colors, status cards, red led hint, status dialect.+---++Parent skill: **adom-ui-design**++# Status LEDs - one language++The audit found five dialects for the same sentence ("here is my health"). From now on+every dashboard says it the same way: one compact pill row, directly under the say strip.++## The pill row++```+[● Dashboard v1.3.12 serving] [● Bridge answering] [● KiCad 10.0.5] [● Updates current] [● Plugin 5 live]+```++- One pill per subsystem: LED dot, label, and a 1-4 word state. Nothing else visible.+- LED colors from tokens ONLY: `--led-green` healthy, `--led-amber` busy or degraded,+  `--led-red` broken. Gray = unknown or not yet probed. Brand colors never carry health.+- The row wraps on narrow screens; it never becomes a grid of cards.++```css+.leds { display: flex; flex-wrap: wrap; gap: 8px; }+.led-pill { display: inline-flex; align-items: center; gap: 7px; background: var(--surface);+  border: 1px solid var(--line); border-radius: 999px; padding: 4px 12px; font-size: 12.5px;+  cursor: pointer; }+.led-pill .dot { width: 8px; height: 8px; border-radius: 50%; }+.led-pill[data-state=ok] .dot { background: var(--led-green); }+.led-pill[data-state=busy] .dot { background: var(--led-amber); }+.led-pill[data-state=bad] .dot { background: var(--led-red); }+```++## Prose on demand, never by default++kicad and fusion shipped a paragraph of explainer inside every status card, always visible.+That prose is good - as EXPANSION. Tapping a pill opens its detail (popover on desktop,+sheet on touch) holding the full explanation, timestamps, and versions. The resting page+shows pills only.++## A red LED always carries its next step++The detail of any non-green pill leads with what to DO, exactly like the CLI's `Hint:`+contract: "Bridge not answering. Start it on the box: `adom-bridge serve`." A red light+with no next step is blame, not help.++## Staleness is a state, not a mystery++A "busy" that has been busy for a day is broken. If a subsystem reports busy/starting+longer than a sane ceiling (default 15 minutes), the pill escalates to amber with+"stuck?" in the state and the detail says how to bounce it. (Found live on 2026-08-24:+Fusion's add-in amber-busy for 24h, which every reader assumed was fine.)++## The dock reads what you show++The dashboard's /api/health (CORS `*`, per the dockbar guide) is the same truth this row+renders. The dock card's LED, the hover preview, and this row must never disagree - one+server-side state, three views.++## Ralph-test++Kill one subsystem for real, reload: its pill goes red, its detail names the fix command.+Screenshot rest state: pills only, no prose. Tap a pill: prose appears. Narrow width: the+row wraps, no horizontal page scroll.