Onlook 仓库 AI Agent 开发指南:Monorepo 架构约束、tRPC/Next.js 实践与最小化 Diff 原则
2026/9/10 21:25:33 网站建设 项目流程

Onlook 仓库 AI Agent 开发指南:Monorepo 架构约束、tRPC/Next.js 实践与最小化 Diff 原则

【免费下载链接】onlookThe Cursor for Designers • An Open-Source AI-First Design tool • Visually build, style, and edit your React App with AI项目地址: https://gitcode.com/GitHub_Trending/on/onlook

本文基于 Onlook 仓库根目录的 CLAUDE.md(Agent 指南)展开,并结合仓库源码逐一验证其规则背后的实现依据。Onlook 是一个开源的、AI-First 的 React 可视化编辑工具("The Cursor for Designers"),仓库采用 Bun workspaces 管理的 Monorepo 结构。本文面向在 Onlook 仓库内工作的自动化编码 Agent(以及希望以 Agent 友好的方式理解该仓库的开发者),系统梳理其技术栈约束、目录约定、客户端/服务端边界、tRPC 路由注册机制、环境变量治理、MobX store 生命周期等核心规则,并给出可直接执行的常用命令。读完本文,你将掌握一套"正确性优先、diff 最小化、token 高效"的仓库级 Agent 协作规范,以及每条规则对应的真实源码位置。

指南的定位与适用范围

CLAUDE.md是一份"可执行规则"(Actionable rules)文档,受众是工作于本仓库内的自动化编码 Agent。其核心目标非常明确:

  • 目标:生成小而正确(small, correct)的 diff,并与项目既有架构保持一致;
  • 非目标:不修改生成的产物(generated artifacts)、锁文件(lockfiles)或node_modules

这意味着任何 Agent 在本仓库内改动代码时,第一优先级不是"完成功能",而是"以最小范围、最小副作用的方式完成功能"。与之配套,文档还给出了上下文纪律(Context Discipline):用 ripgrep 精确搜索、只打开必要的文件、只读小段内容、避免读取node_modules/.next/大资源文件,从而在 token 受限的 Agent 环境中高效工作。

仓库全景(Repo Map)

Onlook 是一个由Bun workspaces管理的 Monorepo,这一点在根目录 package.json 中得到确认:

{ "name": "@onlook/repo", "packageManager": "bun@1.3.1", "workspaces": [ "packages/*", "apps/*", "tooling/*", "apps/web/*", "docs" ] }

文档给出的核心目录映射如下:

