Cursor BYOK 前端
在 apps/desktop 中已有的组件和主题系统基础上构建。保留 Tauri HTTP 边界;前端管理功能调用 /__byok-api__/api,绝不添加 IPC 业务 API。
桌面 HTTP 通信
- 将 Rust 管理服务器视为桌面 UI 的单一 HTTP 源。主 WebView 和浏览器打开的详情页面必须使用
http://127.0.0.1:<dynamic-port>/__byok-api__/;不要从tauri://localhost加载主 UI,也不要暴露第二个前端源。 - 使每个前端管理请求相对于
/__byok-api__/api。不要发现、注入、持久化或传递apiOrigin,也不要从固定端口构建管理 URL。 - 保留
/__byok-api__/命名空间专用于此边界:/__byok-api__/api/*由 Rust 管理 API 本地处理。- 其他
/__byok-api__/*路径是前端文档、资源、模块和开发资源。
- 在生产环境中,通过 Rust 服务器提供由 Tauri 的
frontendDist嵌入的前端。重用 Tauri 的资源解析器,而不是打包或复制第二套前端资源。 - 在开发环境中,让 Rust 服务器将非 API 的
/__byok-api__/*请求反向代理到 Vite。保留请求路径和查询字符串。不要配置 Vite 将管理 API 请求代理回 Rust。 - 将 Vite 的基本路径配置为
/__byok-api__/,以便生成的资源、模块导入和开发客户端 URL 保持在保留的命名空间下。 - 将开发 Vite 服务器绑定到与 Rust 代理目标兼容的显式环回地址;不要依赖
localhost解析到相同的 IP 系列。 - 使用其正常的环回 HTTP URL 和哈希路由打开外部详情页面。由于页面和 API 同源,不要添加 Tauri URL 变通方法、API 源查询参数或依赖 CORS 的浏览器流。
- 仅将 Tauri 命令用于原生桌面功能,例如打开终端、托盘集成或操作系统操作。不要为 HTTP 业务 API 创建 IPC 镜像。
组件边界
- 将 Provider、Model、Call、Usage 和持久化设置状态保持在展示组件之外。
- 让组件接收业务快照并分发操作。允许本地 React 状态用于瞬态 UI 行为,例如焦点、打开状态、拖动状态和输入草稿。
- 将页面、布局、图表、表单和可重用控件拆分为专注的组件。不要将整个功能集收集在
App.tsx中。 - 保持
App.tsx作为路由组合根。
页面布局
- 使用
components/layout/PageLayout.tsx构建每个全高页面;不要手动编写带有网格行的页面外壳。 - 将页面级命令和控件(例如创建、导入、导出、筛选、排序和视图选项)放在通过
PageActions共享的页面操作区域中。仅将针对一个具体列表项的操作(例如编辑和删除)保留在该项的行内。不要在页面内容内添加重复的操作栏或面板标题控件。 - 绝不要在数据表格或平面数据列表上方渲染标题区域。使用页面标题提供上下文,将列标签保留在表头中,将页面级控件放在
PageActions中,并将表格包装在无标题的Card中。在分组的父子视图中标识父数据的分层标签不是表格标题。 - 保持主滚动视口全高且
position: relative;将页面标题和操作区域绝对定位在其上方。 - 使用滚动内容的
padding-top保留绝对标题/操作区域,绝不能通过缩短或偏移滚动视口本身来实现。 - 保持菜单 Card 固定,并使用内容区域的
margin-left保留它;不要将 Header、菜单和内容放在流式网格中。 - 不要使用子节或节内边距来创建外部间距。使用兄弟元素之间的 flex/grid 间距以及容器边界处的边距或固定偏移量;滚动内容顶部内边距仅为其绝对定位的标题/操作叠加层保留。
- 使用具有以下不变量的垂直弹性布局:
- Header:
flex: 0 0 auto。 - Footer:
flex: 0 0 auto。 - Content:
flex: 1 1 auto、min-height: 0和overflow: hidden。
- Header:
- 保持 Header 和 Footer 在滚动视口之外。它们绝不能随内容增长或收缩。
- 仅对可能进入原生圆角的边缘 chrome 应用 macOS、Windows 和 Linux 窗口角安全区变量。
- 保持 macOS 交通灯空间和顶部固定拖拽条无交互控件。
- 避免使用装饰性品牌文本和说明性文字,除非它们能帮助用户执行操作或解释状态。
排版
- 以 SCSS 编写组件样式,并从
src/styles/_typography.scss获取每个font-size;绝不要在 SCSS、React 样式或图表选项中硬编码字号。 - 对普通 UI 文本和普通标题(包括标题栏、面板、卡片和节标题)使用
base。不要仅仅因为元素是h1或h2就增大字号。 - 仅对辅助信息使用
xs。保留lg用于显式 oversized 显示标题;不要将其用于常规标题。 - 更改标度后审计所有排版令牌的用法,而不仅仅是原始数字字号。
滚动和虚拟化
- 对垂直滚动页面内容使用
components/layout/VirtualPage.tsx。 - 对可增长的数据集合和菜单使用
components/virtual/VirtualList.tsx。 - 将
ScrollArea视为由虚拟滚动实现拥有的低级原语。不要在页面或布局中直接导入ScrollArea。 - 使用全局的
scroll-shadow-top和scroll-shadow-bottom蒙版实现滚动边缘阴影。当滚动内容嵌入保留绝对叠加层时,从与其padding-top相同的 CSS 变量获取顶部阴影距离。 - 将非列表页面建模为一系列可测量的顶级虚拟节。
- 不要使用文档滚动、原生页面
overflow: auto或嵌套的垂直滚动容器。 - 确保虚拟视口的每个 flex/grid 祖先都具有
min-height: 0,并且视口具有明确的有界高度。
路由和页面
- 使用
HashRouter以实现 Tauri 兼容性。 - 保持
/作为统计仪表板,并通过共享的AppLayout中的持久菜单暴露每个主页面。 - 对主页面使用顶级路由:
/、/providers、/models、/calls和/settings。 - 不要创建单独的 Home 和 Settings 外壳,也不要添加 Home 切换控件。
- 保持路由内容独立于窗口 Header 和 Footer。
图表
- 通过
components/charts/EChart.tsx使用 ECharts。 - 仅注册所需的 ECharts Core 图表、组件和渲染器。
- 保持图表组件声明式:接受领域数据并构建选项,而不获取或持久化数据。
- 让
ResizeObserver根据其布局容器调整图表大小,并在卸载时销毁实例。
验证
从 apps/desktop 运行:
npm run check
npm run tauri:build -- --debug --no-bundle
验证 Header 和 Footer 保持固定,只有虚拟内容视口滚动,设置导航在最小窗口尺寸下仍然可访问,并且 macOS 控件保持在圆角和交通灯非安全区域之外。