browser-preview

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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.
你已经了解
preview_serve
preview_stop
。本内容填补了空白:当
preview_serve
返回URL之后会发生什么——用户实际是如何查看预览的。

What is Browser

什么是Browser

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:
  • Each
    preview_serve
    call creates one Browser tab
  • URL format:
    https://<host>/preview/{id}/
  • Browser panel has a ⋮ menu (top-right) showing "RUNNING SERVICES" list
  • Browser tab can be closed by the user without stopping the backend service
  • Backend service stopping → Browser tab shows an error page
前端右侧面板有两个标签页:WorkspaceBrowser。Browser标签页通过iframe渲染预览URL。当你调用
preview_serve
时,前端会自动打开加载该URL的Browser标签页。
关键信息:
  • 每次调用
    preview_serve
    会创建一个Browser标签页
  • URL格式:
    https://<host>/preview/{id}/
  • Browser面板右上角有一个**⋮菜单**,显示“RUNNING SERVICES”(运行中的服务)列表
  • 用户可以在不停止后端服务的情况下关闭Browser标签页
  • 后端服务停止后,Browser标签页会显示错误页面

⚠️ CRITICAL: Never Tell Users to Access localhost

⚠️ 重要提醒:切勿告知用户访问localhost

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:
  • NEVER tell the user to visit
    http://localhost:{port}
    or
    http://127.0.0.1:{port}
    — they cannot reach it
  • ALWAYS direct users to the preview URL:
    /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 preview
  • When a preview is running, tell the user: "Check the Browser panel, or refresh the Browser panel"
  • If you need to give the user a URL, use the
    url
    field returned by
    preview_serve
    (format:
    /preview/{id}/
    )
