From 0a75413c96041116cc4ca7e49d3a3fc5810760c2 Mon Sep 17 00:00:00 2001 From: coso Date: Sun, 15 Mar 2026 00:43:25 +0800 Subject: [PATCH] feat: release v0.87.0 with browser runtime and workbench improvements --- .github/workflows/release.yml | 11 +- RELEASE_NOTES.md | 63 +- docs/research/next-browser-reference.md | 508 ++++ docs/research/nextbrowser-debugger.md | 427 +++ .../research/nextbrowser-features-analysis.md | 1610 ++++++++++++ docs/research/nextbrowser-features.md | 215 ++ docs/research/nextbrowser-liveview.md | 176 ++ package.json | 2 +- scripts/check-app-version-consistency.mjs | 13 +- src-tauri/Cargo.lock | 50 +- src-tauri/Cargo.toml | 6 +- src-tauri/crates/agent/src/aster_state.rs | 2 + src-tauri/crates/agent/src/tools/mod.rs | 4 + .../crates/agent/src/tools/skill_tool_gate.rs | 180 ++ src-tauri/crates/browser-runtime/Cargo.toml | 21 + .../crates/browser-runtime/src/action.rs | 249 ++ .../crates/browser-runtime/src/client.rs | 72 + src-tauri/crates/browser-runtime/src/lib.rs | 11 + .../crates/browser-runtime/src/manager.rs | 1132 ++++++++ src-tauri/crates/browser-runtime/src/types.rs | 183 ++ src-tauri/src/app/commands/logs.rs | 22 +- src-tauri/src/app/runner.rs | 14 + src-tauri/src/commands/aster_agent_cmd.rs | 424 ++- src-tauri/src/commands/browser_runtime_cmd.rs | 238 ++ src-tauri/src/commands/mod.rs | 1 + src-tauri/src/commands/skill_cmd.rs | 210 ++ src-tauri/src/commands/webview_cmd.rs | 1284 +++++++-- src-tauri/src/dev_bridge/dispatcher.rs | 221 ++ src-tauri/src/services/README.md | 1 + .../src/services/browser_runtime_window.rs | 109 + src-tauri/src/services/mod.rs | 1 + src-tauri/tauri.conf.headless.json | 2 +- src-tauri/tauri.conf.json | 2 +- src/App.tsx | 48 +- src/RootRouter.tsx | 11 + src/components/AppSidebar.tsx | 44 +- src/components/README.md | 3 - src/components/Sidebar.tsx | 62 - src/components/agent/chat/README.md | 9 +- .../components/CanvasWorkbenchLayout.test.tsx | 529 ++++ .../chat/components/CanvasWorkbenchLayout.tsx | 1367 ++++++++++ .../agent/chat/components/ChatNavbar.test.tsx | 89 +- .../agent/chat/components/ChatNavbar.tsx | 53 +- .../chat/components/ChatSidebar.test.tsx | 97 +- .../agent/chat/components/ChatSidebar.tsx | 860 ++++-- .../agent/chat/components/EmptyState.test.tsx | 26 + .../agent/chat/components/EmptyState.tsx | 61 +- .../components/HarnessStatusPanel.test.tsx | 19 + .../chat/components/HarnessStatusPanel.tsx | 545 ++-- .../components/InputbarComposerSection.tsx | 49 +- .../components/SkillSelector.test.tsx | 266 ++ .../Inputbar/components/SkillSelector.tsx | 368 +++ .../Inputbar/hooks/useInputbarController.ts | 2 + .../agent/chat/components/Inputbar/index.tsx | 26 +- .../agent/chat/components/README.md | 1 + .../components/RuntimeStyleControlBar.tsx | 24 +- .../chat/components/ThemeWorkbenchSidebar.tsx | 6 +- .../agent/chat/hooks/agentChatShared.ts | 186 +- src/components/agent/chat/hooks/index.ts | 3 +- .../agent/chat/hooks/useAgentChat.test.tsx | 25 + .../agent/chat/hooks/useAgentChat.ts | 37 - .../agent/chat/hooks/useAgentSession.ts | 109 +- .../hooks/useArtifactDisplayState.test.ts | 48 +- .../chat/hooks/useArtifactDisplayState.ts | 33 + .../chat/hooks/useAsterAgentChat.test.tsx | 95 + .../agent/chat/hooks/useAsterAgentChat.ts | 42 +- src/components/agent/chat/index.test.tsx | 924 ++++++- src/components/agent/chat/index.tsx | 2317 ++++++++++++++--- src/components/agent/chat/types.ts | 13 + .../chat/utils/browserAssistIntent.test.ts | 51 + .../agent/chat/utils/browserAssistIntent.ts | 63 + .../chat/utils/browserAssistSession.test.ts | 161 ++ .../agent/chat/utils/browserAssistSession.ts | 319 +++ .../chat/utils/canvasWorkbenchDiff.test.ts | 30 + .../agent/chat/utils/canvasWorkbenchDiff.ts | 85 + .../chat/utils/generalAgentPrompt.test.ts | 18 + .../agent/chat/utils/generalAgentPrompt.ts | 20 + .../chat/utils/generalResourceSync.test.ts | 61 + .../agent/chat/utils/generalResourceSync.ts | 119 + .../agent/chat/utils/harnessSkills.test.ts | 60 + .../agent/chat/utils/harnessSkills.ts | 19 + .../agent/chat/utils/skillFailure.ts | 6 +- src/components/api-server/ApiServerPage.tsx | 1818 +++++++------ src/components/api-server/LogsTab.tsx | 12 +- src/components/artifact/ArtifactList.tsx | 3 + src/components/artifact/ArtifactRenderer.tsx | 98 +- .../artifact/ArtifactToolbar.test.ts | 1 + src/components/artifact/ArtifactToolbar.tsx | 102 +- src/components/artifact/index.ts | 1 + .../renderers/BrowserAssistRenderer.test.tsx | 130 + .../renderers/BrowserAssistRenderer.tsx | 138 + src/components/artifact/renderers/index.ts | 14 + .../canvas/document/DocumentCanvas.tsx | 10 +- .../canvas/document/DocumentToolbar.tsx | 33 +- .../canvas/shared/CanvasBreadcrumbHeader.tsx | 9 +- src/components/extensions/ExtensionsPage.tsx | 58 - src/components/extensions/index.ts | 1 - .../general-chat/canvas/CanvasPanel.tsx | 10 +- src/components/general-chat/types.ts | 3 + .../image-gen/ImageGenPage.test.tsx | 4 +- src/components/image-gen/index.ts | 2 +- .../image-gen/tabs/AiImageGenTab.tsx | 3 +- src/components/image-gen/useImageGen.ts | 12 +- src/components/memory/MemoryPage.tsx | 1496 +++++++---- src/components/memory/StyleLibraryPanel.tsx | 170 +- .../openclaw/OpenClawConfigurePage.tsx | 8 +- src/components/openclaw/OpenClawPage.tsx | 5 +- .../projects/ContentEditorPage.test.tsx | 197 ++ src/components/projects/ContentEditorPage.tsx | 298 ++- .../projects/ContentListPage.test.tsx | 154 ++ src/components/projects/ContentListPage.tsx | 599 +++-- .../projects/CreateProjectDialog.test.tsx | 128 + .../projects/CreateProjectDialog.tsx | 246 +- src/components/projects/MemorySidebar.tsx | 209 +- .../projects/ProjectSelector.test.ts | 71 +- src/components/projects/ProjectSelector.tsx | 694 ++++- .../projects/ProjectSelector.ui.test.tsx | 342 +++ .../projects/editor/EditorToolbar.tsx | 232 +- .../projects/memory/CharacterPanel.tsx | 536 ++-- .../projects/memory/OutlinePanel.tsx | 431 ++- .../memory/ProjectMemoryPanels.test.tsx | 192 ++ .../projects/memory/StyleGuidePanel.tsx | 219 +- .../projects/memory/WorldBuildingPanel.tsx | 296 ++- .../projects/projectSelectorUtils.ts | 47 +- src/components/prompts/PromptCard.tsx | 127 - src/components/prompts/PromptForm.tsx | 114 - src/components/prompts/PromptsPage.tsx | 465 ---- src/components/prompts/index.ts | 3 - src/components/resources/ResourcesPage.tsx | 3 +- src/components/settings-v2/_layout/index.tsx | 37 +- .../settings-v2/account/stats/index.test.tsx | 167 ++ .../settings-v2/account/stats/index.tsx | 1030 +++++--- .../general/appearance/index.test.tsx | 168 ++ .../settings-v2/general/appearance/index.tsx | 1030 ++++++-- .../general/chat-appearance/index.tsx | 380 +-- .../general/hotkeys/index.test.tsx | 197 ++ .../settings-v2/general/hotkeys/index.tsx | 576 ++-- .../settings-v2/general/memory/index.test.tsx | 13 + .../settings-v2/general/memory/index.tsx | 1385 ++++++---- .../settings-v2/hooks/useSettingsCategory.ts | 6 - .../system/channels/index.test.tsx | 210 ++ .../settings-v2/system/channels/index.tsx | 917 +++++-- .../system/chrome-relay/index.test.tsx | 200 ++ .../settings-v2/system/chrome-relay/index.tsx | 1897 +++++++++----- .../system/developer/index.test.tsx | 277 ++ .../settings-v2/system/developer/index.tsx | 521 +++- .../system/environment/index.test.tsx | 255 ++ .../settings-v2/system/environment/index.tsx | 927 +++++-- .../security-performance/index.test.tsx | 286 ++ .../system/security-performance/index.tsx | 862 ++++-- .../system/web-search/index.test.tsx | 7 + .../settings-v2/system/web-search/index.tsx | 1391 ++++++---- src/components/skills/SkillsPage.test.tsx | 53 +- src/components/skills/SkillsPage.tsx | 52 +- src/components/style/StylePage.test.tsx | 2 + src/components/style/StylePage.tsx | 584 +++-- src/components/tools/ToolCardContextMenu.tsx | 15 +- src/components/workspace/WorkbenchPage.tsx | 5 +- .../panels/WorkbenchCreateEntryHome.tsx | 414 +-- .../workspace/panels/WorkbenchMainContent.tsx | 253 +- .../workbenchRightRailActionSections.tsx | 31 +- .../workbenchRightRailExpandedChrome.tsx | 38 +- .../BrowserRuntimeDebugPanel.test.tsx | 125 + .../BrowserRuntimeDebugPanel.tsx | 1236 +++++++++ .../BrowserRuntimeWorkspace.test.tsx | 177 ++ .../BrowserRuntimeWorkspace.tsx | 221 ++ src/features/browser-runtime/api.ts | 62 + src/features/browser-runtime/index.ts | 2 + .../useBrowserRuntimeDebug.test.tsx | 192 ++ .../browser-runtime/useBrowserRuntimeDebug.ts | 876 +++++++ src/hooks/useProjects.ts | 26 + src/hooks/usePrompts.ts | 134 - src/index.css | 4 + src/lib/api/skills.ts | 14 + src/lib/artifact/parser.ts | 1 + src/lib/artifact/types.ts | 3 + src/lib/dev-bridge/safeInvoke.ts | 37 +- src/lib/resourceProjectSelection.ts | 1 + src/lib/tauri-mock/core.ts | 134 + src/lib/webview-api.ts | 339 ++- src/lib/workspace/navigation.test.ts | 50 +- src/lib/workspace/navigation.ts | 14 + src/pages/README.md | 1 + src/pages/browser-runtime-debugger.tsx | 12 + src/pages/index.ts | 1 + src/types/page.ts | 9 +- src/types/settings.ts | 1 - tailwind.config.js | 4 + 188 files changed, 35449 insertions(+), 9030 deletions(-) create mode 100644 docs/research/next-browser-reference.md create mode 100644 docs/research/nextbrowser-debugger.md create mode 100644 docs/research/nextbrowser-features-analysis.md create mode 100644 docs/research/nextbrowser-features.md create mode 100644 docs/research/nextbrowser-liveview.md create mode 100644 src-tauri/crates/agent/src/tools/skill_tool_gate.rs create mode 100644 src-tauri/crates/browser-runtime/Cargo.toml create mode 100644 src-tauri/crates/browser-runtime/src/action.rs create mode 100644 src-tauri/crates/browser-runtime/src/client.rs create mode 100644 src-tauri/crates/browser-runtime/src/lib.rs create mode 100644 src-tauri/crates/browser-runtime/src/manager.rs create mode 100644 src-tauri/crates/browser-runtime/src/types.rs create mode 100644 src-tauri/src/commands/browser_runtime_cmd.rs create mode 100644 src-tauri/src/services/browser_runtime_window.rs delete mode 100644 src/components/Sidebar.tsx create mode 100644 src/components/agent/chat/components/CanvasWorkbenchLayout.test.tsx create mode 100644 src/components/agent/chat/components/CanvasWorkbenchLayout.tsx create mode 100644 src/components/agent/chat/components/Inputbar/components/SkillSelector.test.tsx create mode 100644 src/components/agent/chat/components/Inputbar/components/SkillSelector.tsx create mode 100644 src/components/agent/chat/utils/browserAssistIntent.test.ts create mode 100644 src/components/agent/chat/utils/browserAssistIntent.ts create mode 100644 src/components/agent/chat/utils/browserAssistSession.test.ts create mode 100644 src/components/agent/chat/utils/browserAssistSession.ts create mode 100644 src/components/agent/chat/utils/canvasWorkbenchDiff.test.ts create mode 100644 src/components/agent/chat/utils/canvasWorkbenchDiff.ts create mode 100644 src/components/agent/chat/utils/generalResourceSync.test.ts create mode 100644 src/components/agent/chat/utils/generalResourceSync.ts create mode 100644 src/components/agent/chat/utils/harnessSkills.test.ts create mode 100644 src/components/agent/chat/utils/harnessSkills.ts create mode 100644 src/components/artifact/renderers/BrowserAssistRenderer.test.tsx create mode 100644 src/components/artifact/renderers/BrowserAssistRenderer.tsx delete mode 100644 src/components/extensions/ExtensionsPage.tsx delete mode 100644 src/components/extensions/index.ts create mode 100644 src/components/projects/ContentEditorPage.test.tsx create mode 100644 src/components/projects/ContentListPage.test.tsx create mode 100644 src/components/projects/CreateProjectDialog.test.tsx create mode 100644 src/components/projects/ProjectSelector.ui.test.tsx create mode 100644 src/components/projects/memory/ProjectMemoryPanels.test.tsx delete mode 100644 src/components/prompts/PromptCard.tsx delete mode 100644 src/components/prompts/PromptForm.tsx delete mode 100644 src/components/prompts/PromptsPage.tsx delete mode 100644 src/components/prompts/index.ts create mode 100644 src/components/settings-v2/account/stats/index.test.tsx create mode 100644 src/components/settings-v2/general/appearance/index.test.tsx create mode 100644 src/components/settings-v2/general/hotkeys/index.test.tsx create mode 100644 src/components/settings-v2/system/channels/index.test.tsx create mode 100644 src/components/settings-v2/system/chrome-relay/index.test.tsx create mode 100644 src/components/settings-v2/system/developer/index.test.tsx create mode 100644 src/components/settings-v2/system/environment/index.test.tsx create mode 100644 src/components/settings-v2/system/security-performance/index.test.tsx create mode 100644 src/features/browser-runtime/BrowserRuntimeDebugPanel.test.tsx create mode 100644 src/features/browser-runtime/BrowserRuntimeDebugPanel.tsx create mode 100644 src/features/browser-runtime/BrowserRuntimeWorkspace.test.tsx create mode 100644 src/features/browser-runtime/BrowserRuntimeWorkspace.tsx create mode 100644 src/features/browser-runtime/api.ts create mode 100644 src/features/browser-runtime/index.ts create mode 100644 src/features/browser-runtime/useBrowserRuntimeDebug.test.tsx create mode 100644 src/features/browser-runtime/useBrowserRuntimeDebug.ts delete mode 100644 src/hooks/usePrompts.ts create mode 100644 src/pages/browser-runtime-debugger.tsx diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6f78829e..de4ac15c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,7 +86,7 @@ jobs: cache-all-crates: true save-if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} - - name: Sync version from tag to tauri.conf.json + - name: Sync version from tag to tauri configs shell: bash run: | VERSION="${{ github.event.inputs.version || github.ref_name }}" @@ -95,11 +95,14 @@ jobs: cd src-tauri node -e " const fs = require('fs'); - const conf = JSON.parse(fs.readFileSync('tauri.conf.json', 'utf8')); - conf.version = '$VERSION'; - fs.writeFileSync('tauri.conf.json', JSON.stringify(conf, null, 2) + '\n'); + for (const file of ['tauri.conf.json', 'tauri.conf.headless.json']) { + const conf = JSON.parse(fs.readFileSync(file, 'utf8')); + conf.version = '$VERSION'; + fs.writeFileSync(file, JSON.stringify(conf, null, 2) + '\n'); + } " echo "tauri.conf.json version is now: $(node -e "console.log(JSON.parse(require('fs').readFileSync('tauri.conf.json','utf8')).version)")" + echo "tauri.conf.headless.json version is now: $(node -e "console.log(JSON.parse(require('fs').readFileSync('tauri.conf.headless.json','utf8')).version)")" - name: Load release notes shell: bash diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1d33e77a9..704269f10 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,59 +1,46 @@ -## ProxyCast v0.86.0 +## ProxyCast v0.87.0 ### ✨ 新功能 -- **Aster 集成升级**: 升级到 aster-rust v0.17.1,带来更稳定的 Agent 运行时支持 -- **排队机制**: 新增 Agent 运行时排队系统,支持多轮对话请求的有序处理 -- **Artifact 自动预览**: 实现 Artifact 自动预览同步机制,提升内容创作体验 -- **搜索结果预览**: 新增搜索结果预览列表组件,优化 Web 搜索交互 -- **Skill 脚手架**: 新增 Skill 创建脚手架对话框,简化自定义 Skill 开发流程 -- **会话作用域存储**: 实现 Agent 会话级别的状态管理机制 +- **浏览器协助运行时**:新增 `browser-runtime` crate、Tauri 命令与调试页,支持在 Agent 会话内拉起浏览器协助能力 +- **Browser Assist 产物渲染**:新增 Browser Assist Artifact 渲染器,可在 General 会话中直接承接浏览器协助状态与操作 +- **画布工作台布局**:新增 `CanvasWorkbenchLayout`,支持预览 / 文件 / 差异 / 工作台联动,以及分栏与堆叠两种布局模式 +- **技能显式选择器**:新增 `SkillSelector` 与相关 harness skill 聚合逻辑,提升技能发现、点选与触发体验 +- **新任务入口模式**:新增 `new-task` 入口、空状态页与会话恢复控制,梳理 Claw / 新任务两类进入路径 ### 🔧 优化与重构 -- **Request Tool Policy 重构**: 大幅重构请求工具策略模块(+1176 行),提升 Web 搜索预调用的可靠性 -- **Skill 服务增强**: 重构 Skill 加载器和匹配器,优化 Skill 发现和执行流程(+1064 行) -- **API Key Provider 优化**: 增强 API Key 提供商服务,改进凭证池管理(+433 行) -- **Skill Model 完善**: 新增 Skill 模型定义,规范化 Skill 元数据管理(+345 行) -- **Aster State 扩展**: 扩展 Agent 状态管理,支持更复杂的运行时场景(+367 行) -- **数据库 Schema 更新**: 新增 agent_runtime_queue 表,支持排队机制持久化 +- **General Chat 主流程重构**:重写 Agent Chat 页的大量 General 场景逻辑,统一画布、Artifact、Workbench 与浏览器协助状态 +- **Webview / Browser Runtime 协调增强**:扩展 `webview_cmd`、窗口服务与前端 API,补齐浏览器运行时会话、窗口与控制链路 +- **Settings v2 大规模重排**:重构外观、快捷键、频道、Chrome Relay、环境、安全性能、账户统计等设置页 +- **项目与记忆工作区升级**:重构内容列表、内容编辑器、项目选择器及多类 Memory 面板,统一信息架构与交互密度 +- **导航与侧栏收敛**:调整应用侧栏、任务侧栏、资源与工作区导航,移除部分旧页面与冗余入口 ### 🐛 修复 -- **Clippy 警告修复**: 修复多个 Rust clippy 警告,提升代码质量 - - 简化 `let...else` 为 `?` 操作符 - - 合并连续的字符串替换操作 - - 优化字符匹配模式 -- **类型安全改进**: 修复前端类型定义,增强 TypeScript 类型检查 - -### 📦 依赖更新 - -- 升级 aster-rust 到 v0.17.1 -- 更新相关 Rust 依赖包版本 +- **前端 Hooks 依赖修复**:补齐 `useCallback` 依赖,消除 `npm run lint` 中的 `react-hooks/exhaustive-deps` 警告 +- **Rust Clippy 清理**:移除浏览器运行时客户端中的冗余类型转换,保持 `cargo clippy` 输出干净 +- **日志测试并发污染修复**:将日志诊断测试切到独立临时目录,避免并行测试互相删除 `raw_response` 工件 +- **Workspace 错误态兜底**:补强任务侧栏与相关页面的 workspace 异常显示与恢复路径 ### 🧪 测试 -- 新增多个组件单元测试 - - Modal 组件测试 - - SearchResultPreviewList 测试 - - ArtifactRenderer UI 测试 - - SkillScaffoldDialog 测试 - - useArtifactAutoPreviewSync 测试 - - useArtifactDisplayState 测试 - - searchQueryGrouping 测试 +- 新增 Browser Assist、Canvas Workbench、Skill Selector、General Resource Sync、Harness Skills 等前端测试 +- 补充项目页、设置页、统计页、内存面板等多处 UI / 交互测试 +- 发布前已执行:`cargo fmt --all`、`cargo test`、`cargo clippy`、`npm run lint` ### 📝 文档 -- 新增设计语言文档 (design-language.md) -- 更新 AGENTS.md,补充跨平台兼容约束和 UI 指导 -- 完善 aiprompts 文档索引 +- 补充 Agent Chat / Components 文档说明 +- 新增 `docs/research/` 研究资料目录 +- 更新发布流程相关说明,覆盖 headless Tauri 配置版本同步 ### 🛠️ 开发体验 -- 新增本地 aster-rust 覆盖脚本 (setup-local-aster-override.mjs) -- 改进 legacy surfaces 报告脚本 -- 优化开发工具链配置 +- **版本一致性检查增强**:`check-app-version-consistency.mjs` 现覆盖 `tauri.conf.headless.json` +- **Release Workflow 补强**:GitHub Release 工作流现同步标准与 headless 两份 Tauri 配置版本 +- **跨端发布一致性提升**:统一 `package.json`、Cargo workspace、Tauri 配置与 release notes 的版本入口 --- -**完整变更**: v0.85.0...v0.86.0 +**完整变更**: v0.86.0...v0.87.0 diff --git a/docs/research/next-browser-reference.md b/docs/research/next-browser-reference.md new file mode 100644 index 000000000..b4552b8e2 --- /dev/null +++ b/docs/research/next-browser-reference.md @@ -0,0 +1,508 @@ +# next-browser 项目参考分析 + +> 来源: `/Users/coso/Documents/dev/ai/skills/next-browser` +> 项目: @vercel/next-browser - Vercel 官方的浏览器自动化工具 + +## 项目概述 + +`next-browser` 是 Vercel 开发的一个 **CLI 工具**,为 AI Agent 提供对 React DevTools 和 Next.js dev server 的编程访问。它将 GUI 操作(组件树、props、hooks、PPR shells、错误)转换为 **结构化文本输出的 shell 命令**。 + +### 核心理念 +- **为 AI Agent 设计** - LLM 无法读取 DevTools 面板,但可以运行 `next-browser tree` 并解析输出 +- **无状态命令** - 每个命令都是对长期运行的浏览器守护进程的一次性调用 +- **结构化输出** - 所有输出都是可解析的文本/JSON,便于 Agent 处理 + +--- + +## 值得 Proxycast 参考的设计 + +### 1. 守护进程 + 客户端架构 ⭐⭐⭐ + +**设计模式:** +``` +CLI 命令 → Unix Socket → 守护进程 → Playwright Browser +``` + +**实现细节:** +```typescript +// daemon.ts - 守护进程 +const server = createServer((socket) => { + let buffer = ""; + socket.on("data", (chunk) => { + buffer += chunk; + let newline: number; + while ((newline = buffer.indexOf("\n")) >= 0) { + const line = buffer.slice(0, newline); + buffer = buffer.slice(newline + 1); + if (line) dispatch(line, socket); + } + }); +}); + +server.listen(socketPath); // Unix socket + +async function dispatch(line: string, socket: Socket) { + const cmd = JSON.parse(line); + const result = await run(cmd).catch((err) => ({ ok: false, error: cleanError(err) })); + socket.write(JSON.stringify({ id: cmd.id, ...result }) + "\n"); +} +``` + +**Proxycast 应用:** +- ✅ **替代 Tauri event 的高吞吐方案** - 当事件推送超过 100/s 时,切换到本地 Unix Socket +- ✅ **守护进程管理浏览器实例** - 避免每次命令都启动新浏览器 +- ✅ **命令-响应模式** - 清晰的请求/响应边界,便于错误处理 + +--- + +### 2. Playwright + React DevTools 集成 ⭐⭐⭐ + +**核心技术:** +```typescript +// browser.ts +const extensionPath = resolve(import.meta.dirname, "../extensions/react-devtools-chrome"); + +// Pre-read the hook script +const installHook = readFileSync( + join(extensionPath, "build", "installHook.js"), + "utf-8", +); + +async function launch() { + const context = await chromium.launchPersistentContext(profileDirPath, { + headless: false, + args: [ + `--disable-extensions-except=${extensionPath}`, + `--load-extension=${extensionPath}`, + "--auto-open-devtools-for-tabs", + ], + }); + + // Pre-inject hook to win the race against extension + await context.addInitScript(installHook); + + return context; +} +``` + +**关键亮点:** +1. **预加载 React DevTools 扩展** - 通过 `--load-extension` 加载 Chrome 扩展 +2. **预注入 Hook** - 使用 `addInitScript` 在页面加载前注入 React DevTools hook +3. **自动打开 DevTools** - `--auto-open-devtools-for-tabs` 确保扩展激活 + +**Proxycast 应用:** +- ✅ **扩展 CDP 能力** - 不仅仅是原始 CDP,还可以加载自定义扩展 +- ✅ **预注入脚本** - 在页面加载前注入监控/调试脚本 +- ✅ **持久化上下文** - 使用 `launchPersistentContext` 保持会话状态 + +--- + +### 3. 组件树提取 ⭐⭐ + +**实现原理:** +```typescript +// tree.ts +export async function tree(page: Page) { + // 通过 React DevTools hook 获取组件树 + const operations = await page.evaluate(() => { + return window.__REACT_DEVTOOLS_GLOBAL_HOOK__.flushInitialOperations(); + }); + + // 解析 operations 构建组件树 + const tree = parseOperations(operations); + return tree; +} + +export async function inspect(page: Page, nodeId: number) { + // 获取组件的 props/hooks/state + const data = await page.evaluate((id) => { + return window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inspectElement(id); + }, nodeId); + + return { + props: data.props, + hooks: data.hooks, + state: data.state, + source: data.source, + }; +} +``` + +**Proxycast 应用:** +- ✅ **深度调试能力** - 不仅看到 DOM,还能看到 React 组件结构 +- ✅ **状态检查** - 检查组件的 props/hooks/state +- ✅ **源码定位** - 通过 source map 定位到原始源码位置 + +--- + +### 4. PPR (Partial Prerendering) 锁定机制 ⭐⭐⭐ + +**核心概念:** +```typescript +// browser.ts +let release: (() => void) | null = null; +let settled: Promise | null = null; + +export async function lock() { + if (release) return; // Already locked + + // Use @next/playwright's instant() to set cookie + const { promise, resolve } = Promise.withResolvers(); + release = resolve; + settled = promise; + + await instant(page!, async () => { + await settled; // Block until unlock() + }); +} + +export async function unlock() { + if (!release) return; + + // Capture locked state + const lockedSnapshot = await captureSuspenseBoundaries(); + + // Release the lock + release(); + release = null; + settled = null; + + // Wait for boundaries to settle + await page!.waitForTimeout(500); + + // Capture unlocked state + const unlockedSnapshot = await captureSuspenseBoundaries(); + + // Analyze the diff + return analyzePPRShell(lockedSnapshot, unlockedSnapshot); +} +``` + +**工作原理:** +1. **Lock** - 设置 `next-instant-navigation-testing=1` cookie,阻止动态数据加载 +2. **Capture** - 截取 PPR shell(静态 HTML + `