# Brifdo 公共原型组件语义

本文解释 Agent 应该如何选择稿见公共组件。MCP `get_library_catalog` 会为每个组件返回用途
`purpose`、子节点可用的 `allowedSlots`，以及每个 recipe 的写入 tuple：`recipeId`、根节点
必须使用的 `slot`，可测量组件还带最小宽度 `minWidth`。
`component`/`variant`/`size`/`state`/`composition` 由 `recipeId` 唯一确定，写入时不必重复
发送（发送了也必须一致）；每个任务读取一次目录即可。目录是完整且版本化的，本文提供跨组件
的选择和组合规则。

## 选择原则

1. 先按页面职责组织信息，再选组件。总览、列表、详情、录入、设置、报表、排期和队列不应
   套用同一个页面模板。
2. 标准控件必须使用公共 recipe。不能用普通矩形冒充按钮、输入框、下拉框、卡片或状态。
3. `structuredRenderer=implemented` 表示可以把业务数据交给本地编译器参数化；
   `catalog-clone-required` 只能使用完整已发布配方，不能自行拼装或伪造状态。
4. 一个区域最多一个主操作。重复数据必须对齐行列；标签和控件必须对齐；文案必须完整放入
   组件，不得依赖遮挡或溢出。

## 操作与选择

- **Button / Button Group**：提交、创建、保存、确认、返回等明确动作。主操作用 default，
  次操作用 secondary/outline，危险操作只用 destructive。不要把导航列表或状态文字做成按钮。
- **Toggle / Switch / Checkbox / Radio Group**：布尔设置、多选、单选。Radio 适合少量且互斥的
  选项；选项多时用 Select。控件与 label 必须作为同一 Field 对齐。
- **Dropdown Menu / Context Menu / Command**：低频操作集合或大量可搜索命令。不要替代页面主
  导航，也不要隐藏唯一主操作。

## 表单与输入

- **Field / Label**：所有表单控件的语义容器，承载标签、说明和错误。优先组合 Field，而不是
  分别摆放互不关联的文字和输入框。
- **Input / Textarea**：短文本与长文本。长说明用 Textarea；日期、数字、电话等使用对应专用
  输入能力。错误态必须有可理解的错误信息。
- **Select / Combobox**：有限选项和可搜索选项。少于约五个、需要一眼比较的互斥项优先 Radio。
- **Date Picker / Calendar**：日期与日期范围。报表筛选使用日期筛选组合，不用普通文本模拟。
- **File Upload**：附件或导入任务。必须展示类型、大小、上传状态和失败恢复，不用按钮假装
  已选择文件。

## 数据与内容

- **Card**：一个有明确边界的业务摘要、实体或任务。Metric Card 只用于关键指标，不应让每个
  页面都固定出现 KPI 卡片。卡片标题、描述、内容和操作使用对应 slot。
- **Table / Data Table / Data Grid**：结构一致、需要比较或操作的多行数据。表头、单元格和操作列
  必须对齐；详情信息不应硬塞成一行表格。
- **List / Item**：轻量、可扫描的对象或活动流。对象属性很多或需要列比较时改用 Table。
- **Badge**：短状态、分类或等级。不能把长提示、按钮或整段说明塞进 Badge。
- **Avatar / Icon / Separator**：身份、语义图标和结构分隔。图标必须有明确含义，不能替代文字
  说明中的关键业务信息。

## 导航与结构

- **Sidebar / Navigation Menu / Tabs / Breadcrumb**：分别用于跨模块导航、顶层导航、同一上下文
  的并列视图和层级位置。Tabs 不应承载无关页面；面包屑不能替代主导航。
- **Accordion / Collapsible**：次要详情或高级设置。关键状态、错误和主操作不能默认折叠隐藏。
- **Dialog / Sheet / Drawer**：需要保持当前上下文的短任务或详情。复杂多步骤流程使用独立页面。
- **Scroll Area / Resizable / Aspect Ratio**：仅用于真实容器行为，不用来修补错误的页面尺寸。

## 状态与反馈

- **Alert / Toast**：页面级风险、操作结果和短时通知。状态必须使用对应组件，不能用
  Badge 或普通矩形替代 Alert。
- **Progress / Skeleton / Spinner**：确定进度、内容占位和短时等待。不要同时显示互相矛盾的多个
  加载状态。
- **Empty**：没有数据且提供下一步。它不是错误页，也不能在有内容时占据大块空间。
- **Dialog confirmation**：只用于高风险或不可逆操作。普通保存不应每次弹确认框。

## 业务可视化

