Three small things. Removed `.claude-plugin/marketplace.json`. It is the file `/plugin marketplace add` reads, so without it this repo no longer offers itself as a marketplace — which is the odd part of an Angular app carrying one. The caveman plugin's own files stay, so it can be listed from a marketplace of its own later; only the listing is gone. That left four documents asserting an install path that no longer exists, including a README section handing out `/plugin marketplace add` and `/plugin install` commands that would now fail. All four now say what is actually true. `.junie/plans/nasa-star-map.md` still describes the repo as containing only a marketplace, and is left alone: it records what was here before the app was written, and is not a claim about the present. The run skill's numbers had drifted a release behind — 496 tests in 28 files against a real 527 in 31, and a build timed at 9s that now takes 12. Measured rather than guessed. The pinned Playwright version it names was correct. It also gains the Pages build, which is not a plain `npm run build`: the base href and the 404.html copy are both required for a project site, and the artifact root is `dist/star-map/browser` rather than its parent. Plus the matching gotcha, since a Pages build served at `/` looks broken in a way that tells you nothing about whether it would work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G
9.5 KiB
name, description
| name | description |
|---|---|
| run-star-map | Build, launch, run, drive, and screenshot the star-map Angular app in this container. Use when asked to run or start the app, take a screenshot of the map or HUD, check a UI change in a real browser, or run the unit/e2e test suites. |
Running star-map
An Angular 3D star map (three.js, WebGPU renderer falling back to WebGL2). There is no GPU here, so the scene is software-rasterized and slow — but it does run, and it screenshots.
Drive it with the committed driver, which owns the dev server, the browser, and the camera flights:
.claude/skills/run-star-map/driver.mjs
All paths below are relative to the repo root.
Prerequisites
Two container quirks have to be handled first. Both are one-liners, and both are silent footguns if skipped — see Gotchas for why.
1. Node. The default node on PATH is below the floor the Angular CLI enforces. A
satisfying build is at /opt/node. The driver picks it automatically; anything you run by hand
needs the prefix:
node -v # v22.22.2 ← too old, ng refuses to start
/opt/node/bin/node -v # v22.23.2 ← use this
export PATH=/opt/node/bin:$PATH
2. Playwright browser. The pinned @playwright/test (1.62.1) wants Chromium revision 1234;
this container ships 1194, under a different internal layout. Do not run
npx playwright install (the environment blocks browser downloads). Shim the expected path
instead — needed only for npm run e2e, not for the driver:
mkdir -p /opt/pw-browsers/chromium_headless_shell-1234/chrome-headless-shell-linux64
ln -sfn /opt/pw-browsers/chromium_headless_shell-1194/chrome-linux/headless_shell \
/opt/pw-browsers/chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell
touch /opt/pw-browsers/chromium_headless_shell-1234/INSTALLATION_COMPLETE
Install
PATH=/opt/node/bin:$PATH npm ci # ~25s
Run (agent path) — the driver
Takes screenshots of the app at each scale. Starts the dev server on port 4300 if one isn't already up, and stops it on exit.
node .claude/skills/run-star-map/driver.mjs tour
starting dev server on http://localhost:4300 (node v22.23.2)
dev server ready
field:
/tmp/star-map-shots/1-star-field.png (level=Solar Neighbourhood)
galaxy:
/tmp/star-map-shots/2-galactic.png (level=Milky Way)
system:
/tmp/star-map-shots/3-system-sol.png (level=System)
inner:
zoomed to 5.53 AU
/tmp/star-map-shots/4-system-inner.png (level=System)
tour takes ~3m20s — most of it camera flights and software rasterization. For one view:
node .claude/skills/run-star-map/driver.mjs shot galaxy # ~1m
node .claude/skills/run-star-map/driver.mjs shot field system
| View | What it shows |
|---|---|
field |
Solar-neighbourhood star field — the default landing view |
galaxy |
Galactic scale: arms, bar, Sol's position |
system |
Sol at ~120 AU, outer planets labelled |
inner |
Sol zoomed to <6 AU, inner four planets labelled |
Look at the PNG you produced. A black frame means the scene never initialized — that is a failure, not a dark theme.
Flags: --out=DIR (default /tmp/star-map-shots), --stars=N (default 12000), --port=N
(default 4300), --keep-server, --headed.
Checking HUD state without screenshots
Faster than eyeballing a picture, and the right tool for label/HUD changes:
node .claude/skills/run-star-map/driver.mjs probe inner
{
"title": "Sol",
"level": "System",
"labelCount": 4,
"labels": [
{ "name": "Mars", "kind": "Planet" },
{ "name": "Earth", "kind": "Planet" },
{ "name": "Mercury", "kind": "Planet" },
{ "name": "Venus", "kind": "Planet" }
]
}
probe accepts the same four view names. Labels are read from the CSS2D layer
(.map-label > .map-label-name + .map-label-kind).
Run (human path)
PATH=/opt/node/bin:$PATH npm start -- --port=4300
Serves on http://localhost:4300. Useless headless on its own — there is no display to look at. Use it only when you want a long-lived server for the driver to attach to (the driver reuses an already-running server and leaves it alone on exit).
Test
PATH=/opt/node/bin:$PATH npm test # 527 tests, 31 files, ~6s
PATH=/opt/node/bin:$PATH npm run build # ~12s
PATH=/opt/node/bin:$PATH npm run etl:typecheck
PATH=/opt/node/bin:$PATH npm run e2e:typecheck
PATH=/opt/node/bin:$PATH npm run e2e -- --workers=1 # 6 tests, 3 files, ~2.3m
--workers=1 on the e2e suite is not optional here — see Gotchas.
Build for GitHub Pages
.github/workflows/pages.yml publishes the app on a push to main. To reproduce what it builds:
export PATH=/opt/node/bin:$PATH
npm run build -- --base-href "/star-map/"
cp dist/star-map/browser/index.html dist/star-map/browser/404.html
Two things differ from a plain npm run build, and both exist because a project site is served
from a subdirectory rather than a domain root:
--base-href.DataLoaderServicefetches its catalogues with relative URLs (assets/data/stars.bin), which resolve against<base>rather than the current path. Get it wrong and a deep link asks for/body/assets/data/stars.bin. The workflow derives it from the repository name so a rename cannot strand it.404.html. Pages has no rewrite rules, so/body/marshas no file behind it. Serving the app as the 404 body lets the router render the route. The response stays HTTP 404.
The uploaded artifact is dist/star-map/browser, not dist/star-map — the parent also holds
3rdpartylicenses.txt and prerendered-routes.json, which are not part of the site.
Gotchas
-
The wrong Node is first on
PATH./opt/node22is v22.22.2;/opt/nodeis v22.23.2.PATHfinds the older one, andpackage.json#enginesrequires^22.22.3.ng servethen dies with "Node.js version v22.22.2 detected" and nothing else. The names invert what you'd guess —/opt/nodeis newer than/opt/node22. -
npm run e2efails at full parallelism, passing 4 of 6. The two camera-flight specs time out when four browsers share a software rasterizer. The same specs pass alone and pass with--workers=1. It is CPU contention, not a broken test — don't "fix" the specs. -
Never stop the dev server with
pkill -f. It matches against every process's full command line — including the shell running your ownpkill, which then kills itself (exit 144). Narrowing the pattern does not save you, and neither does the[n]gbracket trick: the match is against the whole command line, so a literalng serveanywhere else in it — inside anecho, a comment, a laterpgrep— re-arms the self-match. Kill by port instead, which does no pattern matching at all:fuser -k 4300/tcpOr just let the driver clean up after itself, which it does unless you pass
--keep-server. -
The star count must be cut for anything interactive. The full catalogue is 68 388 stars and runs at roughly 1 fps here.
?stars=Noverrides it; the driver defaults to 12 000. -
One canvas click is not enough to enter a system. Bootstrap (data fetch + raycaster wiring) finishes asynchronously after load, so an early click hits nothing. The driver polls click-until-
hud-current-level-reads-System. Sol is at the origin, dead centre of the default camera, so the centre click is what reliably works. -
Screenshots need a settle delay. A DOM assertion passing does not mean the frame is drawn; the driver waits 2.5s before every capture. Without it you get half-rendered scenes.
-
At 120 AU the inner planets have no labels. They are inside the centre reticle, collapsed to a few pixels, and correctly suppressed. This is not a regression — zoom below ~6 AU (the
innerview) to see Mercury/Venus/Earth/Mars labelled. -
Port 4300, not Angular's usual 4200 — the project's own convention, set in
playwright.config.tsso it never collides with an unrelatedng serve. -
A Pages build looks broken if you serve it at the root.
<base href="/star-map/">makes every asset resolve under that prefix, so openingdist/star-map/browserat/gets you a blank page and a wall of 404s. Serve it under the same prefix as Pages does, with unknown paths falling back to404.html, or the check tells you nothing. -
Don't write driver scripts in
.tsoutside the repo.tsxcompiles a bare.tsas CJS ("Top-level await is currently not supported with the cjs output format"), and a script outside the repo can't resolve@playwright/testat all. The driver is.mjsinside the repo for both reasons.
Troubleshooting
| Symptom | Fix |
|---|---|
Node.js version v22.22.2 detected. The Angular CLI requires... |
export PATH=/opt/node/bin:$PATH |
browserType.launch: Executable doesn't exist at .../chromium_headless_shell-1234/... |
Apply the browser shim in Prerequisites. Do not run npx playwright install. |
| e2e: 2 specs time out on camera flights | Add -- --workers=1 |
| Your shell exits with code 144 while cleaning up | A pkill -f matched your own command line. Use fuser -k 4300/tcp. |
| Screenshot is entirely black | Scene never initialized — check the driver's [pageerror] lines. |
Top-level await is currently not supported with the "cjs" output format |
Name the script .mjs (or .mts) and keep it inside the repo. |
| Driver hangs at "starting dev server" | Another server holds port 4300: fuser -k 4300/tcp, or pass --port=4301. |