Oclaw oclaw
Oclaw 项目开发约定与 Tauri/Vue 架构规范。在为本仓库添加功能、调用 Tauri 命令、改路由或状态时使用;新增代码必须符合本技能中的前端与 Rust 规范。
install
source · Clone the upstream repo
git clone https://github.com/bichenxi/Oclaw
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/bichenxi/Oclaw "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agents/skills/oclaw" ~/.claude/skills/bichenxi-oclaw-oclaw && rm -rf "$T"
manifest:
.agents/skills/oclaw/SKILL.mdsource content
Oclaw 开发技能
项目身份
- 仓库:Oclaw,与 OpenClaw 结合使用的桌面浏览器壳。
- 栈:Tauri 2 + Vue 3 + TypeScript + Vite + Pinia + UnoCSS + Element Plus。
- 内置 HTTP 服务:
在api.rs
暴露浏览器控制接口,供 OpenClaw Agent 通过 curl 调用(见127.0.0.1:18790
)。openclaw-skill/SKILL.md
一、前端架构规范
1.1 目录结构(必须遵守)
src/ ├── api/ # Tauri invoke 封装层,按领域分文件,不在组件里直接 invoke ├── components/ # 小型可复用 UI 组件(TabBar、AIConsole 等) ├── composables/ # 组合式逻辑(useUrlInput 等),以 use 前缀命名 ├── layouts/ # 布局组件,default.vue 为默认布局 ├── pages/ # URL 路由页(unplugin-vue-router 自动注册),目前只有 index.vue ├── stores/ # Pinia stores ├── utils/ # 纯工具函数(http.ts 等) ├── views/ # 应用级全屏面板(通过 specialView 状态切换,不是 URL 路由) └── styles/ # 全局样式
⚠️
vs views/
的区别:pages/
下的文件会被 unplugin-vue-router 自动注册为 URL 路由(如pages/
)。/
存放通过 Piniaviews/
状态切换的全屏面板(OpenclawPage、SettingsPage、SetupPage、SkillsPage),不注册 URL 路由,也可被 layout 内嵌复用(如侧边栏中的 OpenclawPage)。specialView- ❌ 不要把应用面板放进
(会产生 URL 路由跳转,不适合桌面 app)。pages/ - ❌ 不要把 Page 级组件放进
(语义错误,components 只放小型复用组件)。components/
1.2 specialView 视图切换模式
布局通过 Pinia
useTabsStore 的 specialView ref 在多个全屏面板与网页 Tab 之间切换,不使用 vue-router 导航:
// stores/tabs.ts export type SpecialView = 'openclaw' | 'settings' | 'skills' | 'setup' const specialView = ref<SpecialView | null>(null) // 切换到某个面板(同时隐藏当前 webview) await store.switchToSpecialView('openclaw') // null 且 activeTabId 为 null = 首页 const isHome = computed(() => activeTabId.value === null && specialView.value === null)
default.vue 中按优先级渲染:
setup → openclaw → settings → skills → RouterView(首页) → webview 加载过渡
新增全屏面板时:
- 在
新建src/views/XxxPage.vue - 在
类型中加入新值SpecialView - 在
的default.vue
链中加入v-if - 在 TabBar 中加入触发按钮
1.3 当前 API 层文件
src/api/ 按领域分文件,禁止在组件/views 中直接 invoke:
| 文件 | 封装内容 |
|---|---|
| createTabWebview / show/hide/closeWebview / resizeAllWebviews / evalInWebview / setActiveTabLabel |
| checkOpenclawAlive / openclawSendV1 / setAiPaused |
| startInstall / cancelInstall / checkOpenclawInstalled |
| listSkills / readSkillFile / writeSkillFile / createSkill / deleteSkill / getOpenclawGatewayToken 等 |
| getCurrentProfile / setCurrentProfile |
| sidecar 相关命令 |
| greet / onWebviewClick 等杂项 |
1.4 当前 Pinia Stores
| Store | 用途 |
|---|---|
| tabs 列表、activeTabId、specialView、sidebarOpen、aiPaused,所有导航行为 |
| OpenClaw Token / SessionKey / BaseUrl(localStorage 持久化) |
| 浏览器身份(default/work/personal) |
| 操作录制步骤 |
| fnm 安装向导状态(steps、logs、installing、isInstalled) |
| AI 消息流(messages、sending、sendError) |
| 应用级全局状态 |
1.5 组件约定
- 统一使用
,不使用 Options API。<script setup lang="ts">
放小型可复用组件(TabBar、AIConsole)。src/components/
放全屏应用面板(OpenclawPage、SettingsPage、SetupPage、SkillsPage)。src/views/- 纯逻辑抽到
或composables/
,不在页面/视图里堆业务逻辑。utils/
1.6 新增前端功能检查
- 新全屏面板放在
,已加入src/views/
类型与SpecialView
v-if 链。default.vue - 新页面(URL 路由)放在
。src/pages/ - 新 Tauri 调用封装到
对应领域文件,带 TypeScript 类型。src/api/ - 新全局状态在
定义。src/stores/ - 纯逻辑在
或composables/
。utils/
二、Rust 架构规范
2.1 当前模块结构(实际)
src-tauri/src/ ├── main.rs # 入口,调用 lib::run() ├── lib.rs # mod 声明 + manage() + invoke_handler + run() ├── config.rs # 常量(TAB_BAR_HEIGHT、LEFT_PANEL_WIDTH、CHROME_USER_AGENT 等) ├── api.rs # HTTP 服务(127.0.0.1:18790),所有浏览器控制端点 ├── app.rs # greet、on_webview_click、emit_stream_item、simulate_stream ├── openclaw.rs # openclaw_connect/disconnect/send_chat,WebSocket 连接管理 ├── openclaw_http.rs # check_openclaw_alive / openclaw_send_v1(reqwest,no_proxy) ├── openclaw_process.rs # start/stop/is_openclaw_running,进程生命周期管理 ├── installer.rs # fnm sidecar 安装流程(start_install/cancel_install/check_openclaw_installed) ├── profile.rs # 浏览器身份(get/set_current_profile) ├── skills.rs # Skill 文件管理(list/read/write/create/delete/install) ├── bridge.js # 注入 webview 的 JS(snapshot/extract/highlight 等) ├── stealth.js # 反指纹注入脚本 └── webview/ ├── mod.rs ├── commands.rs # create_tab_webview, show/hide/close_webview, resize, eval, snapshot └── rect.rs # calc_webview_rect(计算 webview 位置与大小)
2.2 lib.rs 职责
lib.rs 只做三件事:
声明所有模块mod
注册共享状态.manage(状态)
注册 Tauri 命令generate_handler![所有命令]
新命令实现禁止直接写在
lib.rs,要放进对应领域模块。
2.3 HTTP 服务(api.rs)
api::spawn_http_server 在 setup 回调中启动,监听 127.0.0.1:18790。
所有浏览器控制端点在此定义(navigate/snapshot/click/type/scroll/select/eval/highlight/wait/extract/extract-text/back/forward)。
修改端点后必须同步更新 openclaw-skill/SKILL.md(见 /skill-sync 命令)。
2.4 共享状态(Managed State)
| 类型 | 用途 |
|---|---|
| 当前活动 webview 的 label |
| 一次性 channel sender |
| 一次性 channel sender |
| AI 是否被暂停(AtomicBool) |
| WS 连接与消息 channel |
| openclaw 子进程句柄 |
| fnm 安装进程状态与取消 channel |
2.5 新增 Tauri 命令检查
- 实现写在对应领域模块,不在
。lib.rs - 在
的lib.rs
中注册。generate_handler![...] - 需要共享状态用
注入,新状态在State<T>
中添加。.manage() - 新常量加入
,不硬编码。config.rs - 前端已通过
封装并带类型调用。src/api/
三、CSS 与 UI 规范
必须优先使用 UnoCSS 原子类,禁止在新代码中写
手写 CSS。<style scoped>
3.1 主题色
来自
unocss.config.ts:
| Token | 值 | 用途 |
|---|---|---|
/ | | 主紫色,品牌主色 |
/ | | 主蓝色 |
/ | | 红色/危险 |
/ | 灰色系 | 辅助文字/背景 |
3.2 内置 Shortcut
| 类名 | 含义 |
|---|---|
| 标准按钮 |
| 朴素按钮 |
| |
| 绝对定位居中 |
| 标准过渡动画 |
3.3 允许 <style scoped>
的情况
<style scoped>- 复杂 keyframes 动画
覆盖第三方组件内部样式:deep()
(Tauri 窗口拖拽,UnoCSS 无法表达)-webkit-app-region: drag
3.4 图标与 Logo
- 项目 Logo:
(螃蟹形象),用法:public/logo.png<img src="/logo.png" class="w-9 h-9 rounded-[10px] object-cover shadow" alt="logo" /> - 图标优先使用内联 SVG(
),或stroke="currentColor"
的unplugin-icons
。icon-local-*
四、添加新功能总清单
- 前端视图面板:新建
→ 加src/views/XxxPage.vue
类型 → 加SpecialView
v-if → 加 TabBar 按钮。default.vue - URL 页面:新建
,路由自动生成。src/pages/xxx.vue - Tauri 命令:Rust 模块中实现 →
注册 →lib.rs
封装 → 组件调用 API 函数。src/api/ - 修改 HTTP 端点:改
→ 同步更新api.rs
(运行openclaw-skill/SKILL.md
)。/skill-sync - 新常量:加入
,不硬编码。config.rs