# Step 68: 最简 web 前端

一句话导读:用一个自包含 HTML 页面连上 WebSocket,把 EngineEvent 渲染成聊天气泡、流式文本、工具提示和权限卡片。


# 一、这一步做了什么(What)

step68 新增 server/public/index.html,并让 wsServer.ts 托管它。页面连接 ws://location.host,发送 prompt,接收 event、permission_request、done 等消息,渲染成浏览器聊天界面。

这一步终于让 mini Claude Code 从终端走到浏览器。

# 二、面试官视角:为什么要做?(Why)

面试题:有了 WebSocket 协议,为什么还要做前端页面?用客户端发 JSON 不也能测吗?

协议通了只说明后端可用;真正的产品体验在 UI 层:用户要看到流式输出、工具调用、权限风险、token/cost 反馈,并能在权限点做决定。Web 前端是把 agent 事件流翻译成人能操作的界面。

WS 消息 UI 表现
text/thinking AI 气泡逐字追加
tool_calls 工具 chip
tool_result 成功/失败与耗时
round token/cost/cache
permission_request 允许/拒绝按钮

# 三、原理:它是怎么工作的(How)

前端发送:

ws.send(JSON.stringify({ type: "prompt", text }));
1

接收事件:

ws.onmessage = (e) => {
  const m = JSON.parse(e.data);
  if (m.type === "event") renderEvent(m.ev);
  if (m.type === "permission_request") renderPermission(m);
};
1
2
3
4
5

权限按钮:

ws.send(JSON.stringify({
  type: "permission_response",
  id: req.id,
  allow: true
}));
1
2
3
4
5

数据流:

用户输入
  → ws prompt
  → QueryEngine 事件流
  → ws event
  → DOM 追加/更新气泡
  → permission_request 时渲染按钮
  → permission_response 回到服务端
1
2
3
4
5
6
7

# 四、深入追问(面试常见 follow-up)

Q:为什么页面和 WS 服务同端口?
A:同源避免 CORS 和 WS 跨域配置,教学版更聚焦事件协议。

Q:流式打字机需要额外定时器吗?
A:不需要。后端本来逐段推 text 事件,前端把内容 append 到当前 AI 气泡即可。

Q:权限卡片为什么要显示 risk?
A:risk 来自 Bash 分类器,UI 可以用颜色表达危险程度,让用户审批时有上下文。

Q:浏览器实测的价值是什么?
A:真实 DOM、真实 WS、真实点击事件会暴露纯协议测试看不到的问题,比如按钮状态、输入锁定、重连。

# 五、踩坑 / 设计权衡

step68 纯手写 HTML/JS,没有框架、没有 Markdown 完整渲染、没有会话列表。它要验证的是端到端闭环:输入 → 模型 → 工具审批 → 输出。

# 六、与真实源码的对照

我们的实现 Claude Code 源码
单文件 HTML/JS 完整 React Web 前端
简单气泡/权限卡 组件化消息、权限、工具 UI
本地 WS bridge/remote control channel
前端内存历史 持久化 session transcript

# 七、一句话总结

step68 把抽象事件流变成了可操作 UI:agent 的每个阶段都能被浏览器看见、点击和反馈。

# 下一节预告

下一篇是 step69:打磨工具调用 UI,并用 marked 替换脆弱的手写 Markdown 正则。