# TFDS 页面布局 Recipe（AI 先选 recipe，再写 JSX）

> **作用域**：把页面模板、容器语义、间距、对齐和滚动模型压缩成 AI 可执行的布局决策表。生成整页时，先从本文件选择 **1 个主 recipe**，再读取 `components.index.json` 和 `LAYOUT_RULES.md` 写具体组件。
>
> **优先级**：组件索引 > `SKILL.md` > 本文件 > `LAYOUT_RULES.md` > `GLOBAL_DESIGN_RULES.md` > `DESIGN_PRINCIPLES.md`。本文件负责“先选哪种布局语义”，`LAYOUT_RULES.md` 负责更完整的骨架和响应式细节。

---

## 0. 生成前硬门禁

> **统一起跑顺序（不可跳读、不可乱序）**：写任何整页 / 模板 / 子区前，按下面顺序读完三份文档，缺一不可：
>
> 1. **`PAGE_ARCHETYPES.md`** → 先确定“这是哪一类页面 / 该选哪个模板”（决策树 + 触发关键词）。
> 2. **`LAYOUT_RECIPES.md`（本文件）** → 拿到模板后，选 **1 个主 recipe**，确认容器语义、间距、对齐、滚动模型。
> 3. **`LAYOUT_RULES.md`** → 写 JSX 时回填骨架、双栏 / 多栏、宽度策略、白卡嵌套、自适应等结构细节。
>
> ⛔ 禁止：跳过模板选型直接套通用白卡 / 灰底；跳过本文件直接照搬骨架；本文件已锁定的容器语义，不允许被 `LAYOUT_RULES.md` 反向覆盖。

每个整页文件生成前，先回答 5 个问题，并只选一个主 recipe：

1. 用户进入页面后的第一步是什么？
2. 页面主角是对象列表、AI 输入入口、消息流、主产物/编辑区，还是真人会话？
3. 最外层内容应该直接坐浅灰底，还是进入白色工作卡？
4. 谁负责滚动：整页、工作卡正文、表格、消息流，还是卡片网格？
5. 是否已命中现有页面模板？若命中，是否直接 import 模板或完整复制模板源码，而不是拆模板重拼？

命中页面模板时，recipe 只用于确认容器语义和滚动模型，不允许覆盖模板源码里的外层容器、间距、滚动层、拖拽/折叠/吸底/上下文同步能力。

建议在草稿阶段保留一段 AI 自检注释，交付前可删除：

```jsx
// TFDS recipe: chat-home
// container: gray-direct
// scrollOwner: template-grid
// cardColor: template cards white
```

---

## 1. 六类页面主 Recipe

| Recipe id | 触发场景 | 首选模板 | 背景 / 容器 | 典型组件 | 禁止项 |
| --- | --- | --- | --- | --- | --- |
| `base-management` | 筛选、搜索、浏览一批结构化对象、批量操作、查看详情 | `BasePageFramePattern` + 列表示例 | 基础页面框架专用间距：普通入口 `PAGE_FRAME_CONTENT_MARGIN = 16px 16px 16px 0`，页面示例3 `PAGE_FRAME_CONTENT_WITH_TOPBAR_MARGIN = 0 16px 16px 0`；横向并列分栏内部用 `PAGE_FRAME_PANEL_GAP = 8px` | `NavBar`、`FormTitle`、`FilterBar` 组合、`Table`、`TagBar`、`Tag` | 把页面 header 包白卡；给 NavBar 右侧额外加左 padding；`main p-2/px-6/px-8`；`max-w-* mx-auto`；纵向板块叠放误用 `gap-2` |
| `chat-home` | 用户尚未进入上下文，第一步是输入需求 / 搜索助手 / 从模板开始 | `ChatHomePagePattern` | 右侧内容区直接保持 `var(--color-blueGrey-200)`，Hero / ChatInput / Tabs / 搜索 / 卡片网格直接坐灰底 | `NavBar`、`ChatInput`、`Tabs size="sm"`、`Input`、`Card color="white"` | 整块右侧首页内容外包 `bg-surface rounded-*`；模板卡用 `color="grey"` |
| `chat-conversation` | 已在 AI 任务里持续追问、看执行流和产物 | `ChatConversationPattern` | 浅灰会话工作区，消息流和底部输入各自固定职责 | `ChatMessage`、`ChatInput`、`Button`、产物卡 | 用 `ChatBubble` 手搓 AI 消息；让底部输入随消息滚走 |
| `copilot-workbench` | 对象 / 产物 / 编辑器是主角，AI 只是侧助 | `CopilotPagePattern` | 页面 header 直接坐灰底，主工作区进入白色 `WorkSurface`，AI 面板按辅助区处理 | `FormTitle`、`Tabs`、`ChatInput`、`TextArea/Input/Select`、产物区 | 做成纯聊天页；AI 侧栏抢主工作区宽度；整页滚动破坏对照 |
| `im-thread` | 真人客服、私信、站内信、履约沟通的单线程详情；不需要右侧信息区 | `IMConversationPattern` | 会话背景按 IM 语义处理，线程列表 / 消息流 / 输入固定 | `ConversationList`、`ChatBubble`、`ChatInput variant="im-basic"` | 用 `ChatMessage` 渲染普通 IM；把推荐层塞到消息流和输入框之间；把完整三栏客服 / IM 工作台误判为单线程 IM |
| `customer-service-workspace` | 客服正在处理会话、工单、质检、托管或在线 Agent 工作流；或页面是左侧会话/工单列表 + 中间 IM 聊天 + 右侧信息 Tabs 的三栏 IM 工作台；**工单 Agent 工作台（`TicketAgentWorkspaceFramePattern`）也归入本 recipe（同为工作台容器语义）** | `CustomerServiceWorkspaceFramePattern` / `TicketAgentWorkspaceFramePattern` | 工作台框架自身贴浏览器顶部 / 底部 / 右侧；只保留模板内部 16px 节奏，不再套外层 `main p-4`；左侧可组合 `NavBar platform="bytehi"` 作为 AppShell 左栏；右侧 InfoDisplayPanel 可用 N 个信息 Tabs 兼容业务信息区 | `NavBar platform="bytehi"`、`CustomerServiceWorkspaceFramePattern` / `TicketAgentWorkspaceFramePattern`、`ConversationList`、`IMConversationPattern`、`InfoDisplayPanel`；托管助手 tab 用 `ChatMessage` 用户气泡 + `HiAI` 执行流 + 推荐回复 + 复制/发送按钮 | 外层再套 Card / bg-surface 白卡 / `main p-4` / margin wrapper；在 NavBar 与工作台之间加 gap 或 padding；把 ByteHi 菜单塞进工作台内部；新造三栏框架；托管助手与 IM 气泡文案不一致 |

