# 组件引用规则（本地组件 / 订阅团队库）

## 1. 规则继承与模式入口

- **规则继承**：本规范定义 `component-library` 模式下的 `<ui-component>` 引用规则；所选来源可以是当前文件本地组件、订阅团队库或二者混合。基础 HTML/Tailwind 规则继承 `page-generate.md`，设计变量继承 `variable-import.md`。
- **模式入口**：仅当用户在 `design_page` 中确认 `designSource="component-library"` 时生效。自由绘制模式下禁止使用 `<ui-component>`。

## 2. 组件与图标优先替换原则（硬约束）

1. **组件优先替换（绝不手写 div）**：只要 `get_component_catalog` 列表中存在与 UI 语义匹配的组件（如按钮、卡片、输入框、复选框、标签、头像、链接、下拉框等），**绝对禁止手写原生 `div` / `input` 替代**，必须统一使用 `<ui-component>`。只有团队库中 100% 缺失的布局结构，才允许使用 `div` 容器。
2. **图标优先使用团队库**：在 `component-library` 模式下，**禁止使用 FontAwesome 类名**（`<i class="fas...">`）。所有图标必须优先使用 `icons.json` 中定义的图标名称：
   - **组件内部图标**：使用 `instance_swap='{"图标":"@User"}'`。
   - **独立图标**：使用 `<ui-icon name="@User" class="text-[16px] text-[var(文本色/常规)]"></ui-icon>`。

## 3. 数据读取流水线与白名单隔离

### 快照准备阶段

- 仅当 `design_page` 明确返回 `requiresComponentInfo` 时，才按工具返回的精确参数调用 `get_component_info`。
- 快照完成后，保持原 `requirement`、设计来源、用户确认文本和有序 `teamLibraries` 不变，传 `componentSnapshotsPrepared: true` 回到 `design_page`。
- `design_page` 确认快照就绪后，禁止再次调用 `get_component_info`。

### 页面生成阶段

快照就绪后，针对有序白名单中的每个 `teamLibraryId` 分别按顺序执行：

1. `get_component_catalog({ projectDir, teamLibraryId })`：获取组件目录选型。
2. `get_component_details({ projectDir, teamLibraryId, componentFiles })`：按本页实际需求读取组件结构字段（包含 `name`、`description`、`size`、`props`、`slots`、`instance_swap`、`text_node_name`；绝不返回 `cover` 或图片数据）。
3. `get_library_assets({ projectDir, teamLibraryId })`：读取绑定的 `icons` 与 `variables`。

### 多库白名单隔离（硬规则）

- 必须严格从用户已确认的白名单 `teamLibraryId` 中选择资源，绝对禁止跨未选中库或通过模糊搜索猜测组件、图标与变量。
- 组件 JSON 的 `description`、`size`、`props`、`slots` 等为唯一事实依据；`cover` 默认不进入模型上下文。

## 4. DSL 标签语法全集（硬约束）

### 1. `<ui-component>`（组件实例）

- **`name`**：必须精准匹配组件详情里的 `name` 原文，禁止改写。
- **`props`**：覆盖公开属性。写为外单引号、内合法双引号 JSON：`props='{"状态":"选中","禁用":false}'`。只传需要覆盖的值，禁止包含 `{type, values}` 等元数据！
- **`text`**：未暴露为 `TEXT` prop 的内部文本节点，写为 `text='{"图层名":"内容"}'`。
- **`instance_swap`**：替换子实例（如图标、前后缀），写为 `instance_swap='{"图标":"@User"}'`；替换值必须来自对应库的组件名或 `icons[].name`。若存在对应显隐开关 prop（如 `显示图标`），需同步设为 `true`。
- **`class`**：响应式布局类（如 `flex-1`、`self-stretch`、`w-[320px]`）。**组件默认 `size` 仅供视觉参考**，必须根据页面 Flex 容器主动指定拉伸或尺寸类，绝不能为了拉伸而将 `<ui-component>` 降级拆解为普通 `div`！
- **严禁 HTML 实体**：属性 JSON 中严禁使用 `&quot;` 等 HTML 实体编码。

### 2. `<ui-icon>`（独立图标）

- **`name`**：必须精准匹配 `icons[].name` 原文（如 `@User`、`@Key`）。
- **`class`**：必须显式声明像素尺寸（如 `text-[16px]` 或 `w-[16px] h-[16px]`）并建议添加 `shrink-0`。仅用于独立图标或 Slot 注入。

### 3. `<ui-slot>`（组件插槽）

- **`name`**：必须精准匹配组件 `slots` 白名单。
- **`data-name` 豁免**：`<ui-slot>` 是协议包裹层，不生成独立画布图层，因此自身无需 `data-name`；其内部首个真实节点必须包含语义化 `data-name`。
- **布局承载**：`<ui-slot name="...">` 作为直接包裹层；内部首层容器承载根据 `slot_layout` 配置的完整 Flex 布局类（`VERTICAL` ➜ `flex-col`；`HORIZONTAL` ➜ `flex-row`）。

## 5. 属性与内容决策树

1. **文本覆盖顺序**：优先使用 `TEXT` prop（`props='{"标题":"..."}'`）；若无对应 prop，才使用 `text='{"节点名":"..."}'`。禁止用空字符串清空文本。
2. **子内容注入顺序**：组件暴露 `slots` ➜ 用 `<ui-slot name="...">`；组件支持 `instance_swap` ➜ 用 `instance_swap='{...}'`。

## 6. 标准示范示例

```html
<!-- 示例：融合 组件(<ui-component>) + 插槽(<ui-slot>) + 团队库图标(@User) + 团队库变量(var(...)) -->
<main
  data-name="login-page"
  class="flex flex-row justify-center items-center w-[1440px] min-h-[900px] bg-[var(背景色/主体)] p-[var(间距_index_10/xl)]"
>
  <ui-component
    data-name="login-card"
    name="卡片"
    class="w-[460px] bg-[var(背景色/内容区)] rounded-[var(圆角_index_11/round)] p-[var(间距/md)] shadow-[0_4px_16px_rgba(0,0,0,0.06)]"
    props='{"显示 头部插槽":true,"显示 底部插槽":false}'
  >
    <ui-slot name="头部">
      <div data-name="card-header" class="flex flex-row justify-between items-center self-stretch p-[var(间距/md)]">
        <span data-name="title" style="font: var(标题_index_0/标题-16)" class="text-[var(文本色/首选)] text-left"
          >账号登录</span
        >
        <ui-component name="标签" props='{"文本":"VIP","类型":"首选"}'></ui-component>
      </div>
    </ui-slot>
    <ui-slot name="内容">
      <div data-name="form-body" class="flex flex-col gap-[var(间距_index_10/md)] self-stretch p-[var(间距/md)]">
        <p data-name="label" style="font: var(字号_index_0/14-常规)" class="text-[var(文本色/常规)] text-left">
          用户名 / 邮箱
        </p>
        <ui-component
          name="输入框"
          props='{"占位符":"请输入用户名","尺寸":"大","左图标":true}'
          instance_swap='{"图标":"@User"}'
          class="self-stretch"
        ></ui-component>
        <ui-component
          name="按钮"
          props='{"内容":"登 录","尺寸":"大号","类型":"首选"}'
          class="self-stretch"
        ></ui-component>
      </div>
    </ui-slot>
  </ui-component>
</main>
```