职责路径
主应用(Next.js App Router + TailwindCSS)apps/web/client
API 路由(tRPC routers)apps/web/client/src/server/api/routers/*
tRPC 路由聚合入口apps/web/client/src/server/api/root.ts
共享工具包packages/*(如packages/utility

仓库中packages/下还有大量共享能力包,如packages/ui(组件库)、packages/db(Drizzle ORM 数据层)、packages/models(领域模型)、packages/parser(代码解析)、packages/code-provider(沙箱代码提供)等,它们共同支撑主应用的编辑器、AI 对话、发布等能力。根package.json中 workspace 声明还包含apps/web/*,即 web 目录下的clientpreloadserver都属于同一 workspace 体系。

技术栈与运行时约束

文档明确规定了本仓库的技术选型与唯一运行时:

  • UI:Next.js App Router + TailwindCSS;
  • API:tRPC + Zod(位于apps/web/client/src/server/api/*);
  • 包管理器仅允许 Bun,所有安装与脚本执行都必须使用 Bun,禁止 npm、yarn 或 pnpm。

根 package.json 的 scripts 全部以bun --filter ...cd ... && bun ...形式组织,例如typechecktestdb:push等,印证了这一约束。此外根目录存在 bun.lock 与 bunfig.toml,进一步说明 Bun 是唯一受支持的包管理器。

Agent 优先级(Agent Priorities)

在动手之前,Agent 应遵循以下优先级清单:

  • 正确性优先:最小范围(minimal scope)与定向编辑(targeted edits);
  • 尊重 App Router 中的客户端/服务端边界;
  • 优先复用本地既有模式与现有抽象,避免引入一次性框架(one-off frameworks);
  • 不修改构建产物、生成文件或锁文件;
  • 所有脚本一律使用 Bun,不引入 npm/yarn;
  • 在自动化环境中避免运行本地开发服务器;
  • 尊重类型安全。

Next.js App Router 实践

apps/web/client是标准的 Next.js App Router 应用,文档给出以下强制规则:

Server Component 优先

  • 默认使用 Server Components;只有在需要事件处理、state/effects、浏览器 API 或客户端专属库时,才添加use client指令。
  • 应用结构位于apps/web/client/src/app/**(含page.tsxlayout.tsxroute.ts)。

客户端边界(Client Boundary)

客户端 Provider 必须位于客户端边界之后,典型例子是 apps/web/client/src/trpc/react.tsx,其首行即为'use client'

文档强调的一个模式是:只需在功能入口处设置一个客户端边界,其内部的子组件即使使用了observer也无需重复添加use client。典型示例是 apps/web/client/src/app/project/[id]/_components/main.tsx,该文件首行'use client'后使用observer包裹了整个编辑器主界面,而其内部导入的EditorBarCanvasLeftPanelRightPanel等子组件不必各自声明客户端边界。

Root Layout 作为 RSC 壳

apps/web/client/src/app/layout.tsx 是一个典型的 RSC 壳:全局样式(@/styles/globals.css@onlook/ui/globals.css)、TRPCReactProviderFeatureFlagsProviderTelemetryProviderThemeProviderforcedTheme="dark"保持暗色主题默认)、AuthProviderNextIntlClientProvider依次嵌套,第三方脚本(Zaraz、RB2BLoader)通过env.NODE_ENV === 'production'门控,仅在正式环境加载——这与文档中"scripts gated by env"的描述完全一致。

MobX observer 组件必须是客户端组件

使用mobx-react-liteobserver的组件必须位于客户端边界内(含use client)。相关依赖可在 apps/web/client/package.json 中确认。

tRPC API 架构

tRPC 是 Onlook 服务端 API 的唯一通道,文档规则如下:

  • Routers 位于apps/web/client/src/server/api/routers/**必须apps/web/client/src/server/api/root.ts中导出;
  • 使用apps/web/client/src/server/api/trpc.ts中的publicProcedure/protectedProcedure,并用 Zod 校验输入;
  • 序列化由 SuperJSON 处理,返回普通对象/数组;
  • 客户端通过apps/web/client/src/trpc/react.tsx(React Query + tRPC links)消费。

Router 目录组织

从源码看,apps/web/client/src/server/api/routers/按领域划分子目录:chat/(会话、消息、建议)、project/(分支、fork、frame、成员、沙箱、设置)、domain/(自定义域名与验证)、publish/(部署、发布/取消发布)、subscription/usage/user/forward/(编辑器转发)等,另有code.tsgithub.tsimage.ts等顶层文件。

root.ts 的手动聚合

apps/web/client/src/server/api/root.ts 是"新增 Router 必须在此注册"的直接证据——它手动把sandboxRouteruserRouterinvitationRouterprojectRouterbranchRoutersettingsRouterchatRouterframeRouteruserCanvasRouterutilsRoutermemberRouterdomainRoutergithubRoutersubscriptionRouterusageRouterpublishRouter聚合进appRouter,并导出AppRouter类型与createCaller服务端调用器。如果新增 Router 忘记在此注册,端点将不可达(这也是 Common Pitfalls 中专门列出的一条)。

trpc.ts 的过程定义

apps/web/client/src/server/api/trpc.ts 详细展示了三种过程的实现:

  • publicProcedure:基础过程,挂载timingMiddleware(开发环境模拟 100–500ms 随机网络延迟,帮助提前发现瀑布式请求,并打印每个路径的执行耗时);
  • protectedProcedure:在公共过程基础上校验ctx.user与用户邮箱,未认证直接抛UNAUTHORIZED
  • adminProcedure:更进一步,注入createAdminClient()(Supabase service role)以绕过 RLS——注释明确警告"Use with extreme caution as it bypasses RLS policies"。

上下文通过createTRPCContext构建:先调用createClient()(服务端 Supabase 客户端)获取用户会话,再注入 Drizzledbuser。错误格式化器会捕获ZodError并展平(flatten)为前端可用的zodError字段。示例 Router(apps/web/client/src/server/api/routers/project/branch.ts)展示了典型写法:protectedProcedure.input(branchInsertSchema).mutation(...),其中输入 schema 直接来自@onlook/db

客户端接入

apps/web/client/src/trpc/react.tsx 使用createTRPCReact<AppRouter>()建立类型安全的 API 客户端,并用单例模式(clientQueryClientSingleton)在浏览器侧复用 QueryClient;apps/web/client/src/trpc/query-client.ts 设置 30 秒默认staleTime避免 SSR 后立即重复请求,并通过 SuperJSON 完成数据脱水/水合。

Auth 与 Supabase

Onlook 使用 Supabase 管理认证与会话,文档规定了两类客户端并强调边界:

  • 服务端客户端:apps/web/client/src/utils/supabase/server.ts 基于@supabase/ssrcreateServerClient,通过next/headerscookies()读写会话 Cookie,供 Server Components、Server Actions 与路由使用;
  • 浏览器客户端:apps/web/client/src/utils/supabase/client/index.ts 基于createBrowserClient,供客户端组件使用,并额外封装了存储桶的 URL 获取、文件信息与上传工具;
  • 禁止将服务端专属客户端传入客户端代码。

apps/web/client/src/utils/supabase/下还有admin.ts(service role 管理客户端)、middleware.ts(会话刷新)、request-server.ts等配套模块。

环境变量与配置治理

环境变量的定义与校验集中在 apps/web/client/src/env.ts,采用@t3-oss/env-nextjs+ Zod:

  • server schema:校验NODE_ENVCSB_API_KEYSUPABASE_DATABASE_URLSUPABASE_SERVICE_ROLE_KEYOPENROUTER_API_KEY等必填项,以及 Stripe、Bedrock、Vertex AI、Anthropic、OpenAI、n8n、Firecrawl、Exa、Langfuse、GitHub App 等一组可选密钥;
  • client schema:仅暴露NEXT_PUBLIC_*前缀变量(如NEXT_PUBLIC_SITE_URLNEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEY),其中NEXT_PUBLIC_FEATURE_COLLABORATIONz.coerce.boolean()将字符串转为布尔;
  • 运行时映射runtimeEnv手动逐项映射process.env,这正是 t3-env 在 Edge runtime 下无法解构process.env的应对方案;
  • 跳过校验SKIP_ENV_VALIDATION环境变量可跳过校验(对 Docker 构建尤其有用);
  • 空串语义emptyStringAsUndefined: true,即SOME_VAR=''会被视为 undefined,从而触发 Zod 校验错误。

apps/web/client/next.config.ts 首部即import './src/env',在构建期强制执行环境变量校验——与文档要求完全对应。

process.env 的使用边界

文档对process.env的使用做了严格限定:

  • 优先使用@/env导出的env
  • 在服务端专属 helper 中(例如 apps/web/client/src/trpc/helpers.ts 的getBaseUrl()),只允许读取部署类变量VERCEL_URL/PORT
  • 客户端代码中禁止使用process.env
  • 共享模块中如需读取,必须用typeof window === 'undefined'守卫。

getBaseUrl()的实现正是这一规则的范本:浏览器环境返回window.location.origin,否则回退到https://${process.env.VERCEL_URL}http://localhost:${process.env.PORT ?? 3000},并且整个文件只会被 tRPC 客户端链路在服务端/客户端两侧加载。

导入与路径别名

文档规定路径别名@/*~/*均映射到apps/web/client/src/*。这在 apps/web/client/tsconfig.json 中得到确认:

"paths": { "@/*": ["./src/*"], "/*": ["./*"], "~/*": ["./src/*"] }

配套规则:

  • 不得将服务端专属模块导入客户端组件(会引发打包/运行时错误);
  • 例外:编辑器某些代码编辑模块已使用 Node 的path,仅在那些模块内复用,不要在客户端代码中导入process
  • 必要时按环境拆分文件(server 文件 vs client 文件)。

从实际代码看,tRPC 层服务端文件普遍使用~/*(如root.tsimport { createCallerFactory, createTRPCRouter } from '~/server/api/trpc'),而客户端组件文件多用@/*(如layout.tsximport { env } from '@/env'),两种别名等价可用。

MobX + React Stores

编辑器核心状态由 MobX 管理,apps/web/client/src/components/store/editor/engine.ts 中的EditorEngine是文档点名引用的示例 store(第 1 行即import { makeAutoObservable } from 'mobx')。文档针对 store 生命周期给出了一组易踩坑的纪律:

  • useState(() => new Store())创建 store 实例,保证跨渲染稳定;
  • 将活跃 store 保存在useRef中;异步清理用setTimeout(() => storeRef.current?.clear(), 0),避免路由切换时的竞态(race conditions);
  • 避免useMemo创建 store 实例——React 可能丢弃被 memo 的值导致数据丢失;
  • 如果 store 实例放入 effect deps 会造成死循环,则拆分关注点(如 project 与 branch 分开);
  • observer组件仅限客户端;在功能入口放置一个客户端边界即可,子级 observer 无需重复use client(例:apps/web/client/src/app/project/[id]/_components/main.tsx)。

EditorEngine内部聚合了大量 Manager(BranchManagerCanvasManagerChatManagerCodeManagerElementsManagerFramesManagerStyleManagerTextEditingManager等),是理解编辑器架构的入口。

样式与 UI

  • TailwindCSS 优先,全局样式已在 apps/web/client/src/app/layout.tsx 中导入(@/styles/globals.css@onlook/ui/globals.css);
  • 优先复用@onlook/ui提供的组件与本地既有模式,避免自造轮子。packages/ui/src/components/下是 shadcn 风格的组件库(button、dialog、dropdown-menu、select、tooltip、accordion、command 等数十个组件),由 packages/ui/src/index.ts 统一导出;
  • 通过 layout 中的ThemeProvider保持暗色主题默认(forcedTheme="dark")。

国际化(i18n)

  • 使用next-intl,Provider 位于 apps/web/client/src/app/layout.tsx(NextIntlClientProvider包裹全部子内容),并在 apps/web/client/next.config.ts 中通过createNextIntlPlugin接入;
  • 文案字符串存放在apps/web/client/messages/*,目前包含en.jsones.jsonja.jsonko.jsonzh.json五种语言及类型声明 en.d.json.ts;
  • 新增/修改文案应在 message 文件中进行,避免在代码中硬编码面向用户的文本
  • 保持 key 稳定,优先新增 key 而非破坏性重命名。

常见陷阱清单(Common Pitfalls)

文档将 Agent 最常触发的错误收敛为一张清单,每条都有明确的规避方式:

陷阱后果规避方式
需要事件/浏览器 API 处缺少use client事件未绑定在功能根节点设置单个边界即可
新增 tRPC router 未在root.ts导出端点不可达apps/web/client/src/server/api/root.ts注册
环境变量未在env.ts类型化/暴露运行时/Edge 失败优先env,避免客户端新增process.env读取
客户端组件导入服务端代码打包/运行时错误按环境拆分文件;path仅限既有代码编辑器模块
绕过 i18n 硬编码文案无法多语言使用 message 文件与 hooks
useMemo创建 MobX store引用丢失、数据丢失useState+useRef管理生命周期
路由切换时同步清理 store竞态条件setTimeout(..., 0)延迟异步清理

Agent 上下文纪律(Context Discipline)

为了在有限 token 下保持高效,Agent 应:

  • 用 ripgrep 进行窄范围搜索,只打开需要的文件;
  • 只读小段内容,避免读取node_modules.next、大体积资源文件;
  • 提出符合既有约定(conventions)的最小 diff,避免大面积重构(wide refactors)。

这与指南开篇"small, correct diffs, token-efficient"的目标形成闭环。

常用命令速查(Notes)

文档末尾给出的命令是仓库中 Agent 日常操作的基准,结合根 package.json 可得到完整对照:

命令作用根脚本对应
bun test运行单元测试bun --filter '*' test
bun run typecheck类型检查(针对 web-client)bun --filter @onlook/web-client typecheck
bun run db:push将数据库变更应用到本地开发库cd packages/db && bun db:push
bun run db:seed填充种子数据cd packages/db && bun db:seed
bun run db:migrate执行迁移cd packages/db && bun db:migrate
bun run lint全仓 lintbun --filter '*' lint

两条明确禁令需要特别注意:

  • 禁止在自动化上下文中运行本地开发服务器(bun dev类命令);
  • 禁止运行bun run db:gen——该命令保留给维护者(maintainer),Agent 不应触碰数据库 schema 生成流程。

此外文档还提示:除非必要,不要使用额外类型("DO NOT use any type unless necessary"),以保持 diff 的克制与类型面的稳定。

总结:如何让 Agent 在 Onlook 仓库中安全高效地工作

综合全文,在 Onlook 仓库中工作的 Agent 应遵循一条主线:先理解架构边界(App Router 的 RSC/客户端划分、tRPC 的手动注册机制、Supabase 双客户端模型、env 的集中校验),再以最小 diff 落地改动。所有硬性规则都可以在源码中找到落点——root.ts是路由注册的强制检查点,env.ts是环境变量的唯一事实来源,layout.tsx是 Provider 与样式/主题/i18n 的装配现场,trpc.ts是过程(procedure)与权限语义的定义处,engine.ts则是 MobX store 生命周期的范本。遵循本文所梳理的约束与命令,Agent 既能保持代码风格与既有架构一致,也能避免最常见的运行时与边界错误。

【免费下载链接】onlookThe Cursor for Designers • An Open-Source AI-First Design tool • Visually build, style, and edit your React App with AI项目地址: https://gitcode.com/GitHub_Trending/on/onlook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询