| name | run-local-uis |
| description | Start all five UIs (landing + drive-ui + provider-dashboard + s3-ui + photos) locally with hot reload and print the ports table. Use when the user says "run locally", "run the UIs", "start the UIs", "spin up the UIs", or similar — covers any time the goal is to drive the user-interfaces apps in a browser, including verifying changes to the shared network-picker, the landing page, or any individual UI. |
Spin up every user-interfaces/ app on its dedicated port with Vite HMR, then
print the ports table so the user can click straight into a browser. The
landing page needs special handling — it's a static HTML file with sentinel
placeholders that production replaces at build time — so we serve it through
a small Vite config kept alongside this skill that does the substitution
in-memory and rewrites the card links to point at the local dev ports.
Ports
These ports are the canonical ones — user-interfaces/README.md and each
app's vite.config.ts are aligned to them. 5176 is reserved for the landing
page by this skill.
Steps
-
Check what's already up. Be idempotent — don't restart servers the user
already has running:
for p in 5174 5175 5176 5177 5178; do
ss -ltn "sport = :$p" 2>/dev/null | grep -q LISTEN && echo "$p: up" || echo "$p: free"
done
-
Free ports → start that app, leave the rest alone. Launch each as a
detached background process so it survives the turn. Log to
/tmp/run-local-uis/<app>.log:
mkdir -p /tmp/run-local-uis
cd /home/bparity/parity/web3-storage/user-interfaces
nohup pnpm --filter @web3-storage/drive-ui run dev -- --host 127.0.0.1 \
> /tmp/run-local-uis/drive-ui.log 2>&1 &
disown
nohup pnpm --filter provider-dashboard run dev -- --host 127.0.0.1 \
> /tmp/run-local-uis/provider.log 2>&1 &
disown
nohup pnpm --filter @web3-storage/s3-ui run dev -- --host 127.0.0.1 \
> /tmp/run-local-uis/s3-ui.log 2>&1 &
disown
nohup pnpm --filter @web3-storage/photos run dev -- --host 127.0.0.1 \
> /tmp/run-local-uis/photos.log 2>&1 &
disown
nohup ./s3-ui/node_modules/.bin/vite \
--config /home/bparity/parity/web3-storage/.claude/skills/run-local-uis/landing-vite.config.mjs \
> /tmp/run-local-uis/landing.log 2>&1 &
disown
Skip any app whose port is already listening.
-
Wait briefly and verify each port is up. Vite usually binds within 2-5s
after pnpm install is already warm:
sleep 5
ss -ltn 2>/dev/null | grep -E ':51(74|75|76|77|78)\b'
If a port is still missing, tail -30 /tmp/run-local-uis/<app>.log to find
the failure (most common: pnpm install not yet run for the workspace, or
another process holding the port).
-
Confirm the landing page's substitution worked. The raw HTML contains
__NETWORKS_JSON__, __DEFAULT_NETWORK_ID__, __VALID_IDS_JSON__ and
relative ./console/ style links — if any of those survive into the served
HTML, the vite config didn't run:
body=$(curl -s --max-time 5 http://127.0.0.1:5176/)
echo "stale placeholders: $(grep -cE '__(NETWORKS_JSON|DEFAULT_NETWORK_ID|VALID_IDS_JSON)__' <<<"$body")"
echo "stale relative cards: $(grep -cE \"['\\\"]\\\\./(provider|drive|s3|photos)/['\\\"]\" <<<"$body")"
Both counts should be 0.
-
Print the table. Show the user the four URLs above as a compact markdown
table so they can click into any of them. Mention that hot reload is wired
for every app (Vite HMR for the React apps; the landing's HMR also covers
user-interfaces/shared/network-config/src/{networks,types}.ts via the
plugin's watcher).
-
Stopping. When the user wants to stop, kill by port (don't use
pkill -f vite.config.mjs — the pattern matches the kill command itself
and silently kills your shell):
for p in 5174 5175 5176 5177 5178; do
pid=$(ss -ltnp 2>/dev/null | sed -n "s/.*:$p .*pid=\([0-9]*\).*/\1/p" | head -1)
[ -n "$pid" ] && kill "$pid"
done
Notes
- Hot reload coverage: edits anywhere under each app's
src/ HMR-update
the app; edits in user-interfaces/shared/network-picker/src/ HMR-update
all four React apps that import it as source. The landing page's plugin
watches shared/network-config/src/{networks,types}.ts and triggers full
reload on change.
- Why a custom vite config for landing?
landing/index.html has sentinel
placeholders (__NETWORKS_JSON__, etc.) that
user-interfaces/landing/inject-config.mjs rewrites at build/deploy time.
Serving the raw file would surface JS syntax errors. The skill's config
reproduces the substitution as an in-memory transformIndexHtml plugin and
additionally rewrites the relative card links (./provider/, ./drive/,
./s3/) to absolute dev-server URLs so the landing's cards open the
local apps instead of falling through to the landing's own port.
- First-time setup (only if
pnpm install hasn't been run in this clone
yet): cd user-interfaces && pnpm install.