### 1.1 `base-management` 列表示例选择（4 个页面示例怎么挑）

`BasePageFramePattern` 是骨架，列表内容应直接复用现成的页面示例，而不是手搓 Table + 筛选。按以下语义信号挑示例：

| 你需要的页面信号 | 选哪个示例 | 顶部结构 | 典型组件 | 常见误用 |
| --- | --- | --- | --- | --- |
| 普通结构化对象管理：筛选 + 表格 + 详情入口（机器人列表、变量列表、策略列表、知识库管理等） | **页面示例1**（默认） | NavBar 嵌入页面 header（标题 + 主操作） | `FilterBar`、`Table`、`Tag` 状态列、右侧操作菜单 | 把 header 包白卡；筛选区在表格外侧加 `gap-2` |
| 列表 + 顶部 KPI / 概览统计（数据卡 + 列表） | **页面示例2** | NavBar header + 顶部数据卡组 | `Card` 数据指标 + 下方 `Table` / 长列表 | 数据卡和表格放进同一张大白卡；数据卡用 `color="grey"` |
| 列表上方需要二级页面 Tabs / 业务切换（如分类 Tab + 列表） | **页面示例3** | `PAGE_FRAME_CONTENT_WITH_TOPBAR_MARGIN`（顶部 Tab 已占位） | 顶部页面级 `Tabs size="sm"` + 下方筛选 + 表格 | 顶部 Tab 用 `size="md"`；二级 Tab 又被外层白卡包住 |
| 没有标准表格，是卡片网格 / 实体宫格（应用市场、模板库、机器人广场等） | **页面示例4** | NavBar header + 卡片网格 | `Card color="white"` 网格 + `Tag` + stats，`auto-fit minmax` 网格 | 固定 3/4 列；卡片用 `color="grey"`；外层再套白卡 |

> 选不准时，**默认页面示例1**；不要为了“看起来更高级”用示例2/3/4 凭空加 KPI / Tab / 网格。

---

## 2. 容器语义表（不要只说“灰底 / 白卡”）

