npx skills add ...
npx skills add starchild-ai-agent/official-skills --skill browser-preview
Preview panel and iframe diagnostics: registry, ports, paths, blank screens.
npx skills add starchild-ai-agent/official-skills --skill browser-preview
You already know preview_serve and preview_stop. This skill fills the gap: what happens after preview_serve returns a URL — how the user actually sees it.
The frontend has a right-side panel with two tabs: Files and Preview. The Preview tab renders preview URLs inside an iframe. When you call preview_serve, the frontend automatically opens a Preview tab loading that URL. (Jobs are now managed in a standalone modal accessible from the left sidebar, not in the right panel.)
Key facts:
preview_serve call creates one Preview tabhttps://<host>/preview/{id}/The user's browser CANNOT access localhost or 127.0.0.1. These addresses point to the server container, not the user's machine. The preview architecture uses a reverse proxy:
Rules:
http://localhost:{port} or http://127.0.0.1:{port} — they cannot reach it/preview/{id}/ (or the full URL https://<host>/preview/{id}/)curl http://localhost:{port} is for your own server-side diagnostics only — never suggest it to the user as a way to "test" the previewurl field returned by preview_serve (format: /preview/{id}/)Because previews are served under /preview/{id}/, absolute paths in HTML/JS/CSS will break. The reverse proxy strips the /preview/{id} prefix before forwarding to the backend, but the browser resolves absolute paths from the domain root.
Example of the problem:
Common patterns to fix:
| Broken (absolute) | Fixed (relative) |
|---|---|
"/static/app.js" | "static/app.js" or "./static/app.js" |
"/api/users" | "api/users" or "./api/users" |
"/images/logo.png" | "images/logo.png" or "./images/logo.png" |
url('/fonts/x.woff') | url('./fonts/x.woff') |
fetch('/data.json') | fetch('data.json') |
Check ALL places where paths appear:
src, href attributesfetch(), XMLHttpRequest, dynamic importsurl() references'/static/' in template strings or concatenation)publicPath, base, assetPrefix)⚠️ Be thorough — it's common to fix CSS url() but miss JS string literals like '/static/' (with single quotes). Search for ALL occurrences of absolute paths across all file types.
Never look at workspace directories like preview/, output/, or random folders to understand preview state. Those are user data, not preview service state.
The only sources of truth:
/data/previews.json (running services)/data/preview_history.json (all past services)preview_serve / preview_stop toolscurl (server-side only, for your diagnostics)Do NOT use ls/find on workspace directories to diagnose preview issues. Do NOT call unrelated tools like list_scheduled_tasks. Stay focused.
When a user reports any Preview panel problem, follow this exact sequence:
⚠️ Your bash CWD is /data/workspace/. The registry is at /data/previews.json (absolute path, one level up). Always use the absolute path.
JSON structure:
If registry has entries → Go to Step 3 (verify services) If registry is empty or missing → Go to Step 4 (check history)
For each preview in the registry, check if the port is responding server-side (this is your diagnostic, not for the user):
If port responds (200):
/preview/{id}/preview_stop(id) then preview_serve(dir, title, command) using the info from the registry. This forces the frontend to re-register the tab.If port does NOT respond:
When there are no running services, use a two-tier lookup to find projects the user can preview:
JSON structure:
History entries are never removed by preview_stop — they persist across restarts. Entries are automatically pruned only when the project directory no longer exists.
If history has entries:
preview_serve with the dir, title, and command from the history entryIf user says a project is missing from history → fall through to Tier 2.
Then:
preview_serve with the appropriate directoryDon't just say "no services running" and stop. Always check history first, then scan, and offer options.
| User says | You do |
|---|---|
| "tab disappeared" / "tab 不见了" | Step 1 → 2 → 3 or 4 |
| "blank page" / "白屏" | Check port (server-side), if dead → recreate; if alive → check for absolute path issues |
| "not updating" / "内容没更新" | Suggest refresh button in Preview tab, or recreate preview |
| "port conflict" / "端口冲突" | preview_stop old → preview_serve new |
| "can't see service" / "⋮ menu empty" | preview_stop + preview_serve to force re-register |
| "where's my project" / "what did I build" | Read /data/preview_history.json and list entries |
| "resource load failed" / "JS/CSS 404" | Check for absolute paths (/static/, /api/), fix to relative paths |
When you can't do something, tell the user the manual action (e.g., "click refresh in Preview tab"). If manual action doesn't work, recreate the preview with preview_stop + preview_serve.
url() paths but missing JS string literals with absolute paths/preview/{id}/ as the user-facing URLcurl localhost:{port} only for your own server-side diagnosticspreview_stop + preview_serve to restart, then tell user to check Preview panel