十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Electric Agents Mobile:基于 Expo 与 DOM Components 构建原生移动端 Agent 客户端

Electric Agents Mobile:基于 Expo 与 DOM Components 构建原生移动端 Agent 客户端 Electric Agents Mobile基于 Expo 与 DOM Components 构建原生移动端 Agent 客户端【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric导读本文以 packages/agents-mobile/PLAN.md 为主线完整拆解 Electric Agents 移动端electric-ax/agents-mobile的设计与落地过程它如何用 Expo React Native 构建原生外壳同时通过 Expo DOM Components / WebView 复用agents-server-ui的聊天与状态检查界面并借助一套类型化的 postMessage 桥接协议实现原生导航 Web 视图内容的双层架构。读完你将掌握embed 构建管线vite build --mode mobile-embed、window.__MOBILE_EMBED__启动契约、Native ⇄ Embed 消息协议、原生五屏导航结构以及 bundle 瘦身与验证命令等可直接复用的实战细节。一、目标与产品决策不是移植桌面 UI而是移动优先的客户端PLAN.md 明确了移动端的定位通过 URL 连接已有 agents server不自带本地 Horton 运行时。App 的目标不是把桌面/Web 工作台逐像素移植到手机上而是感觉像 Electric Agents 桌面/Web UI但具备原生移动外壳——移动导航、原生工具栏、抽屉/侧边栏行为且同一时刻只渲染一个活跃视图。围绕这一定位规划阶段定下了一系列产品决策原文照录并加以展开用 Expo 承载移动 App当前仓库为 Expo SDK 54 / React Native 0.81.5 / React 19.1.0见 package.json复用 Web UI 资源并打包进移动 Appbundled asset 策略见下文v1 只支持服务端 URL不做鉴权流仓库后续演进中已加入 Cloud auth 与 OAuth 回调属于 v1 之后的扩展移动端暂不暴露工作目录working-directory控件不支持桌面式平铺tiling、分屏split panes、Tile 拖拽与布局持久化聊天/会话视图通过 WebView 复用移动外壳/导航/侧边栏/顶部工具栏全部使用 React Native 原生控件实现。这些决策直接决定了后续的包结构与双层架构——原生层负责壳Web 层负责内容。二、包结构与双层架构Native Shell Bundled Web Views2.1 包形态移动端作为独立 workspace 包存在于packages/agents-mobile它依赖packages/agents-server-ui产出的 embed 构建产物。规划中移动包应拥有的职责全部能在仓库中对应到具体文件职责仓库对应文件相对路径Expo app 配置与原生运行时app.config.ts、App.tsx、index.ts服务端 URL 设置与持久化ServerSetupScreen.tsx、savedServers.ts原生会话列表 / 抽屉SessionListScreen.tsx、SessionTree.tsx原生顶部工具栏Header.tsx、TopBarIconButton.tsx原生路由与返回行为app/_layout.tsxexpo-routerWebView 容器屏SessionScreen.tsx移动设置HomeMenu.tsx原生 UI 与嵌入 Web 视图的主题协调ThemeProvider.tsx、theme.ts2.2 架构演进的关键转向从 WebView 嵌入到 Expo DOM ComponentsPLAN.md 特别记录了一次重要架构修正实现已从最初的静态react-native-webviewembed 方案迁移到 Expo DOM Components。原生外壳现在通过packages/agents-server-ui/src/embed/SessionDomEmbed.tsx渲染会话表面而旧的assets/embed.htmlbundle、WebView 桥与build:mobile-embed流程已被移除。在 SessionDomEmbed.tsx 中可以看到use dom指令——这是 Expo DOM Components 的入口标记其注释明确说明将仅 Web 的依赖、CSS Modules 与 TanStack DB 实例留在agents-server-ui内而不是从原生 mobile 包重复导入。文件同时把 embed 内的路由跳转翻译为对外回调onNavigatePathname捕获/entity(...)路径后转发给原生侧onRequestOpenEntity。2.3 两层各自的职责边界Native ShellReact Native拥有服务端设置屏、会话列表屏、新建会话屏、会话详情屏原生顶部工具栏、原生抽屉/侧边栏、设置屏聊天与状态检查器state explorer之间的视图切换。原生外壳需要订阅 agents server 的实体/会话元数据。PLAN.md 对此给出明确的技术立场预期 Electric / TanStack DB 客户端栈能在 Expo 中正常工作若不能应修复 Electric 或 TanStack DB 侧的兼容性问题而不是过早地绕开它单独设计移动端专用 API。仓库中tanstack/db、tanstack/react-db、tanstack/electric-db-collection均直接出现在移动包依赖里见 package.json印证了这一决策。Bundled Web Viewsembed拥有聊天时间线Chat timeline聊天输入框Chat composer状态检查器State explorer未来按实体定制的 Web 视图。WebView 内禁止渲染侧边栏、工作区平铺、分屏菜单、Tile 拖拽、布局持久化、桌面运行时控件、本地 Horton 的 API key 设置、工作目录选择器。三、Server UI Embed Build独立入口与单文件产物3.1 独立的 embed 入口规划要求为packages/agents-server-ui添加一个独立于完整路由 App 的 embed 入口建议文件为src/embed/main.tsx、src/embed/App.tsx、src/embed/embed.css。仓库实际落地的结构更为细化EmbedApp.tsx —— embed 应用根组件与内存路由EmbedApp.module.css —— 移动端 CSS 覆盖表SessionDomEmbed.tsx —— Expo DOM Components 入口SessionChatLogDomEmbed.tsx 与 SessionStateInspectorDomEmbed.tsx —— 按视图拆分的 DOM 入口mobileDomRuntime.ts —— 移动 WebView 运行时 polyfillstubs/ —— mermaid / shiki / katex / streamdown-math 的轻量替身。3.2 复用什么、避免什么embed 应复用现有 provider 与视图主体但跳过完整工作台外壳。在 EmbedApp.tsx 的EmbedSessionRoot中可以验证复用ThemeProviderappearance{state.theme}、ElectricAgentsProviderbaseUrl{state.serverUrl}、ChatView/ChatLogView、StateExplorerView、ToastProvider以及agents-server-ui/src/components/views/下既有的 markdown / 工具调用渲染。避免完整 App 的RouterProviderembed 改用自建的EmbeddedRoutercreateMemoryHistory路由体为 no-op、Workspace、TileContainer、SplitContainer、Sidebar与桌面 IPC hooks。值得注意的细节是WorkspaceProvider仍然挂载——注释说明 state-explorer 视图内部会调用useWorkspaceembed 只是满足 context其对固定视图的 tile 派发是惰性的inert。3.3 EmbeddedRouter形状一致、行为为空的迷你路由EmbedApp.tsx 中EmbeddedRouter的实现颇具巧思它用createRootRouteindexRouteentityRoute构建一棵与桌面/Web 路由形状一致的迷你路由树使ChatView、EntityContextDrawer等组件内部调用的useNavigate({ to: /entity/$ })能够正常 resolve 而不报错同时路由体组件返回null并通过router.subscribe(onResolved)把每次路由解析结果以pathname转发给原生外壳由原生决定换实体 / push 新屏 / 忽略。这正是 PLAN.md 中navigate消息的来源。四、Embed Boot Contractwindow.__MOBILE_EMBED__embed 的启动契约是移动端与 Web 视图之间的第一道握手原生外壳通过WebView.injectedJavaScriptBeforeContentLoaded在任何 embed 脚本运行之前注入初始配置到window.__MOBILE_EMBED__embed 侧在readEmbedConfig()中同步读取保证首帧绘制即与宿主一致作为兜底URL hash 参数#serverUrl…entityUrl…view…theme…同样被支持便于开发期在普通浏览器里直接打开 embed 页面。PLAN.md 明确支持的视图为chat与state-explorer仓库中的 embed 代码进一步演化出chat-log视图供原生聊天输入框场景使用。embed 状态对象在 EmbedApp.tsx 的EmbedState类型中可见包含serverUrl、entityUrl、view、theme、inlineQueuedMessages、bottomInset、serverHeaders等字段——其中bottomInset会被写到文档根节点的--mobile-chat-bottom-insetCSS 变量上让传送到body的图片预览对话框也能避开原生 composer 的遮挡。五、WebView Bridge原生 ⇄ embed 的 JSON 信封协议桥接协议是原生导航 Web 内容架构的中枢。PLAN.md 规定这是一个极小的、类型化的 JSON 信封走 WebViewpostMessage通道两侧在镜像文件中共享同一份 schemapackages/agents-mobile/src/webview/bridge.ts与packages/agents-server-ui/src/embed/bridge.ts并配套两侧的 vitest 解析器测试agents-server-ui/src/embed/__tests__/bridge.test.ts与agents-mobile/src/webview/__tests__/bridge.test.ts合计 24 个用例。5.1 消息清单Native → Embedready之后的实时更新{ type: set-view, view: chat | state-explorer } { type: set-entity, entityUrl: string } { type: set-theme, theme: light | dark }Embed → Native{ type: ready, /* React 树挂载后发出 */ } { type: navigate, pathname: string } { type: error, message: string }5.2 为什么需要ready与常驻 WebViewready保证消息不丢宿主在收到ready之前会把所有set-*消息排队等 embed 的 React 树真正挂载后再放行避免挂载前与首次交互之间的消息丢失。常驻避免重复解析 bundle实时的set-*消息用于切换视图、实体与主题无需重新解析数 MB 的 bundle。WebView 以embed.uri为 key 常驻于SessionScreen的整个生命周期仓库中进一步演进为 App 级的PersistentEmbed离屏时用display: none隐藏跨屏导航也不再重解析。跨屏导航返回列表、从列表打开另一个会话会卸载 WebView 并重新解析——这是 v1 接受的代价。EntityHost以entityUrl为 key切换活跃实体时聊天输入与滚动位置能干净复位见 EmbedApp.tsx 的EntityHost。六、原生屏幕设计6.1 Server Setup首次运行首启流程要求用户输入 agents server URL并用以下请求做健康校验GET /_electric/health校验通过后将选中的 server URL 持久化到本地仓库对应 savedServers.ts 与 ServerSetupScreen.tsx后者实现了服务端验证与保存逻辑。6.2 Session List会话列表展示活跃 server 的实体原生列表借用 Web 侧边栏已有的分组与展示思路并针对移动端调整最近会话置顶状态指示器status indicators之后可按需加入置顶会话与搜索/过滤列表不包含 split / open-to-side 行为。仓库中 SessionRow.tsx 实现了行级展示左侧 22px 状态点列、右侧小写类型标签与折叠子树时的N计数、chevron 展开提示、停止会话 0.55 透明度、缩进8 depth * 12SessionTree.tsx 递归渲染父子树并用逐行连接线 1:1 还原 Web 侧SidebarRow.module.css的伪元素连线效果StatusDot.tsx 与agents-server-ui/src/components/StatusDot.tsx保持同一套跨主题固定色板#3b82f6运行中 /#22c55e空闲 /#eab308生成中 /#cbd5e1已停止。6.3 New Session新建会话v1 的原生新建会话屏需要从 server 加载实体类型entity types优先使用默认hortonagent存在时允许用户输入初始消息使用 server 默认参数生成不询问工作目录。生成成功后导航到新会话屏。仓库中的 NewSessionScreen.tsx 及 spawnArgs.ts、SchemaArgsControls.tsx 覆盖了参数默认值与 schema 驱动的参数控件。6.4 Session Detail会话详情原生屏包含顶部工具栏返回/菜单、标题、状态、视图切换器 选中实体视图的 WebView默认视图为chat。6.5 Settings设置移动端设置刻意保持精简v1 仅包含活跃 server URL、主题、诊断/版本信息不包含桌面本地运行时设置。仓库中诊断能力落在 DiagnosticsScreen.tsx展示 server URL、实时健康探测、embed bundle URI/大小与 App/Expo/OS 版本。七、Bundled Asset 策略为什么把 Web 构建打包进 App移动 App 将 embed 的 Web 构建作为静态资源打包WebView 加载本地 HTML bundle该 embed App 再去连接远端 agents server。这一策略带来三个收益PLAN.md 原文版本匹配可预期移动端/Web embed 版本与应用发布严格绑定不依赖 server 托管前端资源无需每个 agents server 都提供移动端专用前端UI 外壳离线可用即使 server 不可达界面框架仍在。当然数据、流、会话生成与消息发送仍然必须依赖 server。八、样式方向与移动端 CSS 覆盖样式基调是先与现有 App 视觉一致再针对移动端人体工学适配保持 Electric Agents 的配色、字体、间距与消息样式增大触摸目标touch targets尊重安全区safe areas针对手机屏幕收窄聊栏宽度优先使用原生工具栏/抽屉转场在移动路径上移除仅 hover 的交互暗示让 composer 与移动键盘的配合足够健壮。PLAN.md 特别点名 embed 需要移动端专属 CSS重点覆盖viewport 高度、安全区 padding、键盘遮挡、滚动到底部行为、紧凑消息间距、state explorer 响应式。仓库中 EmbedApp.module.css 实现了这些覆盖——例如取消EntityTimeline.content、MessageInput.root、MessageInput.composer的 36–40px gutter让聊天几何结构在移动端横向铺满。配套的构建技巧是Vite mobile-embed 模式把 CSS Modules 输出固定为[name]_[local]_[hash:base64:5]使 embed 覆盖选择器如[class*EntityTimeline][class*_content_]能稳定匹配生产环境的 hash 类名。另外 embed 的viewport为maximum-scale1, user-scalableno并通过覆盖表把input/textarea字号下限设为 16px避免 iOS 聚焦时自动放大。九、风险清单与针对性对策PLAN.md 列出的三项主要风险仓库均有对应落地React Native 兼容性预期 Electric / TanStack DB 客户端栈可在 Expo 运行若有兼容问题优先修 Electric / TanStack DB 本身而非过早新增移动专用 server API。仓库实测遇到的真实问题是crypto.randomUUID缺失——移动 WebView 可能只有crypto.getRandomValues而没有更新的randomUUID帮助函数而 TanStack DB/runtime 创建事务时依赖randomUUID。mobileDomRuntime.ts 在 embed 边界安装 polyfill 解决原生侧则在 index.ts 顶部引入react-native-random-uuid确保在任何模块求值crypto.randomUUID之前完成装载。WebView 键盘与滚动聊天 composer 与时间线需要在 iOS/Android 上尽早测试WebView 键盘尺寸变化、安全区与滚动锚定是 UX 风险最高的细节。仓库的应对包括WebView 外包KeyboardAvoidingViewiOS 用paddingembed CSS 用height: 100%而非100vh以便随键盘收缩见 PLAN.md Phase 5。Bundle 接线把 Vite 产物接入 Expo 需要一条小型构建管线embed 构建必须与完整桌面/Web 构建隔离避免移动端继承工作台外壳行为。此外 iOS 的 JavaScriptCore 只在 16.4 支持正则 lookbehind而 chat DOM bundlestreamdown内含 lookbehind——因此 app.config.ts 通过expo-build-properties将 iOSdeploymentTarget固定为16.4低于此版本整个 DOM bundle 会解析失败导致聊天空白。十、实施阶段从骨架到打磨PLAN.md 将实施拆为六个阶段并标注了仓库中的完成状态这是理解当前代码组织方式的最佳索引Phase 1Skeleton — DONE在packages/agents-mobile创建 Expo 包加入react-native-webview服务端 URL 设置 健康检查活跃 server 的本地持久化基础原生导航跟踪 Expo SDK 54React Native 0.81React 19.1。pnpm monorepo 内置的 Metro 支持意味着无需metro.config.js覆盖——html已在默认assetExts中、每个 workspace 包都在watchFolders里、unstable_enableSymlinks不再需要。可运行pnpm dlx expo-doctor确认。Phase 2Server Data — DONE复用/抽取 Electric Agents 客户端逻辑构建 entity 与 entity-type 集合渲染原生会话列表渲染不带工作目录支持的原生新建会话流程。Phase 3Embedded Chat — IN PROGRESS已完成项节选自 PLAN.md 原文在packages/agents-server-ui/embed.htmlsrc/embed/main.tsx增加 mobile-embed 入口用vite build --mode mobile-embedvite-plugin-singlefile将整个 SPA 内联为单个 HTML新增scripts/emit-mobile-embed.mjs把该 HTML 复制到packages/agents-mobile/assets/embed.html作为受跟踪静态资源移动端 WebView 宿主embedSource.ts通过expo-asset加载该资源避免 Metro 把数 MB 字符串内联进 JS bundleembed 禁用缩放与 iOS 聚焦自动放大CSS Modules hash 模式固定保证覆盖选择器匹配生产产物。待办⏭端到端验证 streaming、发送、markdown、工具调用与重连行为裁剪 mermaid / shiki / katex / streamdown 等重型渲染器避免 bundle 达 13 MB仓库后续已在 Phase 6 通过 stubs/ 别名将 bundle 从 13.1 MB 压到 5.0 MB移动聊天以代码不高亮、跳过图表与公式为代价v1 可接受。Phase 4Native Mobile Shell — IN PROGRESS外壳设计从Web 侧边栏的逐字移植转向标准移动聊天 App 模式ChatGPT / Claude 风格Web 侧边栏概念仍在但收进 kebab 菜单而非常驻底部。已完成的组件族Header支持两种布局alignleading主页的[title flex][actions]与aligncenterpush 屏的[leading abs][居中 title][actions abs]对齐UINavigationBarIcon原语用react-native-svg绘制小型内联 SVG 集back / search / more / pencil / check / chevron-right / sun / moon / system / server / filter / info / swap / chat / databaseLucide 风格描边图标与 Web 侧边栏保持资源对等且无字体依赖TopBarIconButton36×36 ghost 按钮Fab右下角安全区感知的扩展胶囊作为会话列表的新建主 CTASessionListScreen主页结构[Electric Agents] … [search][more] / list / FAB搜索时切换内联SearchBarSessionRow/StatusDot/SessionTree见前文expandedTree.tsuseExpandedTreeNodes.ts的移动镜像按 url 分桶监听使折叠切换只重渲染对应行并持久化到 AsyncStorageHomeMenu主页 kebabBottomSheet 子页面覆盖 Server状态点 切换、Group by、Show按类型/状态可见性钻取、Theme、DiagnosticsuseSidebarPrefs与useThemePreferencesystem/light/dark均被HomeMenu消费SessionScreen工具栏遵循 iOS 聊天范式[← 返回][居中标题][⋯ kebab]kebab 展开SessionMenu暴露实体状态与 chat / state-explorer 视图切换StatusBar样式随解析后的主题翻转theme serverUrl entityUrl view 通过injectedJavaScriptBeforeContentLoaded注入 WebView设置window.__MOBILE_EMBED__。待办⏭移除不再需要的组件SidebarRow、IconToggle、Badge、SidebarFooter、ServerPickerTile、FooterIconButtonStatusDot因树视图重新恢复。Phase 5Additional Views Bridge — DONE基础桥协议在bridge.ts中正式化两侧镜像覆盖ready/navigate/set-view/set-entity/set-theme原生只在 embed 确认ready后发送set-*避免启动期消息静默丢失State Explorer 切换与实体导航全部经由桥切视图、跳转关联实体、切主题都在原地重渲染不重解析 bundleWebView 外包KeyboardAvoidingViewiOSpaddingembed CSS 用height: 100%EntityHost以entityUrl为 key实体切换时输入/滚动干净复位常驻 WebViewPersistentEmbed位于 App 层离屏时display: none跨屏导航不再重解析 bundle会话列表下拉刷新会以 600ms 最短停留时间重跑连通性探测。Phase 6Polish — DONE基础原生屏与 Web App 消费同一套颜色/间距/圆角/字体 token亮暗双模式一致Screen使用react-native-safe-area-context的SafeAreaView顶层套SafeAreaProvider同时适配 iOS 刘海与 AndroidedgeToEdgeEnabled状态栏用expo-status-barstylelight|dark与边缘到边缘的透明系统栏协同Crypto polyfill 提升到index.tsDiagnosticsScreen暴露 server URL 实时健康探测 embed bundle URI/大小 App/Expo/OS 版本bundle 13.1 MB → 5.0 MBstub 别名bridge.ts解析器双侧 vitest 覆盖合计 24 测试。待办⏭横屏/平板的安全区边界情况。十一、构建与验证实操命令11.1 按需生成 embed bundlebundle 是opt-in的且因单文件构建体量过大当前约 13 MB 量级瘦身后约 5 MB被排除在 git 历史之外。可按需从任意一侧生成# 从仓库根目录 pnpm --filter electric-ax/agents-server-ui build:mobile-embed # 或从移动包执行同一脚本只是路径更便捷 pnpm --filter electric-ax/agents-mobile embed:build该脚本会用新构建覆盖packages/agents-mobile/assets/embed.html。Metro 在下次 reload 时即可拾取embedSource.ts通过expo-asset解析它。仓库在该路径下保留一个小占位文件保证全新 checkout 也能正常解析不要把重新生成的数 MB 文件提交回 git。11.2 基础验证pnpm --filter electric-ax/agents-mobile typecheck pnpm --filter electric-ax/agents-mobile doctor # expo-doctor17/17 必须通过 pnpm --filter electric-ax/agents-server-ui typecheck11.3 改动 embed 后的联调pnpm --filter electric-ax/agents-mobile embed:build pnpm --filter electric-ax/agents-mobile ios # 或 android在模拟器中确认 JS console 里的ready/set-*消息流是否正常。十二、第一里程碑架构有效性的最小证明PLAN.md 定义的首个可用里程碑为用户输入 server URLApp 在原生列表中展示实时会话用户用初始消息启动默认 Horton 会话App 导航到原生会话屏聊天在 bundled WebView 内运行消息能正确流式输出与发送。这个里程碑在不提前投入次要视图与打磨的前提下验证主架构。仓库当前的状态表明该里程碑已远超达成——原生会话屏SessionScreen.tsx已实现原生 composerNativeComposer、附件attachments.ts、expo-image-picker、斜杠命令自动补全slashAutocomplete.ts、权限useEntityPermissions.ts、分享ShareSessionScreen与 Cloud authCloudAuthContext、OAuth 回调等更上层能力但架构主干与 PLAN.md 的设计完全一致原生外壳 常驻 DOM embed 类型化桥协议。十三、延伸阅读完整规划packages/agents-mobile/PLAN.md移动包结构与脚本packages/agents-mobile/package.json、packages/agents-mobile/app.config.tsembed 实现packages/agents-server-ui/src/embed/EmbedApp.tsx、packages/agents-server-ui/src/embed/SessionDomEmbed.tsx、packages/agents-server-ui/src/embed/mobileDomRuntime.ts原生组件族packages/agents-mobile/src/components/、原生屏幕packages/agents-mobile/src/screens/、客户端逻辑packages/agents-mobile/src/lib/【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表