ideabrowser.com — find trending startup ideas with real demand
Try itnpx skills add https://github.com/starchild-ai-agent/official-skills --skill browser-previewYou 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: Workspace and Browser. Browser renders preview URLs inside an iframe. When you call preview_serve, the frontend automatically opens a Browser tab loading that URL.
Key facts:
preview_serve call creates one Browser 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:
User's Browser → https://<host>/preview/{id}/path → (reverse proxy) → 127.0.0.1:{port}/path
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:
<!-- ❌ BROKEN: browser requests https://host/static/app.js → 404 (bypasses preview proxy) -->
<script src="/static/app.js"></script>
<!-- ✅ WORKS: browser requests https://host/preview/{id}/static/app.js → proxied correctly -->
<script src="static/app.js"></script>
<script src="./static/app.js"></script>
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 Browser problem, follow this exact sequence:
cat /data/previews.json 2>/dev/null || echo "NO_REGISTRY"
⚠️ 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:
{
"previews": [
{"id": "f343befc", "title": "My App", "dir": "/data/workspace/my-project", "command": "npm run dev", "port": 9080, "is_builtin": false}
]
}
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):
curl -s -o /dev/null -w "%{http_code}" http://localhost:{port}
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:
preview_stop(id="{id}")
preview_serve(dir="{dir}", title="{title}", command="{command}")
When there are no running services, use a two-tier lookup to find projects the user can preview:
cat /data/preview_history.json 2>/dev/null || echo "NO_HISTORY"
JSON structure:
{
"history": [
{
"id": "f343befc",
"title": "Trading System",
"dir": "/data/workspace/my-project",
"command": "python main.py",
"port": 8000,
"is_builtin": false,
"created_at": 1709100000.0,
"last_started_at": 1709200000.0
}
]
}
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.
find /data/workspace -maxdepth 2 \( -name "package.json" -o -name "index.html" -o -name "*.html" -o -name "app.py" -o -name "main.py" -o -name "vite.config.*" \) -not -path "*/node_modules/*" -not -path "*/skills/*" -not -path "*/memory/*" -not -path "*/prompt/*" -not -path "*/.git/*" 2>/dev/null
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 Browser 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 Browser 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 Browser panel