Regenerate the Launchpad Page
Regenerate the Launchpad Page
Section titled “Regenerate the Launchpad Page”The Launchpad is a single static HTML page listing every managed project with its dev / docs / prod / GitHub links and port band — built to be the browser’s default localhost site. The page is derived state with a single owner (the links index): it is never hand-edited, only regenerated.
Schema and derivation rules live in the Launchpad Links Index reference. Always-on serving is #1245; liveness badges are #1246. How the page looks — the colour tokens for each theme, type, motion, and what each liveness colour means — is set in the Launchpad design brief (#2144).
node ~/.claude/scripts/launchpad/render-page.js --out ~/.claude/launchpad/index.htmlThat one command derives the links index and renders the page. The project
list is this machine’s projects in the
fleet feed — the document the one dashboard
server, scripts/dashboard/server.js, serves at /fleet.json (#2081) — and
each project’s links come from declared state (ports ledger band,
projects.json, its dev.json / wrangler config / git remote). With no fleet
feed, every ledger project is listed; --ledger on links-index.js forces
that. Re-running is always safe: unchanged inputs produce a byte-identical
page.
When to regenerate
Section titled “When to regenerate”Whenever declared state changes — a project gains a port band, a dev.json,
a wrangler route, or a new repo remote:
- after
bob initprovisions a new project (port band allocated) - after adding or editing a project’s
dev.json - after a wrangler route / custom domain change
- after registering a project in
projects.json(the operator repo’s derived registry, #3432)
Two-step variant (inspect the index first)
Section titled “Two-step variant (inspect the index first)”node ~/.claude/scripts/launchpad/links-index.js --out /tmp/links.json # 1. generatejq '.projects[] | select(.name=="tct")' /tmp/links.json # 2. inspectnode ~/.claude/scripts/launchpad/render-page.js --index /tmp/links.json --out ~/.claude/launchpad/index.htmlrender-page.js with --index renders a prebuilt index verbatim — useful for
diffing what changed before republishing, or for rendering a snapshot from
another machine.
Verify
Section titled “Verify”open ~/.claude/launchpad/index.html # macOS; or use the always-on server (runbooks/launchpad-serving.md)make test-launchpad-links test-launchpad-pageIn the page: / focuses the filter, typing narrows by project name or stack
term, Enter opens the first match’s dev server, Escape clears. Light/dark
follows the browser theme.
Dev/docs pills carry a liveness dot — filled = up, red = down, hollow =
unknown — painted per page load from the server’s /liveness.json probe.
Opened as a plain file (no server), the dots simply stay hollow; see the
probe semantics.
The page also applies your layout after it paints — the view and each
project’s order and span, from ~/.claude/launchpad/layout.json via the
server’s /layout.json (#2139). The rendered file never changes with the
layout; a project you have not arranged appends in default order, and as a
plain file the page uses the layout the session last saw in localStorage,
never erroring. Contract, routes and merge rules: the
layout contract.
The header has a view switcher (#2141). Every view shows the same projects from the same page data: switching never reloads anything, and the filter you typed stays applied.
| View | Shortcut | Shows |
|---|---|---|
| Tiles | 1 |
The default: a grid of project tiles you can reorder and resize (below). |
| List | 2 |
One dense row per project — the original Launchpad layout, with the same filter and Enter behaviour. |
| Board | 3 |
Cards in columns: what is up, what you can start, what is down (#2142). See Board below. |
| Ports | 4 |
The ports ledger as a band strip: every project’s band, the free ports between them, and any collision (#2143). See Port map below. |
| Graph | 5 |
Projects as nodes, joined by what they share: stack, mirror host, neighbouring port bands (#2153). See Graph below. |
- Switching. Click a view in the switcher, or press
1–5anywhere except in the filter box (there the digits are part of your search). - Saved. The view you pick is saved as
viewin~/.claude/launchpad/layout.json, with your tile arrangement untouched, and the page opens in it next time. As a plain file it is kept in the browser’slocalStorage. - Force a view for one load. Add
?view=tiles,?view=list,?view=board,?view=portmapor?view=graphto the URL — for a bookmark or a test. The page opens in that view and saves nothing; your saved view is unchanged until you pick another one. An unknown value is ignored.
Board: what is running, and what can I start
Section titled “Board: what is running, and what can I start”The board (#2142) shows each project as a card in a column. Each card shows the project name (a link to its dev server), its port band and, where the project can be started from here, a start button. Each column has a count badge. A column with nothing in it stays on the board and says nothing here, so an empty column never looks like missing data.
Choose how the cards are grouped with group by above the board:
| Group by | Columns |
|---|---|
liveness (default) |
Up — the dev server answers. Startable — it is down, and the project declares a dev start path (dev.json). Down — it is down and cannot be started from here. Unknown — not probed yet, or no dev server declared. |
stack |
One column per stack (the project’s first stack term), then unassigned for projects that declare none. |
- Saved. The choice is saved as
board.groupByin~/.claude/launchpad/layout.json, next to your view and tile arrangement, and neither one resets the other. - Starting a project. Press start on a card in Startable. It is the
same start as the red dot in the list: the button pulses starting… while
bob dev upruns. A failure showsstart failed: …in the card, and the page then checks liveness again, so a project that came up moves to Up. - Cards move themselves. Cards are not dragged: the grouping is worked out from live state. When liveness changes, cards slide to their new column (or just land, with reduce motion on).
- Filter.
/filters the board too. The counts show the matching cards. - There is no
areagrouping: no project declares an area yet, and the board does not guess one.
Port map: which ports are taken
Section titled “Port map: which ports are taken”The port map (#2143) draws the ports ledger (ports.json in the operator
repo) as one strip, lowest port on the left. You see which bands are taken,
where the free ports are and who sits next to whom, without reading JSON.
It only shows the ledger: nothing on it can be edited.
| On the strip | Means |
|---|---|
| A filled segment | One project’s band, labelled with its name and start–end. |
| The bar at a band’s left edge | The band’s base port: the dev server. It turns green while the dev server answers. |
| A dashed, hollow segment | Free ports: no project’s band covers them. A long free run is drawn shorter than its real size, but its label gives the true range. |
| A striped segment | A collision: two or more projects declare bands that share these ports. |
- Links. Hover over a band, or move to it with
Tab, to see its dev, docs and dash links with their liveness dots. PressEnterto open its dev server. - Collisions. Every collision is also listed in a warning line above
the strip, naming both bands and the ports they share. The page never
fixes one. Change the band in
ports.json(see Port allocation), then regenerate the page. - Small screens. Below 600px wide the strip becomes a list: one row per band, with its links shown in the row.
- Where the data comes from. Only the links index: each project’s
baseand the index’sband_width, the ledger’s ports per band. The page and the server read no other file.
Graph: how projects relate
Section titled “Graph: how projects relate”The graph (#2153) draws every project as a node. A line joins two projects when the declared state says they have something in common. Nothing else is inferred, so a sparse graph is accurate.
| Edge | Drawn as | Joins two projects when |
|---|---|---|
| stack | solid, blue | They share stack terms (node, cloudflare-workers…). More shared terms make a thicker line. |
| mirror | dashed, amber | Both mirror to the same host in the operator’s mirrors.json. A project that is not mirrored has none. |
| ports | dotted, grey | Their port bands are next to each other in the ledger, touching or overlapping. A free gap between bands breaks the line. |
- Legend. The buttons above the graph show how many edges of each kind there are. Click one to hide that kind, and click it again to show it. Mirror edges are the ones to hide when every project mirrors to one host: they then join everything to everything.
- Nodes. A node with more links is bigger. A node whose dev server answers is green, slightly larger, and glows. Down and unknown nodes stay plain.
- Links. Hover over a node, or move to it with
Tab, to see its dev, docs and dash links with their liveness dots. PressEnterto open its dev server.Tabgoes through the nodes in the index’s order. - Pinning. Drag a node to place it. It stays there (a dashed ring marks
it), and the others settle around it. The position is saved as
graph.pinsin~/.claude/launchpad/layout.json, next to your view, tiles and board choice, and none of them resets the others. Double-click a pinned node to unpin it. - Movement. The page loads with the graph already laid out. Pins move their nodes into place and the rest settle around them within two seconds, then nothing moves. With reduce motion on, the graph is drawn already settled.
- Filter.
/dims the nodes that do not match, and every edge that does not touch a match. Nothing is hidden, so the graph keeps its shape.Escapeclears it. - Screen readers. A hidden list under the graph names each project and what it is joined to, edge kind by edge kind.
- Where the data comes from. Only the links index: each project’s
stack,mirror_hostandbase, and the index’sband_width. The layout is computed in the page from a fixed seed, so a given index always draws the same graph.
Tiles: reorder and resize
Section titled “Tiles: reorder and resize”The page opens as a grid of project tiles (#2140). Each tile shows the project name, its port band, the link pills with their liveness dots (the start dot where the project is startable) and its stack terms. Arrange them to suit you; the arrangement is saved and comes back on the next load.
| To | Do |
|---|---|
| Move a tile | Press its grab handle ⠿ and drag. A dashed placeholder shows where it will land; release to drop it there. |
| Move a tile from the keyboard | Focus the tile (Tab), then press Alt+← or Alt+→. |
| Widen or narrow a tile | Click its resize handle ⇲ to cycle 1 → 2 → 3 columns, or drag the handle sideways by the columns you want. A three-column tile puts all its links on one row. |
| Find a project | Press / and type. Non-matching tiles hide; the saved order is untouched. Enter opens the first visible tile’s dev server, Escape clears. |
- Where it is saved. Served by the Launchpad server, every move or
resize is saved to
~/.claude/launchpad/layout.jsonthroughPOST /action/layout. Opened as a plain file, it is kept in the browser’slocalStoragefor that browser only. To start over, delete the file (or clear the site’s storage): every tile goes back to default order at one column. - Filtered moves. With a filter on,
Alt+←/→steps past the next visible tile; a hidden tile keeps its place. - Touch. Drag the grab handle with a finger; the handles never scroll the page. Under 480px wide the grid is one column and dragging still works.
- Motion. Moves and resizes slide into place in under 200 ms. With the system’s reduce motion setting on, nothing animates; tiles just land.
Verify it with make test-launchpad-page. It pins the tile and switcher markup and the
grid rules, and drives the page’s own script for drag, keyboard, resize,
filter, reduced motion, the view switcher and the ?view= override;
make test-launchpad-board does the same for the board — grouping, columns,
the group key, card moves and the start button.
Keyboard
Section titled “Keyboard”Every view answers the same keys. Outside a text box:
| Key | Does |
|---|---|
⌘K (macOS) / Ctrl+K |
Open the command palette (below). Works from any view and from inside the filter box. |
/ |
Focus the filter. Type to narrow the projects; Enter opens the first match’s dev server, Escape clears. |
1–5 |
Switch view: Tiles, List, Board, Ports, Graph. |
Tab |
Walk the tiles (or the graph’s nodes). |
Alt+← / Alt+→ |
Move the focused tile. |
Command palette
Section titled “Command palette”⌘K / Ctrl+K opens a search box over everything the page can act on
(#2155). Type part of a name and act on a match without leaving the
keyboard.
| Type | To find | Enter does |
|---|---|---|
A project name: alp |
The project, then its links | Opens its dev server (its first link when it has none) |
A name and a link kind: bravo docs |
That one link (dev, docs, dash, prod, repo, issues, ci, board) | Opens the link |
A port number: 6313 |
The link served on that port; a project by its band’s base port | Opens the link |
A stack term: workers |
The projects with that term | Opens the dev server |
start bravo |
Start a project that declares a dev start path (a dev.json) | Starts its dev server, as clicking the red dev dot does; the outcome shows on the project’s tile |
A view name: graph |
The view | Switches to it, and saves it as your view |
- Moving. The best match is selected as you type.
↓/↑move the selection,Enteracts on it, and⌘Enter/Ctrl+Enteropens a link in a new tab. Clicking a match acts on it too. - Matching. A match at the start of a name ranks first, then one at the start of a word, then one inside a word, then the letters typed in order anywhere in the name. Equal matches follow your tile order.
- Dots. Projects and their dev, docs and dash links carry the same liveness dots as the page: green is up, red is down.
- Closing.
Escape(or a click outside the box) closes the palette and puts focus back where it was. Opening and closing never scroll the page or change the view. - The filter is separate. The palette searches every project, whatever
the filter hides, and leaves the filter as it was. Inside the palette,
/and the digits are search text.
Verify it with make test-launchpad-palette (the items and the ranking) and
make test-launchpad-palette-page (the palette in a real browser: keys,
focus, dots, start, views).
- Never edit the generated HTML. Fix the declared source (dev.json, wrangler config, ports ledger, projects.json) and regenerate.
- Nothing is guessed. A project missing a prod URL or docs link means no route / docs block is declared — declare it, then regenerate.
- The renderer adds no state of its own: no timestamps, no counters. Diffs of the output always reflect a diff of declared inputs.