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 行数子组件数评分
初始26190
R22619079 (严格)
R32432384
R42119589
R51879996

提取的 9 个组件:types.tsutils.tsDeliveryTicksMessageItemActionSheetSuggestionBarHistoryDrawerHeaderMenuConnectionBanner

UI 风格确立

  • 不要聊天气泡 — 坚持 Discord/Slack 平铺风格(头像+名字+时间在上,内容在下)
  • 品牌色 — 从绿色 #67B88B 统一为橙色 #EF5A23
  • 文字对比度 — 主文字 #1F2328,次要 #4D5561

开发环境矩阵

WebBot 维护 7 个 pm2 开发服务:

服务端口项目
dev-web3026client-web
dev-docs3029docs
dev-gw3020gateway admin
dev-portal3013agent-portal
dev-channel-h53023channel H5
dev-craft3010clawcraft
dev-bi3016agentic-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修复内容
27f69b4bWebSocket 包白名单机制(只处理 message.send/message.receive/thinking.*/agent.list)+ 空内容/无效内容过滤([Image]📎 File*[cancelled]*
98e58b5a历史脏数据清洗(localStorage 启动时清洗)+ 隐藏零对话幽灵 Agent + getInboxItems 强制过滤
a40c03b6修复 Inbox↔Chats Tab 切换导致聊天框状态丢失(activeAgent 被无条件设为 null)

6 道防线(全链路审查)

  1. localStorage 旧缓存 — 启动时清洗脏数据
  2. IndexedDB 历史数据populateAgentFromMessages 应用 isContentMessage 过滤
  3. 实时 AI 回复 (WS message.send) — 内容校验拦截空/无效内容
  4. 实时用户消息 (WS message.receive) — 同等内容校验
  5. 其他实时系统包 (typing/status/delta) — 白名单机制完全拦截
  6. UI 渲染输出 (getInboxItems) — 强制过滤无有效 lastMessage 的空白 Agent

已知边界:后端 Channel 插件若错误使用 message.send 协议发送系统文本(如 "Agent connected"),客户端无法区分,需后端改用 status.update/system.alert 协议。

文件发送机制分析(2026-04-07)

现状分析

OutboundMessagecontentType 只有 "text" | "markdown" | "image" | "voice" | "audio"没有 "file" 类型

outbound.tssendMedia 对非 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.tssendMedia — 过早放弃 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 AdminApp.tsxchatId 默认 = senderId
Web ClientPairing.tsxfallback 从 channelId → senderId
Pluginclient.tschatId 解析加 authUser.senderId fallback

插件更新方式(2026-04-07)

Clawline 插件不是 npm 包,而是通过 GitHub 多仓库管理的独立项目:

仓库作用
clawline/channelOpenClaw Channel 插件(后端信道)
clawline/client-webReact Web 客户端
clawline/client-wechat微信小程序客户端
clawline/gatewayWebSocket 消息中继网关

加载方式~/.openclaw/extensions/clawline/ 是 symlink → 指向 ~/Projects/clawline/channel/。更新流程为 git pull 拉取最新代码 → 重启 gateway 生效。

WebSocket 断线修复(同日)

根因:客户端每 15s 发 ping,服务端 Gateway 从未回复 pong(将 ping 当作普通消息转发),导致客户端判定超时后主动 ws.close(4000, 'Heartbeat timeout'),形成疯狂重连循环。

修复内容Commit
Gateway拦截 ping 并回复 pongfc02056
Client心跳间隔 15s→30s + 容错 2 次未回复42d59a17

Dev 环境部署全景(2026-04-06)

WebBot 梳理了 *.dev.dora.restry.cn 开发环境的完整部署架构。所有服务运行在 matrix 机器上,通过 Caddy 反向代理 统一路由。

域名端口pm2 服务内容负责 Agent
web.dev.dora.restry.cn3026dev-webclient-web(React)clawline-client-web
gw.dev.dora.restry.cn3020dev-gwgateway admin 静态页clawline-client-web
docs.dev.dora.restry.cn3029dev-docsVitePress 文档clawline-client-web
portal.dev.dora.restry.cn3002portal-api + portal-staticPortal 后端 API + 静态前端research-portal
bi.dev.dora.restry.cn8899bi-backend + dev-biBI 服务research-bi
craft.dev.dora.restry.cndev-craftClawcraft(部分请求转发 OWL)research-craft
channel.dev.dora.restry.cn3023dev-channel-h5Channel H5 示例页clawline-channel

Gateway Admin 重新部署

gw.dev.dora.restry.cn 的管理端经历了一次重大部署迁移:

  • 原状:pm2 dev-gw 从旧的 workspace-clawline-gateway/repo/admin/dist serve 静态文件,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_tokensmax_completion_tokens 兼容性问题(GPT-5.4-mini 不支持旧参数),commit 12bb93d 修复。

代码提交与部署(2026-04-08)

本地代码整理提交

researcher agent 整理并提交了 ~/Projects/clawline 下所有未提交代码:

子目录提交内容
client-webfix: resolve Inbox issues and disable ChatRoom typing
client-wechatchore: init package configs
channelchore: update package-lock.json
gatewayfeat: 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 插件 7d4cddfhandleGenericMessage 入口添加 rolling Set(最近 200 条 messageId),重复消息直接 skip
    • Client outbox 47405b87:先 dequeue 再发送,发送失败才 re-enqueue,杜绝重复 flush
  • 版本号改进:BUILD_HASH 从随机 MD5 改为 git commit hash(b1ab4591),patch 版本自动递增 = git commit 总数(如 v0.2.248
  • Git workflow 更新:dev 分支直接 push,main 分支才需要批准

部署目标确认

确认了 client-web 的正确部署目标:web.dev.dora.restry.cn4.194.153.244(本机),不是 OWL(172.16.0.8)。OWL 只用于测试 OpenClaw 插件。

相关主题

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 渲染

GatewayPOST /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.tsMessage 类型加 meta?: Record<string, unknown>2行
ChatRoom.tsx解析 packet.data.meta 字段 + history sync 解析5行
MessageItem.tsx渲染 API 标识 badge(⚡ API)10行