| 容器类型 | 视觉定义 | 语义 | 允许子元素 | 禁止子元素 |
| --- | --- | --- | --- | --- |
| `AppShell` | `h-dvh w-full flex overflow-hidden` + `blueGrey-200` | 平台应用壳 | `NavBar`、右侧主内容区 | 白卡描边、整页圆角、`max-w-*` |
| `PageMetaBar` | 直接坐灰底，无白卡背景 | 当前页面位置、标题、返回、页面级切换、主次操作 | `FormTitle variant="form"`、返回按钮、页面级 `Tabs`、操作按钮 | 表格、长表单、消息流、内容列表 |
| `WorkSurface` | `color-surface` 白底 + `radius-lg` + 无 border / shadow | 用户真正做事的主工作区 | 表格、表单、编辑器、对话流、产物预览、参数区 | 页面级 header、面包屑、全局导航 |
| `ContentCard` | 白底或灰底小卡，按父级背景决定 `Card color` | 重复项、模板项、指标项、结果项 | `Card`、`Tag`、少量 stats、入口箭头 | 承载整页主体、嵌套另一张大白卡 |
| `InlinePanel` | 白卡内部的轻灰/弱描边分组 | 辅助分组、配置块、说明块 | 表单字段、参数组、轻提示 | 替代 `WorkSurface` 成为页面主容器 |
| `MessageStream` | `flex-1 min-h-0 overflow-y-auto` | 时间线消息或 AI 执行流 | `ChatMessage` / `ChatBubble` | 表格主列表、筛选栏 |
| `InputDock` | `shrink-0` 固定在工作流起点或底部 | 输入、发送、任务触发 | `ChatInput`、少量快捷按钮 | 随长内容一起滚动 |
| `FloatingLayer` | 弹窗 / 抽屉组件自带白底、阴影和遮罩 | 临时任务、补充编辑 | `Modal`、`Sheet`、`FullScreenPage` | 手写 fixed div 弹窗 |

---

## 3. 间距 Recipe

### 3.1 通用工作区

| 层级 | 固定值 | 写法 | 说明 |
| --- | --- | --- | --- |
| 通用灰底页面到白卡 | 16px | `main p-4` | 非基础页面框架的页面外框间距，禁止 `p-2/px-6/px-8` |
| 基础页面框架普通入口到右侧白卡 | 16 / 16 / 16 / 0 | `PAGE_FRAME_CONTENT_MARGIN` | 左侧 NavBar 与右侧白卡之间不额外加 16px 灰底空隙 |
| 基础页面框架页面示例3到内容白卡 | 0 / 16 / 16 / 0 | `PAGE_FRAME_CONTENT_WITH_TOPBAR_MARGIN` | 顶部 Tab 已占位，不再额外加顶部和左侧间距 |
| 纵向叠放的上下板块 | 16px | `gap-4` | **白卡 ↔ 白卡 / 白卡 ↔ 其他板块组件**（`flex-col` 默认值）；包括左侧标签栏到右侧主内容、上方数据卡片到下方对话流列表等所有纵向并列板块 |
| 横向并列的左右分栏 | 8px | `gap-2` | 仅用于 `flex-row` 同行多白卡分栏 / 多面板 Playground |
| 白卡内部 padding | 24px | `p-6` 或 `px-6 py-4 + px-6 pb-6` | 只用于白卡内部 |
| 白卡内区块间 | 16px / 24px | `gap-4` / `gap-6` | 筛选区到表格默认 16px，较大 section 用 24px |
| 同组控件 | 8px | `gap-2` | 按钮组、Tag 组、图标文字 |
| 跨控件组合 | 12px | `gap-3` | 标题与状态、搜索与主筛选组 |

### 3.2 AI 入口页

| 区域 | 固定节奏 | 说明 |
| --- | --- | --- |
| Hero | `padding: 120px 40px 80px` | 只用于 `chat-home`，直接坐灰底 |
| 主输入宽度 | `maxWidth: 680px` | `ChatInput` 居中，不再外包白卡 |
| 筛选行 | `padding: 16px 40px` + `gap-4` | 左 Tabs、右搜索；空间不足允许换行 |
| 搜索框 | `width: 240px`，`minWidth: 200px` | 固定宽，不撑满整行 |
| 卡片网格 | `padding: 0 40px 40px` + `gap-4` | 网格自己滚动，卡片 `color="white"` |
| 网格列宽 | `repeat(auto-fit, minmax(min(320px, 100%), 1fr))` | 禁止固定 3/4 列 |

### 3.3 会话 / Copilot

| 区域 | 写法 | 说明 |
| --- | --- | --- |
| 消息流 / 产物流 | `flex-1 min-h-0 overflow-y-auto` | 只让长内容滚 |
| 底部输入 | `shrink-0` | 永远固定，不随消息滚 |
| 面板头 | `shrink-0 px-6 py-4` | 标题和右侧操作固定 |
| 面板正文 | `flex-1 min-h-0 overflow-hidden`，内部再分滚动层 | 保证输入、参数、结果可对照 |

---

## 4. 对齐 Recipe

### 4.1 页面 Header / PageMetaBar

- 左侧：`min-w-0 flex-1`，标题用 `<FormTitle variant="form" />`。
- 右侧：`shrink-0 flex flex-wrap items-center justify-end gap-2`，只放 Button、页面级 Tabs、更多菜单。
- 整行：`items-start justify-between gap-3`；空间不足时右侧操作换行，不压缩标题。
- 禁止：`bg-surface rounded-*` 包住页面标题；禁止为了对齐内容卡而给 header 加白底。

