Clawline Client Web
Clawline 的 React Web 聊天客户端,提供多 Agent 对话、技能管理、PWA 离线支持等功能。
概述
Clawline Client Web 是 Clawline 生态的核心前端应用,由 webbot 主力开发维护。项目从 Vite React 模板起步,演进为功能完备的多 Agent 聊天客户端,支持 WebSocket 实时通信、流式消息渲染、PWA 离线缓存等企业级特性。
项目信息
| 属性 | 值 |
|---|---|
| 仓库 | clawline/client-web |
| 技术栈 | React 19 + TypeScript + Vite + Tailwind CSS |
| 版本 | v0.1.0 |
| 开发端口 | 3026 |
| 开发环境 | https://web.dev.dora.restry.cn |
| 生产环境 | https://chat.clawlines.net |
| 开发分支 | dev |
核心功能
多 Agent 聊天
- 支持连接多个 OpenClaw 服务器
- Agent 列表网格/列表视图切换,拖拽排序(Reorder 组件 + localStorage 持久化)
- 自定义 Agent 头像(右键/长按设置)
- 消息历史同步、新建/切换会话
消息系统
- 流式消息渲染 — 实时显示 AI 思考过程和 delta 输出
- Markdown 渲染 — 代码高亮、inline code 暖色样式
- 工具调用展示 — JSON 自动解析为可读文本
- 消息操作 — Reply(引用回复)、Copy(剪贴板 + toast 反馈)
- 断点续传 — 客户端断连后通过
stream.resume恢复流式内容
技能系统
- 斜杠命令菜单(
/触发自动补全) - 技能三分类展示:工作区技能 + 全局技能(高亮)vs 内置技能(灰色折叠)
- 药丸按钮交互:点击填入输入框,长按 800ms 直接发送
- AI 智能建议(通过 channel 插件 + OpenClaw SDK 调模型生成)
输入交互
- WhatsApp 风格底部输入栏:
+按钮菜单 + 输入框 + Mic/Send - 语音录制(带计时器显示)
- 动态建议条(空输入显示命令 chips,bot 回复后显示上下文建议)
- 移动端文字选择保护(ActionSheet 不劫持长按选中)
PWA & 离线
- Service Worker 缓存策略(详见 PWA 部署页面)
- 自动版本更新检测 + UpdateBanner
- BUILD_HASH 版本号显示(Profile 页底部)
架构演进
ChatRoom 大重构(2026-03-25)
通过 adversarial-qa(左右互搏 QA)自动化审核,经过 5 轮迭代完成:
| 轮次 | ChatRoom 行数 | 子组件数 | 评分 |
|---|---|---|---|
| 初始 | 2619 | 0 | — |
| R2 | 2619 | 0 | 79 (严格) |
| R3 | 2432 | 3 | 84 |
| R4 | 2119 | 5 | 89 |
| R5 | 1879 | 9 | 96 ✅ |
提取的 9 个组件:types.ts、utils.ts、DeliveryTicks、MessageItem、ActionSheet、SuggestionBar、HistoryDrawer、HeaderMenu、ConnectionBanner
UI 风格确立
- 不要聊天气泡 — 坚持 Discord/Slack 平铺风格(头像+名字+时间在上,内容在下)
- 品牌色 — 从绿色
#67B88B统一为橙色#EF5A23 - 文字对比度 — 主文字
#1F2328,次要#4D5561
开发环境矩阵
WebBot 维护 7 个 pm2 开发服务:
| 服务 | 端口 | 项目 |
|---|---|---|
| dev-web | 3026 | client-web |
| dev-docs | 3029 | docs |
| dev-gw | 3020 | gateway admin |
| dev-portal | 3013 | agent-portal |
| dev-channel-h5 | 3023 | channel H5 |
| dev-craft | 3010 | clawcraft |
| dev-bi | 3016 | agentic-bi |
关键 Bug 修复记录
| Bug | 根因 | 修复 |
|---|---|---|
| Agent.list 死循环 | useEffect 依赖 expandedIds + ensureAgentsLoaded 无条件调 connect | 加 agents 已有守卫 + 从 deps 移除 expandedIds |
| iOS ActionSheet 被输入栏遮挡 | ActionSheet 在 overflow:auto 容器内的 fixed 定位 bug | 移出 scroll 容器到外层 |
| 复制文字功能失效 | 长按 400ms 劫持系统文字选择 + clipboard API 无同步 fallback | 文字区域 stopPropagation + execCommand 优先 |
| iOS PWA 旧版本不更新 | SW 不自动 skipWaiting + iOS Safari SW 更新延迟 | index.html 加缓存清理脚本 + 自动 skipWaiting |
| 技能按钮点击不灵 | 28px 按钮太小 + AnimatePresence 动画干扰 | 提到动画外 + onPointerDown + 36px |
| 骨架屏卡死 | agentReady 状态在 React StrictMode 双执行下 agent.selected 丢失 | connection.open 后请求 agent.list 兜底 |
经验教训
- iOS PWA 缓存极其顽固 — 必须在 index.html 里内联缓存清理脚本,不能依赖 SW 自更新
- 移动端触控区域 — 最小 44px(Apple HIG),28px 的按钮在手机上几乎点不到
- React StrictMode — effect 双执行会导致 WS 事件在 cleanup 和 re-subscribe 之间丢失
- jiti 缓存 — 改 channel 插件后必须
rm -rf /tmp/jiti/再重启 gateway - Docker 网络隔离 — 不同 compose 项目的容器默认不互通,需
docker network connect
Inbox 过滤优化(2026-04-07)
Inbox 页面系统消息/状态消息过滤逻辑经历了多轮修复,最终通过 6 个入口全链路审查 达到”私人秘书”级别的降噪效果。
问题
Inbox 充斥系统日志、状态播报、空卡片等噪音,核心价值”降噪”未实现。
修复过程(3 轮 commit)
| Commit | 修复内容 |
|---|---|
27f69b4b | WebSocket 包白名单机制(只处理 message.send/message.receive/thinking.*/agent.list)+ 空内容/无效内容过滤([Image]、📎 File、*[cancelled]*) |
98e58b5a | 历史脏数据清洗(localStorage 启动时清洗)+ 隐藏零对话幽灵 Agent + getInboxItems 强制过滤 |
a40c03b6 | 修复 Inbox↔Chats Tab 切换导致聊天框状态丢失(activeAgent 被无条件设为 null) |
6 道防线(全链路审查)
- localStorage 旧缓存 — 启动时清洗脏数据
- IndexedDB 历史数据 —
populateAgentFromMessages应用isContentMessage过滤 - 实时 AI 回复 (WS
message.send) — 内容校验拦截空/无效内容 - 实时用户消息 (WS
message.receive) — 同等内容校验 - 其他实时系统包 (typing/status/delta) — 白名单机制完全拦截
- UI 渲染输出 (
getInboxItems) — 强制过滤无有效lastMessage的空白 Agent
已知边界:后端 Channel 插件若错误使用 message.send 协议发送系统文本(如 "Agent connected"),客户端无法区分,需后端改用 status.update/system.alert 协议。
文件发送机制分析(2026-04-07)
现状分析
OutboundMessage 的 contentType 只有 "text" | "markdown" | "image" | "voice" | "audio",没有 "file" 类型。
outbound.ts 的 sendMedia 对非 image/audio 文件 fallback 为纯文本 📎 URL,丢弃了 mediaUrl 字段,导致客户端无法渲染文件卡片。
底层能力(send.ts)
本地文件路径 → 读取 buffer → 上传到 relay → 得到公开 URL → 发给客户端
data: URI → 上传到 relay 或直接 inline
https:// URL → 直接透传
修复方案
| 层级 | 修复 |
|---|---|
send.ts | 加 "file" contentType |
outbound.ts | 非 image/audio 兜底为 contentType: "file" |
OutboundMessage 类型 | 加 fileName? / fileSize? 元信息字段 |
| 客户端 | ChatRoom.tsx 已有 mediaUrl → mediaType='file' 兜底(无需改动) |
断点在 outbound.ts 的 sendMedia — 过早放弃 media 通道。修复后 commit 05939e6 实现 feat: support file contentType in outbound adapter。
chatId 标识修复(关联问题)
发送文件时暴露了 chatId 路由问题:Web client 连接时传 chatId=channelId(如 "dora"),而 OpenClaw runtime 用 user:senderId(如 "Levis")路由,导致 clients.get("Levis") 找不到连接。
三层修复:
| 层 | 文件 | 改动 |
|---|---|---|
| Gateway Admin | App.tsx | chatId 默认 = senderId |
| Web Client | Pairing.tsx | fallback 从 channelId → senderId |
| Plugin | client.ts | chatId 解析加 authUser.senderId fallback |
插件更新方式(2026-04-07)
Clawline 插件不是 npm 包,而是通过 GitHub 多仓库管理的独立项目:
| 仓库 | 作用 |
|---|---|
clawline/channel | OpenClaw Channel 插件(后端信道) |
clawline/client-web | React Web 客户端 |
clawline/client-wechat | 微信小程序客户端 |
clawline/gateway | WebSocket 消息中继网关 |
加载方式:~/.openclaw/extensions/clawline/ 是 symlink → 指向 ~/Projects/clawline/channel/。更新流程为 git pull 拉取最新代码 → 重启 gateway 生效。
WebSocket 断线修复(同日)
根因:客户端每 15s 发 ping,服务端 Gateway 从未回复 pong(将 ping 当作普通消息转发),导致客户端判定超时后主动 ws.close(4000, 'Heartbeat timeout'),形成疯狂重连循环。
| 修复 | 内容 | Commit |
|---|---|---|
| Gateway | 拦截 ping 并回复 pong | fc02056 |
| Client | 心跳间隔 15s→30s + 容错 2 次未回复 | 42d59a17 |
Dev 环境部署全景(2026-04-06)
WebBot 梳理了 *.dev.dora.restry.cn 开发环境的完整部署架构。所有服务运行在 matrix 机器上,通过 Caddy 反向代理 统一路由。
| 域名 | 端口 | pm2 服务 | 内容 | 负责 Agent |
|---|---|---|---|---|
web.dev.dora.restry.cn | 3026 | dev-web | client-web(React) | clawline-client-web |
gw.dev.dora.restry.cn | 3020 | dev-gw | gateway admin 静态页 | clawline-client-web |
docs.dev.dora.restry.cn | 3029 | dev-docs | VitePress 文档 | clawline-client-web |
portal.dev.dora.restry.cn | 3002 | portal-api + portal-static | Portal 后端 API + 静态前端 | research-portal |
bi.dev.dora.restry.cn | 8899 | bi-backend + dev-bi | BI 服务 | research-bi |
craft.dev.dora.restry.cn | — | dev-craft | Clawcraft(部分请求转发 OWL) | research-craft |
channel.dev.dora.restry.cn | 3023 | dev-channel-h5 | Channel H5 示例页 | clawline-channel |
Gateway Admin 重新部署
gw.dev.dora.restry.cn 的管理端经历了一次重大部署迁移:
- 原状:pm2
dev-gw从旧的workspace-clawline-gateway/repo/admin/distserve 静态文件,JS 资源过旧缺少 AI Settings 按钮 - 根因:research agent 之前 build 了新的前端但部署到了错误目录
- 迁移:pm2
dev-gw从旧workspace-clawline-gateway切换到workspace-clawline-client-web/gateway-repo/public,完成从独立 gateway 仓库到 WebBot 管辖的转移 - 遗留问题:旧版 Logto token 残留导致白屏,需手动清除浏览器 logto 缓存后重新登录
Gateway 全流程测试(Claude Code CLI)
researcher agent 通过 tmux 管理的 Claude Code CLI(session 401d2e5f)对 Gateway 进行了全流程本地测试:
| 功能 | 测试结果 |
|---|---|
| Admin UI | ✅ 正常 |
| AI Settings GET/PUT | ✅ 返回默认值 / DB 覆盖生效 |
| Suggestions API | ✅ 返回 7 条建议 |
| Voice Refine API | ✅ 语音转写清理正常 |
| 消息持久化 | ✅ Supabase 有真实消息 |
Bug 修复:测试中发现 max_tokens → max_completion_tokens 兼容性问题(GPT-5.4-mini 不支持旧参数),commit 12bb93d 修复。
代码提交与部署(2026-04-08)
本地代码整理提交
researcher agent 整理并提交了 ~/Projects/clawline 下所有未提交代码:
| 子目录 | 提交内容 |
|---|---|
| client-web | fix: resolve Inbox issues and disable ChatRoom typing |
| client-wechat | chore: init package configs |
| channel | chore: update package-lock.json |
| gateway | feat: update admin, server and database schema(含新 DESIGN.md、App.tsx、server.js、schema.sql) |
Dev 环境部署更新
WebBot 拉取 dev 分支最新代码并部署:
- client-web:5 个新 commit(Inbox bug 修复、全局消息同步 Zustand syncStore、Inbox expanded view 显示改进等),build + pm2 restart
- 消息去重双层防护:
- Channel 插件
7d4cddf:handleGenericMessage入口添加 rolling Set(最近 200 条 messageId),重复消息直接 skip - Client outbox
47405b87:先 dequeue 再发送,发送失败才 re-enqueue,杜绝重复 flush
- Channel 插件
- 版本号改进:BUILD_HASH 从随机 MD5 改为 git commit hash(
b1ab4591),patch 版本自动递增 = git commit 总数(如v0.2.248) - Git workflow 更新:dev 分支直接 push,main 分支才需要批准
部署目标确认
确认了 client-web 的正确部署目标:web.dev.dora.restry.cn → 4.194.153.244(本机),不是 OWL(172.16.0.8)。OWL 只用于测试 OpenClaw 插件。
相关主题
- webbot
- clawline-gateway — Gateway 产品(现由本 Bot 统一维护)
- clawline-plugin
- pwa-ios-deployment
- openclaw-config
- logto-sso
- caddy-reverse-proxy
- gatewaybot — 原 Gateway 开发 Bot(已淡出)
2026-04-11 更新:Impeccable Skills 与 API 直连标识
Impeccable Skills 安装
安装了 pbakaus/impeccable 技能包到工作区 .agents/skills/ 目录,共 21 个 skills:
- 包括 impeccable 核心及配套的 adapt、animate、arrange、audit 等
- 工作区版本(原版完整 impeccable skill)优先于全局的简化版
teach-impeccable - 支持
craft(做前端)和teach(设计上下文设置)两个模式
API 直连标识 Badge 渲染
为 Gateway 的 POST /api/chat 功能在前端添加直连标识:
标识设计
用 meta.source = "api" 字段标记 API 调用的消息,在前端渲染一个小 badge:
{!isUser && msg.meta?.source === 'api' && (
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-[10px] font-medium bg-accent/10 text-accent ml-1.5 align-middle">
<Zap size={9} />
API
</span>
)}前端改动
| 文件 | 改动 | 行数 |
|---|---|---|
types.ts | Message 类型加 meta?: Record<string, unknown> | 2行 |
ChatRoom.tsx | 解析 packet.data.meta 字段 + history sync 解析 | 5行 |
MessageItem.tsx | 渲染 API 标识 badge(⚡ API) | 10行 |