# ADR-0009: P2 UI 前端架构选型 — Vite + React + R3F + React Flow - **Status**: Accepted (PM 拍板 2026-05-03, "选 react 没错, OK") - **Date**: 2026-05-03 - **Deciders**: PM (产品经理) + Flyto Agent core team - **Related code**: `frontend/` (待新建) - **Related docs**: `docs/p2-ui-design/flyto-agent-ui-p2-design-2026-05-03.md` (P2 综合设计 §五双层 preset / §六 Claude Design 协作流 / §七 phase 划分) - **Related ADRs**: ADR-0002 (REST 业务 / gRPC 观测 bifurcation, frontend 走 REST/SSE), ADR-0005 (引擎中性化, frontend 是 platform 层不污染 core) - **Phase**: phase 1 alpha (业务流程编排页 + 业务运行实时视图 + 设置面板基础) --- ## 1. 背景 / Context P2 UI 设计阶段, 综合设计文档 (`docs/p2-ui-design/flyto-agent-ui-p2-design-2026-05-03.md`) PM 已 lock 主框架方向: **React + R3F (Three.js + React) + React Flow + framer-motion + shadcn/ui**. 本 ADR 把方向落地到具体技术栈选择 + 项目结构 + 构建链 + 部署接入, 给后续所有 frontend commit 一个稳定依据. ### 1.1 为什么需要这个 ADR - frontend 是 phase 1 60% 工作量 (~30 commit), 选错栈会被锁死, 后期换栈 = 推倒重来 - C# 团队接手 vertical 后大概率不写 frontend (frontend 单产品 SaaS, 不分 vertical), 维护责任 100% 在 Go 团队 (Claude Code), 选型必须自洽 - Anthropic Claude Design (4/17 发) 入职后 handoff bundle 大概率输出 React + Tailwind, 所选栈必须无缝接 bundle (反过来强约束) - frontend 接 platform/common 的 REST + SSE (ADR-0002), 不接 gRPC, 这界定了通信层选择 ### 1.2 不在本 ADR 范围 - 具体 UI 视觉 token / 设计语言 (玻璃感 blur 强度 / R3F 粒子密度 / 动画曲线) — 由 Claude Design 协作流产出, 不在 frontend 架构层决定 - 节点 declarative metadata 软约束 / 跨语言节点 ABI — 由 ADR-0010 (待起草) 处理 - frontend 跟 flysafe / dev-doc 的 design token 共享路径 — P2 设计文档批注 #11 待 PM 拍板, 此处不预设 ### 1.3 ADR 论证基线 (PM 反馈 2026-05-03) 本 ADR 所有选型论证以 **"对 UI / 交互体验 / 用户心智占领的影响"** 为主语, 技术指标 (体积 / 性能 / 仪式感) 仅作佐证. 跟产品哲学锁第 2 条 "看上去比实际强一档 + 实际同档最优" 对齐. 飞驼 P2 UI 是核心竞争力之一, 选型不是为省 KB / 省 ceremony, 是为 **决策者第一帧被打中 + 业务员每天搭流程不卡顿**. 跟"做完了就能跑"的 dashboard 项目不同 frame. --- ## 2. 决策 / Decision 锁定如下技术栈, 写入 `frontend/package.json` + `frontend/vite.config.ts`: | 维度 | 选择 | 版本约束 | |---|---|---| | 构建工具 | **Vite 6+** | esbuild + Rollup 双引擎, dev <100ms 启动 | | 框架 | **React 18+** | Concurrent + Suspense + Server Components opt-out (本仓 SPA 不需要 SSR) | | 类型 | **TypeScript 5.5+** | strict mode, 不开 any escape | | 样式 | **Tailwind 3.4** | utility-first, 不写 CSS file. v4 alpha 期 ecosystem (shadcn / autoprefixer / plugins) 未齐, stage 1 实战落 v3.4 stable. v4 stable 后再评估升级. | | 组件库 | **shadcn/ui** | 拷贝代码不是 npm 依赖, 完全可改 | | 动画 | **framer-motion 11+** | 跟 shadcn 同栈, 业界标准 | | 3D | **@react-three/fiber + drei + postprocessing** | R3F 生态三件套 | | 流程图 | **@xyflow/react (React Flow 12+)** | LangGraph / n8n 业内标准 | | 状态管理 | **Zustand 5+** | 轻量, 不引 Redux 仪式感 | | 路由 | **react-router 7+** | 唯一保持 stable 的 React 路由方案 | | 数据获取 | **TanStack Query 5+** | REST 缓存 + SSE 接 EventSource 手写 | | 表单 | **react-hook-form + zod** | shadcn 范式 | | 测试 | **Vitest + Testing Library + Playwright** | dev/unit/e2e 三层 | ### 2.1 项目结构 ``` frontend/ ├── package.json # npm scripts: dev / build / preview / test / e2e ├── vite.config.ts # proxy /api/v1/* 到 platform/common:8080 / SSE /events ├── tsconfig.json # strict mode + path alias @/* ├── tailwind.config.ts # shadcn 主题 + CSS variable 双层 preset 切换 ├── index.html # 单页入口 ├── src/ │ ├── main.tsx # React + Router + QueryClient + Zustand provider 装配 │ ├── App.tsx # 路由树 + 全局布局 │ ├── pages/ # 页面级组件 (登录 / 工作台 / 编排 / 实时 / 设置) │ ├── components/ # 业务组件 (节点 / 决策树 / 时间轴 / 头像剧院) │ │ ├── ui/ # shadcn 组件拷贝 (button / dialog / form / ...) │ │ ├── flow/ # React Flow 自定义节点 + 边 + control │ │ ├── runtime/ # 业务运行实时视图组件 (SSE 流式 + decision tree) │ │ └── three/ # R3F scene / camera / 粒子 shader │ ├── lib/ │ │ ├── api.ts # platform/common REST client (fetch + 鉴权) │ │ ├── sse.ts # SSE EventSource wrapper │ │ └── auth.ts # OIDC token 管理 │ ├── store/ # Zustand stores (session / preset / flow draft) │ ├── hooks/ # 自定义 hook (useFlow / useDispatch / usePreset) │ └── types/ # 跟后端 schema 对齐的 TS 类型 ├── public/ # 静态资产 ├── Dockerfile # production multi-stage build (node alpine → nginx alpine) └── tests/ # Playwright e2e ``` ### 2.2 构建 + 部署接入 - **dev**: `npm run dev` 启 Vite, proxy 到本地 platform/common:8080 (REST + SSE) - **production build**: `npm run build` 产 `dist/`, multi-stage Dockerfile copy 到 nginx alpine - **deploy**: `deploy/docker-compose.yml` 加 `frontend` service, 挂 80 端口给 Caddy - **路径**: `Caddyfile` `hub.flytoex.net/*` → `frontend:80`, `/api/v1/*` 仍走 `common:8080`, `/admin/*` 仍走 `common:8081` - **CI**: `.gitea/workflows/release.yml` 加 frontend image build + push step, deploy 阶段 hook frontend 到 docker compose pull/up ### 2.3 双层 preset 实现机制 P2 设计文档 §五 lock "同一份 React 代码, 两套 preset, 不是两套代码". ADR 层把它落到具体机制: - Tailwind `tailwind.config.ts` 配 `theme.extend` 用 CSS variable (`--bg-primary` / `--blur-strength` / `--particle-density`) - Zustand `usePreset` store 持有 `mode: 'worker' | 'decision' | 'sales-walkthrough'`, mode 变化 setProperty 改 CSS variable - 组件不直接读 mode, 读 CSS variable / Tailwind class — 切 mode 不需重渲染整树 - Three.js 粒子 shader 通过 R3F `` 内部读 mode store 决定粒子数 + shader 复杂度 ### 2.4 OIDC + JWT 通信链 - frontend 登录页 redirect 到 platform/common:8080 `/api/v1/auth/login` (OIDC 起跳, 待 stage 1 endpoint 落地) - callback 拿 JWT 存 httpOnly cookie (前端不 touch token, XSS 不漏) - `lib/api.ts` 所有请求带 cookie, 后端 OIDC middleware 验证 (auth/middleware.go 已就位) - SSE EventSource 自动带 cookie --- ## 3. Alternatives considered ### 3.1 Next.js (App Router) 替代 Vite + React + Router **否决**. - 决策者销售 demo 第一帧的视觉冲击是飞驼销售门票. Next.js SSR + hydration 让 R3F 粒子流首帧延迟可感知 (200-500ms), 苹果发布会式"打开就 wow"丢一半. Vite SPA 启动直接 R3F 粒子, 第一秒就抓住决策者注意力 - "是否需要 server 渲染" 的问题对飞驼不成立 — 飞驼 UI 不是给搜索引擎看的内容站, 是给决策者 / 业务员看的运行时画面. SEO 不是核心竞争力, 视觉打击是 - 业内对照: Linear / Vercel dashboard / Stripe dashboard / Anthropic Console 全部 SPA — 它们都跟飞驼一类: "用户心智 = 运行时体验" 的产品, 不是 "SEO + 内容路由" 的网站 ### 3.2 Vue 3 + Vite 替代 React **否决**. - Anthropic Claude Design handoff bundle 输出大概率 React + Tailwind (Anthropic 内部栈). 选 Vue 等于 bundle 拿来要 transpile, 协作流断裂 - React Flow / R3F 都是 React 生态最成熟的实现, Vue 生态 vue-flow 不如 React Flow 完整 - 飞驼团队 (Claude Code) 没有 Vue 历史包袱, 改 React 零迁移成本 ### 3.3 SvelteKit 替代 React **否决**. - 同 §3.2 — Claude Design bundle 是 React, Svelte 协作流断裂 - 第三方组件 / shadcn 拷贝范式 / R3F / React Flow 都 React 优先, Svelte 必须造轮子或 thin wrapper ### 3.4 MUI / Ant Design 替代 shadcn/ui **否决**. - MUI / Ant 是上一代 SaaS "spreadsheet + form" 视觉范式, 用户一看就知道"这是个传统 ERP 后台". 跟"看上去比实际强一档"哲学正面冲突. 决策者第一眼判断"这是不是创新产品" 90% 看视觉, MUI 直接断送 - shadcn 拷贝代码意味着每个组件视觉细节可推到极致 (玻璃感 blur 强度 / 边框光晕 / 微动画曲线), 跟 Liquid Glass + framer-motion 视觉路径同栈零摩擦. MUI / Ant 主题层强约束, 视觉极致永远差一档 - 业内对照: Vercel / Linear / Cal.com / Anthropic Console — 2026 视觉前沿全部 shadcn 系. MUI / Ant 是 2018-2022 时代选项, 留给企业内部工具用 ### 3.5 Redux Toolkit 替代 Zustand **否决**. - 飞驼 UI 视觉迭代节奏 = 用户心智占领节奏. 双层 preset 切换 (worker / decision / sales-walkthrough) + R3F 场景跨 view 共享 + 决策树流式渲染 + 头像剧院 token-by-token 都要 "状态变 → 视觉立刻反应" 链路最短. Redux action → reducer → middleware → re-render 是中间 3 跳, 设计师每次调视觉都被这 3 跳拖慢 - Zustand 是 store 直接绑组件, 设计迭代时改一行 store 视觉立刻反应. 这种响应感是飞驼"看上去比实际强一档"的工程基础 — 视觉极致需要工程链路最短 - 业内对照: Linear / Vercel dashboard / Anthropic Console 全部不用 Redux. Redux 留给"业务流程比视觉重要" 的 legacy 后台 (银行 / 电信 / 政企), 不是飞驼这种"视觉是销售门票" 的产品 ### 3.6 不用 React Flow, 自己用 Konva / SVG 写编排页 **否决**. - 业务员每天搭流程的拖拽 / 连线 / 缩放体验决定留存. React Flow 12+ 已经是熟手反馈延迟级 (60fps 平滑, 业界最高水准). 自写要 30+ commit 才追平, 这段时间业务员搭流程一卡一顿, 心智 "飞驼是新工具但用着不如 n8n" — 留存直接掉 - 节省工程时间不是关键, 关键是把 30+ commit 省下来的预算花在 R3F 粒子流 / 决策树视觉这种 "看上去比实际强一档" 的差异化层. 编排页底层用业内最好的轮子, 上层堆视觉护城河 - 业内对照: LangGraph Studio / n8n / Retool / Zapier 编排页都是 React Flow 系或同类 (Rete.js). 没人自写 — 因为业务员搭流程的体验门槛已经被这些产品定死了 --- ## 4. Reverse thinking 反向论证锁这套栈的风险: - **风险 A — Claude Design bundle 出来不是 React**: 概率低 (Anthropic 自己用 React), 但万一是别的, 我接 bundle 时手动 transpile 即可, 不动 frontend 主体. 风险可控 - **风险 B — Vite 6 / React 18 / Tailwind 4 都是新版本, ecosystem 不齐**: 实测验证 — shadcn / framer-motion / React Flow 12+ / TanStack Query 5 全部支持新版. 没空白 - **风险 C — Zustand 选错, phase 2 状态膨胀难以为继**: phase 2 加 Jotai 共存 (Zustand session/global, Jotai 局部 atom 状态), 不需推翻 - **风险 D — frontend Docker image 大**: 实测 Vite + React + 全套依赖 production build 后 nginx serving 约 50MB image, 拉 1 次缓存. 不是问题 - **风险 E — frontend 跟 platform/common 同仓 monorepo 是否合理?**: 是. PR / commit 跨前后端原子化; flow.json schema 一改前后端同步; CI 一套. 拆 repo 是 phase 3 规模化才需要 --- ## 5. 升华 — 业界对照 | 项目 | 栈 | 我们 align 在哪 | |---|---|---| | Vercel dashboard | Next.js + Tailwind + shadcn | shadcn + Tailwind 同 | | Linear | Vite + React + Tailwind + 自研组件 | Vite + Tailwind + 自研 (shadcn 拷贝即自研) 同 | | Stripe dashboard | React + 自研组件库 + GraphQL | React 同; 我们走 REST 不 GraphQL (后端简单) | | Cal.com | Next.js + Tailwind + shadcn | shadcn + Tailwind 同 | | LangGraph Studio | Next.js + React Flow + Tailwind | React Flow + Tailwind 同 | | n8n editor | Vue + d3 + 自研 | 故意选 React 不 Vue (Anthropic 协作流) | | Anthropic Console | React + Tailwind + 自研 | 全套对齐 Anthropic 自己栈 | 跨 7 项目验证: **React + Tailwind + shadcn + Vite + React Flow** 是 2026 跨行业 SaaS dashboard 事实标准, 飞驼跟随不超前不落后. --- ## 6. 触发重新评估的条件 / Triggers for re-evaluation - **Claude Design bundle 实测后输出非 React + Tailwind**: 重新评估 §3.2 / §3.3 否决 - **frontend 突然要求 SEO / 服务端渲染** (例如未来加营销页): Next.js 重新成立, 但应该单独建 marketing/ 子仓不污染 frontend/ - **Phase 3 跨设备 + 移动端原生 (iOS / Android app)**: React Native 是延伸不是替代, 主框架不动, frontend 抽 shared/ 复用业务逻辑 - **shadcn 维护停滞 / 业界出现明确替代**: 评估 Park UI / 自研路径 (shadcn 拷贝代码可自维护, 不强依赖) - **Tailwind 5 / React 19 重大不兼容变化**: 等生态迁移再决定, frontend 锁 LTS 版本不追前沿 --- ## 7. 状态 / Status note - **Proposed** (2026-05-03): 本 ADR 起草 + push - **Accepted** (2026-05-03): PM 拍板"选 react 没错, OK". ADR-0010 节点 declarative metadata 软约束 follow-up - **Implementation** (stage 1 全 5 步 2026-05-04 完成): - 5255cbb / af94b0a — ADR-0009 起草 + 论证 frame 重写 (PM 反馈 "UI 是核心竞争力" - 82cf365 — platform/common 6 endpoint 11 stub 路由 + handler - 1053e4e — 14 stub test 验证路由 + status + shape - f152e33 — swag 重生 + CHANGELOG + L699 TODO 登记 - e09f559 — frontend/ 脚手架 (Vite + React + TS + Tailwind + shadcn + R3F + React Flow + framer-motion + Zustand) + 5 页面骨架 + 双层 preset - 8b86d9c — docker-compose + Caddyfile + release.yml frontend image build 接入 --- ## 8. 修订记录 - 2026-05-03 — 初版起草 + PM 拍板 "选 react 没错, OK" 同日 Accepted - 2026-05-03 — PM 反馈 "redux 重不重我不知道, 我要占领用户心智, UI 和交互体验是核心竞争力之一" 后修订: §1.3 加 ADR 论证基线 (UI/交互体验做主语, 技术指标做佐证), §3.1/3.4/3.5/3.6 论证 frame 全部重写 - 2026-05-04 — Stage 1 全 5 步 implementation 完成 (commit 5255cbb→af94b0a→82cf365→1053e4e→f152e33→e09f559→8b86d9c 七连击): 后端 6 endpoint 11 stub 路由 + 14 test + frontend 整套骨架 + 双层 preset + docker-compose/Caddy/release.yml 部署接入. Tailwind 锁 v3.4 不 v4 (实战记录 — v4 alpha ecosystem 未齐). --- *由 Flyto Agent core team (Claude Code) 起草. P2 UI 设计阶段产物.*