### 4.2 白卡 Header

- 白卡主标题用 `<FormTitle variant="level-1" />`；白卡内 section 用 `level-2`。
- 标题旁状态 / 数量 / 身份 Tag 放进 `titleSuffix`，不要放到右侧 action 区。
- 右侧 action 区 `shrink-0 flex flex-wrap justify-end gap-2`。
- 标题容器最小宽度不低于 120px，禁止标题一字一行。

### 4.3 筛选栏 / 工具条

- 搜索框固定约 240px；枚举 / 状态 / 时间控件 160-200px。
- 筛选栏控件默认 36px 高；组内间距 8px，跨组 12px。
- 多个筛选条件必须 `flex-wrap`，不要挤压标题或撑出页面。
- 白卡内筛选栏靠近标题，标题到筛选默认 16px。
- 禁止：在筛选栏把所有输入写成 `w-full`；禁止用 Button/Tag 冒充 Tabs。

### 4.4 卡片网格

- 网格使用 `auto-fit + minmax(min(Npx, 100%), 1fr)`，不要固定列数。
- 模板 / 入口卡片：父级灰底，`Card color="white"`，网格 gap 16px。
- 白卡内部的次级卡片：父级白底，`Card color="grey"` 或轻量 `InlinePanel`。
- 每张卡标题、描述、Tag、stats 自然排布，禁止嵌套卡片。

### 4.5 表格 / 列表

- 白卡内部结构：标题区 `shrink-0` → 筛选区 `shrink-0` → 表格区 `flex-1 min-h-0 overflow-auto`。
- 表格底部分页固定在表格容器内，不漂浮在白卡外。
- 列表项主列文本可省略，但核心状态、错误原因、空状态说明必须完整可读。

---

## 5. AI 反例扫描

生成完成后，按下表逐行自检（症状 → 99% 根因 → 修复）。命中任意一行就必须改；改完重跑这张表。

| 症状（你在代码 / 视觉里看到的） | 99% 根因 | 修复 |
| --- | --- | --- |
| `ChatInput` + 模板 `Card` 网格 + 外层 `bg-surface rounded-*` | 入口页被大白卡包住，违反 `chat-home` 灰底直排 | 拆掉外层白卡；Hero / ChatInput / Tabs / 卡片网格直接坐灰底 |
| `<main className="... p-2 ...">` / `p-6` / `p-8` / `px-6` / `px-8` | 误用白卡内 padding 当外框 | `main p-4`（基础页面框架除外，遵守专用 margin token） |
| 纵向 `flex-col gap-2`（白卡 ↔ 白卡 / 白卡 ↔ 其他板块） | 错把横向分栏的 8px 用到纵向叠放 | 纵向并列板块改 `gap-4`（16px） |
| 左 `aside` 与右主内容 / 上数据卡与下表格之间小于 16px | 上下板块默认 16px 节奏被压缩 | 改 `gap-4`，必要时拆分容器层级 |
| `max-w-* mx-auto` 包住主工作区 | 把 C 端内容居中收窄思路误用到 B 端 | 删除 `max-w-*` 和 `mx-auto`，主工作区充分使用宽度 |
| `Tabs size="md"` 出现在白卡内 / 筛选区 / 卡片内 / Playground | 误把平台顶部一级导航的 size 复用到内容区 | 改 `size="sm"`（仅平台顶部一级导航才用 md） |
| 筛选栏 `Input className="w-full"` | 误用正文表单的“撑满父级”规则到筛选栏 | 筛选栏 Input 固定宽（搜索 ≈ 240px，枚举 / 时间 160–200px） |
| 最外层工作白卡同时有 `border` / `ring` / `shadow` | 把卡片描边规则误用到 WorkSurface | 删除外层描边和阴影，只保留 `radius-lg` |
| `chat-home` 模板卡网格里 `<Card color="grey">` | 父级是灰底，子卡应当是白卡 | 改 `color="white"` |
| 客服 / 工单 Agent 工作台外又包了一层 `bg-surface` / `Card` / `main p-4` | 把工作台框架误当“通用白卡内容” | 工作台框架贴页面边缘，外层不要套白卡 / 不要 `main p-4` |
| 同一页同时用 `ChatBubble` 渲染 AI 任务流 + `ChatMessage` 渲染普通 IM | 角色搞反 | AI 任务会话用 `ChatMessage`；真人 IM 单线程用 `ChatBubble` |
| 卡片网格写死 `grid-cols-3` / `grid-cols-4` | 用固定列硬扛响应式 | 改 `auto-fit minmax(min(320px, 100%), 1fr)` |
