← Commit history

The Dock Bar: a full sub-readme with ten live screenshots

John Lauer ·9de847bb44 ·2mo ago ·parent 368d995
docs/dockbar.md is the deep dive: the rail, the hover read-surface, the interstitial, container launch steps, the CLI taking over the webview, AI threads opened new and resumed with their old context, honest failure, health LEDs, settings, the complete dockbar.json manifest reference, the three rules, and the publisher checklist. Every screenshot is the real app on a real machine, captured during verification. The main README's dock section becomes the short index: corrected to dockbar.json (it still taught the pre-manifest package.json dock block), and it now links the guide per the sub-readme convention.
12 files changed +175−8
README.md+12−8
@@ -102,26 +102,30 @@ Click **Your profile** and the profile editor opens as its own window: name, bio  --- -## The dock - every wiki app, one click+## The Dock Bar - every wiki app, one click -The rail on the right edge of Hydrogen is the dock: the app launcher. Every card is a wiki app page. The dock shows the page's own name, brief, and hero, and launches it the way the app declared in the `dock` block of its wiki `package.json`. Hover a card and dwell to zoom it large; hold Alt as you leave the card to pin the zoom on screen (click anywhere outside to dismiss); right-click to pin apps to the top of the rail.+The rail on the right edge of Hydrogen is the Dock Bar: the app launcher. Every card is a wiki app page. The dock shows the page's own name, brief, and hero, and launches it the way the app declares in a `dockbar.json` manifest at the root of its wiki page repo: install steps run in your container, the app is shown in a webview behind a free loading screen, a live health LED reports the app's own status, and the manifest can open an AI thread already oriented on the app, resuming your original conversation with all its context on a later launch.  The list has two layers: -- **The global list**, curated by the adom team in a world-readable manifest that Hydrogen polls (`https://wiki.adom.inc/blob/bootstrap/hydrogen-bootstrap/dock/apps.json`). Featured placement and ordering live only there.+- **The global list**, curated by the adom team in a world-readable manifest that Hydrogen polls (`https://wiki.adom.inc/blob/bootstrap/hydrogen-bootstrap/dock/apps.json`). It is only a list: featured placement and ordering live there, launch behavior always comes from each app's own `dockbar.json`. - **Your own additions**: Dock settings (the gear at the top of the rail), Add app, any public wiki page as `owner/slug`. Or just ask the AI: "add adom/orbital-lab to my dock." Local to you, pinnable, removable. -When a launch involves a webview, Hydrogen holds the panel on the Adom orbital loading animation (it runs transparent over your theme background) until the app is actually ready, with install and start steps ticking in a toast. Apps get this for free.--The rail is yours to size: drag its inner edge from a slim strip up to the full cards shown here, and every card is a live wiki page (hero, name, brief, badges, version) rather than a bare icon.+The rail is yours to size: drag its inner edge from a slim strip up to full cards, and every card is a live wiki page (hero, name, brief, version) rather than a bare icon.  ![The dock: every card a live wiki app page, sized to full cards](screenshots/dock-rail.png) -Hover a card and dwell, and it zooms to a full billboard so you can actually read what the app is before launching; hold Alt as you leave the card to pin the zoom while you decide (click anywhere to dismiss).+Hover a card and dwell, and it zooms to a full billboard so you can actually read what the app is before launching; hold Alt as you leave the card to pin the zoom while you decide (click anywhere to dismiss); right-click to pin apps to the top of the rail.  ![Dwell on a dock card and it zooms to a full billboard](screenshots/dock-zoom.png) -**Publishing an app?** The authoring contract (the `dock` block, launch steps, `--print-url`, timeouts, the publisher checklist) is the `hydrogen-dock` skill that ships in every Hydrogen container, also readable in the adom/hydrogen-bootstrap page repo under `skills/hydrogen-dock/`. To request a spot on the global list, open an issue on this page.+### Feature guide++| guide | what it covers |+|---|---|+| [The Dock Bar](docs/dockbar.md) | The full tour with screenshots of everything the Dock Bar does (the interstitial, webview takeover, container launch steps, AI threads new and resumed, health LEDs, honest failures, settings), plus the complete `dockbar.json` manifest reference and the publisher checklist for making YOUR app dock-ready. |++**Publishing an app?** Start with the [Dock Bar guide](docs/dockbar.md); the `hydrogen-dock` skill that ships in every Hydrogen container teaches your AI the same contract. To request a spot on the global list, open an issue on this page.  ## The Adom menu 
docs/dockbar.mdadded+163
@@ -0,0 +1,163 @@+# The Dock Bar++The Dock Bar is Hydrogen's app launcher: the rail of app cards on the right edge of the workspace. Every card is a wiki app page, and **any public wiki app can be a card**, including yours. One click installs the app into the user's container, starts it, shows it in a webview, and can open an AI thread already oriented on it. This guide is the complete picture: what a click does, every screenshot of the machinery working, the `dockbar.json` manifest in full, and how to make your own app dock-ready.++Everything below is real: every screenshot is the actual app running on a real machine, captured while the feature was being verified.++## The rail++![The Dock Bar rail with a live app](docs/img/dockbar/rail-live.png)++The rail, with KiCad running. Each card is built for recognition: the app's hero image, its name as the largest text on the card, a two-line brief, and one quiet metadata row (owner and version). The green LED beside the KiCad name is not decoration: it is live health, read from the app's own status endpoint every 15 seconds while the rail is open. A card without a running app shows no LED at all, which is the honest state. Featured apps carry an accent border; the rail collapses to an edge tab and can be pinned open from Dock settings.++![The rail at rest](docs/img/dockbar/rail-idle.png)++The same rail with nothing running: no LEDs lit, because the dock refuses to claim states it has not verified. Cards render from each app's wiki page (name, brief, hero), so a publisher updating their page updates every dock on the next poll without shipping anything.++## Hover: the read surface++![The hover preview](docs/img/dockbar/hover-preview.png)++Resting on a card opens the zoom preview. This is the deliberate split in the design: the card is the *recognize* surface and the preview is the *read* surface. The preview shows the full hero, the complete brief, and the catalog facts that would clutter a card at rail width: the `owner/slug`, author, stars, visibility, version, and the launch-mode chips (WEBVIEW, CONTAINER, AI THREAD). The mode badge in the corner summarizes how the app launches. The arm delay is user-tunable, and Alt pins the preview open.++## Click: the interstitial webview++![The interstitial during a launch](docs/img/dockbar/interstitial.png)++The instant a card is clicked, a webview tab opens with the launch interstitial: the workspace theme background, the Adom orbital animation, the app's name and brief resolved from its wiki page, and, when the manifest declares a favicon, the app's own icon on the tab from the very first frame. Apps bundle none of this; the loading screen is free. Below the orbital, the launch console streams every command and its container output live, so a user (or an AI ralph loop) can watch exactly what a launch is doing rather than staring at a spinner.++## The launch runs a CLI in the container++![Launch steps running in the container](docs/img/dockbar/cli-steps.png)++A `container_command` launch runs the manifest's steps in order inside the user's workspace container: typically an idempotent `adom-wiki pkg install` guarded by `skip_if`, then the app's own `serve --print-url`. The console shows each step's label, the exact command, and its stdout and stderr. The step that prints a URL is the integration contract: the dock scrapes the first URL off stdout, polls it until it answers (budgeted by that step's own `timeout_seconds`), and only then proceeds. In this shot the install step was skipped because the probe found the CLI already present, and the serve step is being health-checked.++## The CLI takes over the interstitial++![The app has taken over the webview](docs/img/dockbar/takeover.png)++When the served URL answers, the dock navigates the same webview from the interstitial to the app: this is `on_ready: "navigate_placeholder"`. The KiCad Dashboard shown here fully replaced its interstitial, and the card LED went green because the dock is now reading the app's own `/api/status`, which reports per-facet LEDs plus a top-level rollup the app controls. How the dock helps the CLI take over: the app does not need to know anything about webviews. It prints one reachable URL (the code-server proxy form, `http://localhost:7380/proxy/<port>/`, is the safe choice) and exits; the dock owns the navigation, the readiness gate, and the failure reporting.++## Opening an AI thread++![A staged AI prompt in a fresh thread](docs/img/dockbar/ai-thread-new.png)++A manifest's `ai` block opens an AI conversation as its own branch of the launch, alongside the webview rather than instead of it. Here KiCad's launch has finished, and a new thread holds the manifest's staged prompt in the composer, unsent, waiting for the user to review and press enter. The dock never picks the AI: it omits the type and the user's own `default_ai` setting decides. `submit: "staged"` is the default because the user stays in control; `submit: "auto"` exists for apps that are useless until the AI acts.++## Finding the original AI thread again++![The original thread resumed with its old context](docs/img/dockbar/ai-thread-resume.png)++The screenshot that matters most for real use. `thread: "existing"` means the app's *own* thread: the dock remembers each app's conversation by its stable id, which survives renames, tab moves, closing, and restarts. A week later, when the user clicks the card again and that thread is closed, the dock reopens the exact conversation from history, foregrounds it with all its old context on screen, and stages the fresh launch prompt into the same conversation. The launch console on the right narrates the reopen ("not open; resuming it from history... resumed this app's original thread") so the user understands why no new thread appeared. Their accumulated context is the value; the dock's job is to never lose it.++## Honest failure++![A launch failing loudly](docs/img/dockbar/honest-failure.png)++When a launch fails, the dock says so: the toast names the app and the URL that never answered, the interstitial stays on a failure status instead of navigating to a dead page, and the console holds the exact probe output. This replaced an earlier behavior where a dead launch still reported success; the dock now treats "started, but never answered" as the publisher-actionable diagnosis it is (the usual cause is a serve command that does not self-background, so the exec timeout kills the server it just started).++## Dock settings++![Dock settings](docs/img/dockbar/settings.png)++The gear at the top of the rail. Hover-zoom tuning, the three visibility behaviors (launch reveal, start collapsed, auto-collapse after launch: turn all three off to keep the rail pinned open), self-serve app adding by `owner/slug`, and a draft-manifest URL for testing curated-list changes against your own Hydrogen before they reach the fleet, with a DRAFT badge on the rail while active.++## The dockbar.json manifest++One file, `dockbar.json`, at the root of the app's wiki page repo. **This is the only name the dock reads.** The earlier `dock.json` spelling is not a fallback and not deprecated-but-working: support for it was deliberately removed, so a page still shipping it gets the plain-wiki-page card until it renames. Discovery: repo root first, then up to two folder levels deep (never `node_modules` or dot-folders), first match wins, resolved location cached for a day.++```json+{+  "dock_manifest": "1.2.0",+  "webview": { "title": "KiCad Dashboard", "favicon": "docs/img/dock-kicad.svg" },+  "health": { "url": "/api/status", "poll_seconds": 15, "timeout_ms": 2000 },+  "launch": {+    "mode": "container_command",+    "console": "show",+    "steps": [+      { "label": "Installing the KiCad bridge",+        "run": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; adom-wiki pkg install adom/kicad-bridge'",+        "skip_if": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; command -v kicad-dashboard'",+        "timeout_seconds": 180 },+      { "label": "Starting the KiCad Dashboard",+        "run": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; exec kicad-dashboard serve --print-url'",+        "await": "url",+        "timeout_seconds": 60 }+    ],+    "on_ready": "navigate_placeholder",+    "ai": {+      "prompt": "The user just opened the KiCad Dashboard from the dock...",+      "thread": "existing",+      "thread_fallback": "new",+      "submit": "staged"+    }+  }+}+```++### webview++| Field | Required | Meaning |+|---|---|---|+| `title` | yes | The webview tab title, and the handle the dock uses to find the tab again on a re-click. Keep it stable across versions or users get duplicate tabs |+| `favicon` | no | Tab icon, a path in the page repo or an absolute https URL. Shown on the interstitial from the first frame |+| `url` | `mode: "webview"` only | The page to load directly |++### launch++| Field | Default | Meaning |+|---|---|---|+| `mode` | | `webview` (load `webview.url`, no steps), `container_command` (run steps, then show the URL), or `ai_thread` (no webview, straight to a thread) |+| `console` | `"show"` | `"hide"` mutes the healthy transcript under the orbital. Failures are never muted |+| `on_ready` | `"navigate_placeholder"` | Navigate the interstitial to the produced URL, or `"hand_to_ai"` to let a thread take the webview over |+| `steps[]` | | `label`, `run`, optional `skip_if` (exit 0 skips), optional `await: "url"` + `url_pattern`, `timeout_seconds` (also governs the readiness wait on an awaiting step) |++### health++| Field | Default | Meaning |+|---|---|---|+| `url` | | Health endpoint, resolved against the app's *served path*, so `/api/status` behind `.../proxy/8931/` probes `.../proxy/8931/api/status`, never the server root |+| `poll_seconds` | 15 | Poll cadence while the rail is open. Polling stops when the rail is collapsed |+| `timeout_ms` | 2000 | A probe that overruns is `unknown` (grey), never red |++The app returns per-facet LEDs (`{label, state: ok|warn|fail, detail, _hint}`) plus a top-level `state` that is its own rollup and wins; without one the dock takes the worst facet. The dock owns the colors (green, yellow, red, blinking accent while a launch is in flight); apps own the semantics. Blinking is deliberately not declarable by apps.++### ai++| Field | Default | Meaning |+|---|---|---|+| `prompt` | app-naming default | The pre-canned prompt |+| `thread` | `"new"` | `"existing"` reuses this app's own thread, resuming it from history when closed |+| `thread_fallback` | `"new"` | When `existing` finds nothing: `new`, or `current` (whatever conversation is open) |+| `submit` | `"staged"` | `staged` leaves the prompt for the user to send; `auto` sends immediately |++### The three rules that break every first attempt++Each of these was learned from a bundled bridge shipping it wrong:++1. **Serve must self-background.** The dock runs steps synchronously and waits for exit; a foreground server is killed at the timeout and its URL is dead before the readiness probe runs. Print the URL, fork, exit 0.+2. **Steps must set PATH.** Wiki CLIs land in `~/.local/bin`, which is not on the exec shell's PATH. This bug's signature is works-for-the-author, fails-for-every-user, because on the dev box the command is already on PATH and `skip_if` lies in your favor.+3. **Serve must be idempotent.** The serve step runs on *every* launch; with an instance already up, detect it, print its URL, exit 0.++## The two-layer list++The curated global list (`dock/apps.json` in adom/hydrogen-bootstrap) is a list and nothing more: which apps appear, in what order, and which are featured. It carries no launch blocks, deliberately, because an inline launch there would override and freeze the manifest the app maintains. Any user can also add any public wiki app from Dock settings by `owner/slug`; being curated is not required to be dock-ready.++## Make your own app dock-ready++1. Public wiki page with a hero and a one-line brief; the dock shows exactly these.+2. `dockbar.json` at the page repo root, per the reference above.+3. Serve verb: prints a reachable URL, self-backgrounds, idempotent.+4. A fast `/api/status` (cache the reading; answer in milliseconds) and a top-level `state`.+5. A favicon that actually exists at the declared path.+6. Test from Dock settings, Add app, `owner/slug`, on a machine that has never had your app installed.++Reference implementation: adom/kicad-bridge, verified green end to end. The dock's own implementation notes live in the Hydrogen source at `docs/DOCK-BAR.md`.++## Talk to the AI++- "add adom/kicad-bridge to my dock"+- "make my app dock-ready" / "write me a dockbar.json"+- "why does my dock card not work" (the AI walks the three rules and the console output)+- "keep the dock bar pinned open"+- "point my dock at a draft apps.json"
docs/img/dockbar/ai-thread-new.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/ai-thread-resume.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/cli-steps.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/honest-failure.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/hover-preview.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/interstitial.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/rail-idle.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/rail-live.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/settings.pngadded
⋯ 1 unchanged line ⋯
docs/img/dockbar/takeover.pngadded
⋯ 1 unchanged line ⋯