- **Chart**：趋势、构成和对比；必须有标题、口径和可读标签。表格更准确时不要强行使用图表。
  Bar/Line/Area Default、Pie Donut、Sparkline Compact、Stat Trend Compact 六个配方接受
  `points: [{id,label,value}]`（2 至 12 个数值，至少一个不为零），渲染器会按这些数值
  在卡片自带的绘图区内重算图形；不给 points 时画的是样例占位图，不能当作数据结论。
  - **负值**（实际－计划、同比增减）：Bar/Line/Area 接受负数。数值刻度会跨过零，绘图区
    里多画一条零轴（`grid.zero`），负柱从零轴向下生长、单系列时用调色板的 danger 色，
    折线/面积跨过零轴。Donut/Sparkline/Stat Trend 没有负值区域，给负数会被拒绝。
  - **多系列对比**（计划 vs 实际、各工序各自一条线）：`points` 只写类目
    `[{id,label}]`，再加 `series: [{id,label,values:[…]}]`（1 至 4 个系列，每个系列的
    `values` 与 points 一一对应、至少一个不为零，label ≤ 16 字）。Bar 默认画分组柱、
    Line/Area 每系列一条线并共用同一数值刻度，卡片内在副标题与绘图区之间画一行图例；
    Donut/Sparkline/Stat Trend 只接受单系列。不要把多组数据压成一条线或一组柱。
  - **堆叠柱**（每日各工序产量构成）：Bar 且 ≥2 个系列时加 `stack: "stack"`，各系列
    按类目叠成一根柱，正值从零轴向上叠、负值向下叠、互不重叠，刻度按柱的正负合计取；
    `stack: "percent"` 把每个类目归一到 100%，刻度以 `%` 标注，只接受非负值。默认
    `"none"` 即分组柱；单系列、Line/Area 不能声明 stack。
  - **数值刻度**：需要在左侧标出数值时加 `valueAxis: {unit?: "万元", ticks?: 3|4|5}`，
    刻度会取整到整数档位并把网格线对齐到刻度值（含负刻度）；不加时保持已发布卡片的无
    数值轴样式。
  - **柱的身份**：画布上每根柱是独立部件，`customData.recipePart` 为
    `bar.<系列id>.<类目id>`（单系列为 `bar.<类目id>`），读取场景时可据此还原系列、
    类目与数值。点击跳转仍以节点为单位（整张图表卡一个 hotspot），没有按柱触发的事件。
- **Gantt / Timeline / Calendar**：排期依赖、事件时间和日程。没有真实时间维度时不要使用。
- **Kanban**：少量稳定状态之间的工作流。仅展示状态统计时使用指标或列表，不创建空看板。

## 编译布局契约（compile intent）

每屏 `sections` 1-6 个、`actions` 最多 6 个，每个 intent 最多 24 屏。section 上限：
metrics 12 项、list 12 项、form 12 个字段、table 2-6 列 1-5 行、gantt 最多 5 行 5 列；
`select` 与 `radio` 的 `options` 最多 7 个。`radio` 恰好 3 个 options 时渲染为横向
单选组，其余数量按下拉 select 呈现（不会因此编译失败）。

**尺寸：宽度固定，高度是下限。** 页面宽度按视口固定（Web 1280、App 402），高度以
800 / 874 起步并随内容向下生长——真实原型页面本来就纵向滚动。放不下的内容不需要拆页，
但超出宽度的组件会被拒绝：App 视口的 table 最多 5 列、gantt 只能 1 列。单个 Frame
高度上限 4000px，超过时才需要拆分页面。

`archetype` 是版式与风格提示，不是语法：任何 section 组合都能编译，按页面真实职责选
最贴近的一项即可。

| archetype            | 适用职责                 |
| -------------------- | ------------------------ |
| overview             | 业务总览与关键态势       |
| record-list、queue   | 记录集合、筛选与待办队列 |
| record-detail        | 单条记录上下文与关联信息 |
| data-entry、settings | 新建、编辑、登记与配置   |
| report               | 指标口径、趋势与明细     |
| schedule             | 排期、时间与依赖关系     |

`actions[].targetScreenId` 只能指向同一 intent 内的屏；跨模块跳转写在计划 acceptance
的 interactions 里。

## 低层写入的 slot 契约（commit_prototype_batch）

- `libraryRecipe.slot` 必填。根节点 slot：card 组件为 `"Card"`，其余组件一律为
  `"root"`。以目录中该 recipe 的 `slot` 字段为准，不要取 `allowedSlots` 第一项。
- 根节点 `instanceId` 等于自身 semanticId；参数化根（card / table / gantt /
  field / date-picker 带 props）text 必须留空，由 props 展开内容。
- 子节点 slot 取自该 recipe 的 `allowedSlots` 列表，`instanceId` 指向所属根节点的
  semanticId，`parentId` 为该根。
- `instanceId` 缺省时按 `parentId ?? semanticId` 推导，节点 `screenId` 缺省时取
  semanticId 的第一段，因此 `read_product_build_context` 返回的节点可直接写回。
- Card 和 Gantt 根不能有 `parentId`（必须是顶层节点）。Button 是单个根矩形，
  文案写在自身 text 上，不能有子节点；放进 Card 时 `parentId` 可指向该 Card，
  但保持自己的 recipe 根身份。
- Card 内的文字必须用 Card slot recipe（instanceId=该 Card），或自成一个已发布
  的文本类 Library 根（如 Label）；不能用无 recipe 的裸文字或矩形冒充组件。

### 错误处理

单个操作的布局问题不算调用失败，逐条列在成功返回的 `rejections[]`
（`message`/`fix`）里；只有整批因布局规则被拒绝报错时才走 `error.details`
（含 `code`/`screenId`/宽高/`suggestedFix`，超量时 `error.truncated=true`）。
`compile_product_module`、`create_product_from_plan` 的布局失败同样走
`error.details`。详见 SKILL.md 的“Error handling”。

## 推荐页面主体

- 总览：少量关键指标 + 趋势/风险 + 今日任务。
- 列表/队列：筛选 + Table/List + 批量/行级操作。
- 详情：对象摘要 + 分组属性 + 历史/关联记录 + 情境操作。
- 录入/设置：分组 Field + 校验 + 单一主提交动作。
- 报表：口径/日期筛选 + 图表或数据表 + 导出。
- 排期：时间轴/Gantt/Calendar + 资源和冲突说明。

完成前必须调用稿见完成门禁；组件数量、自然语言叙述或“看起来完成”都不等于真实完成。