用户的浏览器无法访问
localhost
127.0.0.1
。这些地址指向服务器容器,而非用户的本地机器。预览架构采用反向代理
用户浏览器 → https://<host>/preview/{id}/path → (反向代理) → 127.0.0.1:{port}/path
规则:
  • 绝对不要告知用户访问
    http://localhost:{port}
    http://127.0.0.1:{port}
    ——他们无法访问
  • 务必引导用户使用预览URL:
    /preview/{id}/
    (或完整URL
    https://<host>/preview/{id}/
  • curl http://localhost:{port}
    仅用于你自己的服务器端诊断——切勿建议用户以此方式“测试”预览
  • 当预览运行时,告知用户:“请查看Browser面板,或刷新Browser面板”
  • 若需要给用户提供URL,请使用
    preview_serve
    返回的
    url
    字段(格式:
    /preview/{id}/

⚠️ Static Assets Must Use Relative Paths

⚠️ 静态资源必须使用相对路径

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:
html
<!-- ❌ 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:
  1. HTML
    src
    ,
    href
    attributes
  2. JavaScript
    fetch()
    ,
    XMLHttpRequest
    , dynamic imports
  3. CSS
    url()
    references
  4. JavaScript string literals (e.g.,
    '/static/'
    in template strings or concatenation)
  5. Framework config files (e.g.,
    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.
由于预览通过
/preview/{id}/
路径提供服务,HTML/JS/CSS中的绝对路径会导致页面失效。反向代理在转发请求到后端前会移除
/preview/{id}
前缀,但浏览器会从域名根目录解析绝对路径。
问题示例:
html
<!-- ❌ 失效:浏览器请求https://host/static/app.js → 404(绕过预览代理) -->
<script src="/static/app.js"></script>

<!-- ✅ 有效:浏览器请求https://host/preview/{id}/static/app.js → 正确代理 -->
<script src="static/app.js"></script>
<script src="./static/app.js"></script>
修复方案:
失效(绝对路径)有效(相对路径)
"/static/app.js"
"static/app.js"
"./static/app.js"
"/api/users"
"api/users"
"./api/users"
"/images/logo.png"
"images/logo.png"
"./images/logo.png"
url('/fonts/x.woff')
url('./fonts/x.woff')
fetch('/data.json')
fetch('data.json')
检查所有出现路径的位置
  1. HTML的
    src
    href
    属性
  2. JavaScript的
    fetch()
    XMLHttpRequest
    、动态导入
  3. CSS的
    url()
    引用
  4. JavaScript字符串字面量(例如模板字符串或拼接中的
    '/static/'
  5. 框架配置文件(例如
    publicPath
    base
    assetPrefix
⚠️ 务必彻底检查——常见问题是修复了CSS的
url()
但遗漏了JS中的
'/static/'
字符串字面量(带单引号)。搜索所有文件类型中的绝对路径实例。

⚠️ Do NOT Browse Filesystem to Debug Previews

⚠️ 请勿通过浏览文件系统调试预览

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:
  1. Registry file:
    /data/previews.json
    (running services)
  2. History file:
    /data/preview_history.json
    (all past services)
  3. preview_serve
    /
    preview_stop
    tools
  4. Port checks via
    curl
    (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.
绝对不要查看
preview/
output/
或随机文件夹来了解预览状态。这些是用户数据,而非预览服务状态。
唯一可信的信息来源:
  1. 注册表文件:
    /data/previews.json
    (运行中的服务)
  2. 历史文件:
    /data/preview_history.json
    (所有过往服务)
  3. preview_serve
    /
    preview_stop
    工具
  4. 通过
    curl
    检查端口(仅用于服务器端诊断)
请勿使用
ls
/
find
命令查看工作区目录来诊断预览问题。请勿调用无关工具如
list_scheduled_tasks
。请专注于相关操作。

Step-by-Step: Diagnosing Browser Issues

分步诊断浏览器问题

When a user reports any Browser problem, follow this exact sequence:
当用户报告任何Browser问题时,请严格按照以下步骤操作:

Step 1: Read the registry (running services)

步骤1:查看注册表(运行中的服务)

bash
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:
json
{
  "previews": [
    {"id": "f343befc", "title": "My App", "dir": "/data/workspace/my-project", "command": "npm run dev", "port": 9080, "is_builtin": false}
  ]
}
bash
cat /data/previews.json 2>/dev/null || echo "NO_REGISTRY"
⚠️ 你的bash当前工作目录是
/data/workspace/
。注册表位于
/data/previews.json
(绝对路径,上一级目录)。请始终使用绝对路径。
JSON结构:
json
{
  "previews": [
    {"id": "f343befc", "title": "My App", "dir": "/data/workspace/my-project", "command": "npm run dev", "port": 9080, "is_builtin": false}
  ]
}

Step 2: Branch based on registry state

步骤2:根据注册表状态分支处理

If registry has entries → Go to Step 3 (verify services) If registry is empty or missing → Go to Step 4 (check history)
若注册表有条目 → 进入步骤3(验证服务) 若注册表为空或不存在 → 进入步骤4(查看历史记录)

Step 3: Registry has entries — verify and fix

步骤3:注册表有条目——验证并修复

For each preview in the registry, check if the port is responding server-side (this is your diagnostic, not for the user):
bash
curl -s -o /dev/null -w "%{http_code}" http://localhost:{port}
If port responds (200):
  • The service IS running. Tell the user:
    • "You have a running service: {title}"
    • "Click the ⋮ menu at the top-right of the Browser panel, then click it in the RUNNING SERVICES list to reopen"
    • Preview URL:
      /preview/{id}/
  • If user says the ⋮ menu is empty or doesn't show the service → frontend lost sync. Fix by recreating:
    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:
  • Process crashed but registry entry remains. Recreate:
    preview_stop(id="{id}")
    preview_serve(dir="{dir}", title="{title}", command="{command}")
对于注册表中的每个预览,检查端口是否在服务器端响应(此操作仅用于你的诊断,不面向用户):
bash
curl -s -o /dev/null -w "%{http_code}" http://localhost:{port}
若端口响应(返回200):
  • 服务正在运行。告知用户:
    • "你有一个运行中的服务:{title}"
    • "点击Browser面板右上角的⋮菜单,然后在RUNNING SERVICES列表中点击该服务重新打开"
    • 预览URL:
      /preview/{id}/
  • 若用户表示⋮菜单为空或未显示该服务 → 前端失去同步。通过重新创建预览修复:使用注册表中的信息调用
    preview_stop(id)
    ,然后调用
    preview_serve(dir, title, command)
    。此操作会强制前端重新注册标签页。
若端口无响应:
  • 进程已崩溃但注册表条目仍存在。重新创建预览:
    preview_stop(id="{id}")
    preview_serve(dir="{dir}", title="{title}", command="{command}")

Step 4: No running services — check history first, then scan workspace

步骤4:无运行中的服务——先查看历史记录,再扫描工作区

When there are no running services, use a two-tier lookup to find projects the user can preview:
当没有运行中的服务时,使用两级查找找到用户可以预览的项目:

Tier 1: Read preview history (preferred — fast and accurate)

第一级:查看预览历史记录(优先选择——快速准确)

bash
cat /data/preview_history.json 2>/dev/null || echo "NO_HISTORY"
JSON structure:
json
{
  "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:
  • List all history entries to the user with title, directory, and last started time
  • Ask which one they want to restart
  • Call
    preview_serve
    with the
    dir
    ,
    title
    , and
    command
    from the history entry
If user says a project is missing from history → fall through to Tier 2.
bash
cat /data/preview_history.json 2>/dev/null || echo "NO_HISTORY"
JSON结构:
json
{
  "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
    }
  ]
}
历史记录条目不会被
preview_stop
删除
——会在重启后保留。仅当项目目录不存在时,条目才会被自动清理。
若历史记录有条目:
  • 向用户列出所有历史记录条目,包括标题、目录和最后启动时间
  • 询问用户要重启哪一个
  • 使用历史记录条目中的
    dir
    title
    command
    调用
    preview_serve
若用户表示历史记录中缺少某个项目 → 进入第二级查找。

Tier 2: Scan workspace (fallback — when history is empty or incomplete)

第二级:扫描工作区(备选方案——当历史记录为空或不完整时)

bash
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:
  1. List discovered projects with brief descriptions
  2. Ask the user which one to preview
  3. Call
    preview_serve
    with the appropriate directory
Don't just say "no services running" and stop. Always check history first, then scan, and offer options.
bash
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
然后:
  1. 列出发现的项目及简要描述
  2. 询问用户要预览哪一个
  3. 使用相应目录调用
    preview_serve
不要只说“无运行中的服务”就停止操作。请始终先查看历史记录,再进行扫描,并为用户提供选项。

Quick Reference

快速参考

User saysYou 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
用户反馈你的操作
"tab disappeared" / "tab 不见了"步骤1 → 2 → 3或4
"blank page" / "白屏"检查端口(服务器端),若端口无响应则重新创建预览;若端口正常则检查绝对路径问题
"not updating" / "内容没更新"建议用户点击Browser标签页中的刷新按钮,或重新创建预览
"port conflict" / "端口冲突"
preview_stop
停止旧服务 →
preview_serve
启动新服务
"can't see service" / "⋮ menu empty"
preview_stop
+
preview_serve
强制重新注册
"where's my project" / "what did I build"读取
/data/preview_history.json
并列出条目
"resource load failed" / "JS/CSS 404"检查绝对路径(
/static/
/api/
),修复为相对路径

What You Cannot Do

你无法执行的操作

  • Cannot directly open/close/refresh Browser tabs (frontend UI)
  • Cannot force-refresh the iframe
  • Cannot read what the iframe displays
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
.
  • 无法直接打开/关闭/刷新Browser标签页(前端UI操作)
  • 无法强制刷新iframe
  • 无法读取iframe显示的内容
当你无法执行某项操作时,请告知用户手动操作(例如“点击Browser标签页中的刷新按钮”)。若手动操作无效,请使用
preview_stop
+
preview_serve
重新创建预览。

Common Mistakes to Avoid

需避免的常见错误

  1. ❌ Telling user to "visit http://localhost:18791/" — user cannot access localhost
  2. ❌ Saying "refresh the page at localhost" — meaningless to the user
  3. ❌ Only fixing CSS
    url()
    paths but missing JS string literals with absolute paths
  4. ❌ Forgetting to check ALL file types (HTML, JS, CSS, config) for absolute paths
  5. ✅ Always use
    /preview/{id}/
    as the user-facing URL
  6. ✅ Always use
    curl localhost:{port}
    only for your own server-side diagnostics
  7. ✅ After fixing paths, call
    preview_stop
    +
    preview_serve
    to restart, then tell user to check Browser panel
  1. ❌ 告知用户“访问http://localhost:18791/”——用户无法访问localhost
  2. ❌ 说“刷新localhost上的页面”——对用户无意义
  3. ❌ 仅修复CSS的
    url()
    路径但遗漏JS中的绝对路径字符串字面量
  4. ❌ 忘记检查所有文件类型(HTML、JS、CSS、配置文件)中的绝对路径
  5. ✅ 始终使用
    /preview/{id}/
    作为面向用户的URL
  6. ✅ 仅将
    curl localhost:{port}
    用于服务器端诊断
  7. ✅ 修复路径后,调用
    preview_stop
    +
    preview_serve
    重启服务,然后告知用户查看Browser面板