# 界面 SDK

所有 Nexus 应用共用的一套界面：按钮、表单、卡片、表格、标签页、对话框、Toast，以及以品牌斜杆 `//` 为母题的加载动效（含刻度轮）。外观与 shadcn/ui 一致，取值与 AIDC Console 同源。纯 HTML 应用引一个样式表就能用，需要脚本的部分在 `aidc.js` 的 `ui` 里。每个组件的实物与代码见[组件样例](https://www.ai-dc.ai/developer/ui)。

## 两种用法

| 应用 | 用什么 | 怎么接 |
| --- | --- | --- |
| Nexus 应用（HTML + 原生 JS，没有构建步骤） | 样式表 `ui.css` + `ui` 模块 | `<link rel="stylesheet" href="/developer/sdk/v1/ui.css" />`，清单 `sdk` 登记 `ui` |
| React / Next.js 项目（Tailwind + shadcn） | shadcn/ui 原样 + `@aidc` 注册表 | `npx shadcn@latest add https://www.ai-dc.ai/asset/ui/r/aidc/theme.json`，组件照常 `npx shadcn@latest add button`（见 [AIDC UI](https://www.ai-dc.ai/asset/ui)） |

两边的设计令牌是同一套值、同一套变量名，做出来的界面长得一样。

## 快速开始

```html
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="/developer/sdk/v1/ui.css" />
  </head>
  <body>
    <header class="aidc-appbar"><span class="aidc-appbar__title">瑕疵检测</span></header>
    <main class="aidc-page aidc-stack">
      <div class="aidc-card">
        <div class="aidc-card__header"><h3 class="aidc-card__title">今日良率</h3></div>
        <div class="aidc-card__content">98.4%</div>
      </div>
      <button type="button" class="aidc-btn" id="save">保存</button>
    </main>
    <script type="module">
      import { ui } from "/developer/sdk/v1/aidc.js";
      document.querySelector("#save").onclick = () => ui.toast("已保存", { variant: "success" });
    </script>
  </body>
</html>
```

清单里登记 `"sdk": ["ui", …]`。部署时静态分析会找出页面对 `ui.css` 的引用和对 `ui` 的 import，没登记直接拒收。`aidc app init` 生成的新应用已经接好；已有应用一条命令接入：

```bash
aidc ui add <应用目录> --dry-run   # 先看会改什么
aidc ui add <应用目录>             # 入口页加上 ui.css、清单 sdk 加上 ui；重复运行不会重复改
```

改了清单或入口页，部署前把 `version` 升一级。

## 组件

类都以 `aidc-` 开头：`aidc-btn` 是按钮，`aidc-btn--outline` 是它的变体，`aidc-card__title` 是卡片里的一部分。

| 分组 | 组件 | 类 | 说明 |
| --- | --- | --- | --- |
| 页面 | [顶栏](https://www.ai-dc.ai/developer/ui#appbar) | `aidc-appbar` `aidc-appbar__title` `aidc-appbar__actions` | 应用最上面一条：标识、应用名、右侧操作。吸顶、半透明毛玻璃。 |
| 页面 | [版面](https://www.ai-dc.ai/developer/ui#layout) | `aidc-page` `aidc-page--narrow` `aidc-stack` `aidc-row` `aidc-muted` | 页面容器（最大宽度 + 留白）、竖排 / 横排间距、次要文字。不用 Tailwind 也能排版。 |
| 操作 | [按钮](https://www.ai-dc.ai/developer/ui#button) | `aidc-btn` `aidc-btn--secondary` `aidc-btn--outline` `aidc-btn--ghost` `aidc-btn--destructive` `aidc-btn--link` `aidc-btn--sm` `aidc-btn--lg` `aidc-btn--icon` `aidc-btn--block` | 主按钮是实心黑（signal 主题下是信号橙）；次要、描边、幽灵、危险、链接五种变体，三种尺寸与图标按钮。 |
| 操作 | [标签页](https://www.ai-dc.ai/developer/ui#tabs) | `aidc-tabs` `aidc-tabs__list` `aidc-tabs__trigger` `aidc-tabs__panel` | 同一块区域里切换几组内容。标记写好后调 ui.tabs(容器) 接管点击与方向键。 |
| 操作 | [对话框](https://www.ai-dc.ai/developer/ui#dialog) | `aidc-dialog` `aidc-dialog__header` `aidc-dialog__title` `aidc-dialog__description` `aidc-dialog__footer` | 原生 <dialog>。确认与输入用 ui.confirm / ui.prompt（代替 window.confirm / prompt），复杂内容自己写标记后 showModal()。 |
| 表单 | [输入框](https://www.ai-dc.ai/developer/ui#input) | `aidc-input` | 单行输入；手机上字号 16px（iOS 不会自动放大）。出错时加 aria-invalid="true"。 |
| 表单 | [多行输入](https://www.ai-dc.ai/developer/ui#textarea) | `aidc-textarea` | 多行文本，可纵向拉伸。 |
| 表单 | [下拉选择](https://www.ai-dc.ai/developer/ui#select) | `aidc-select` | 原生 <select>，弹出层由系统负责（手机上是系统选择器）。 |
| 表单 | [复选框](https://www.ai-dc.ai/developer/ui#checkbox) | `aidc-checkbox` | 原生 <input type="checkbox">，外观与 shadcn 一致。 |
| 表单 | [单选框](https://www.ai-dc.ai/developer/ui#radio) | `aidc-radio` | 原生 <input type="radio">。 |
| 表单 | [开关](https://www.ai-dc.ai/developer/ui#switch) | `aidc-switch` | 原生复选框加 role="switch"：开 / 关一项设置。 |
| 表单 | [标签](https://www.ai-dc.ai/developer/ui#label) | `aidc-label` | 表单项的文字标签；包住复选框 / 单选框 / 开关时自动横排对齐。 |
| 表单 | [表单项](https://www.ai-dc.ai/developer/ui#field) | `aidc-field` `aidc-field__description` `aidc-field__error` | 标签 + 控件 + 说明 / 错误提示的一组。 |
| 展示 | [卡片](https://www.ai-dc.ai/developer/ui#card) | `aidc-card` `aidc-card__header` `aidc-card__title` `aidc-card__description` `aidc-card__action` `aidc-card__content` `aidc-card__footer` | 一组相关内容的容器：标题、说明、右上角操作、正文、底部操作。 |
| 展示 | [徽章](https://www.ai-dc.ai/developer/ui#badge) | `aidc-badge` `aidc-badge--secondary` `aidc-badge--outline` `aidc-badge--destructive` `aidc-badge--signal` `aidc-badge--success` | 状态与标签。信号橙只用于需要注意的状态。 |
| 展示 | [表格](https://www.ai-dc.ai/developer/ui#table) | `aidc-table-wrap` `aidc-table` `aidc-table--wrap` `aidc-num` | 数据表：外面包一层 .aidc-table-wrap 就能横向滚动；数字列加 .aidc-num 右对齐、等宽数字。 |
| 展示 | [头像](https://www.ai-dc.ai/developer/ui#avatar) | `aidc-avatar` | 圆形头像；没有图片时放姓氏或缩写。 |
| 展示 | [按键](https://www.ai-dc.ai/developer/ui#kbd) | `aidc-kbd` | 快捷键提示。 |
| 展示 | [分隔线](https://www.ai-dc.ai/developer/ui#separator) | `aidc-separator` `aidc-separator--vertical` | 横向用 <hr>；在横排里用竖向变体。 |
| 展示 | [标识](https://www.ai-dc.ai/developer/ui#logo) | `aidc-logo` `aidc-mark` | AIDC 标识（棱角版双斜杠，logo 橙 #FF4D00）与字标。除了标识本身，界面里不要再用 // 做装饰。 |
| 展示 | [空状态](https://www.ai-dc.ai/developer/ui#empty) | `aidc-empty` `aidc-empty__media` `aidc-empty__title` `aidc-empty__description` | 没有数据时告诉用户为什么、下一步做什么。 |
| 反馈 | [提示条](https://www.ai-dc.ai/developer/ui#alert) | `aidc-alert` `aidc-alert__title` `aidc-alert__description` `aidc-alert--destructive` | 页面里的常驻提示；出错用 destructive。 |
| 反馈 | [Toast](https://www.ai-dc.ai/developer/ui#toast) | `aidc-toaster` `aidc-toast` `aidc-toast__body` `aidc-toast__title` `aidc-toast__description` `aidc-toast__action` | 操作结果的轻提示，几秒后自动消失（悬停暂停）。只用 ui.toast()，不用手写标记。 |
| 反馈 | [加载动效](https://www.ai-dc.ai/developer/ui#loader) | `aidc-loader` | 八种等待态都以品牌斜杆 // 为母题（dial 刻度轮、cadence 节拍、sequence 递进、sweep 掠光、march 行进、pulse 韵律、breathe 呼吸、track 轨道），按场景选、同一界面不混用；大小跟字号，颜色跟 --signal。 |
| 反馈 | [骨架屏](https://www.ai-dc.ai/developer/ui#skeleton) | `aidc-skeleton` | 内容加载前的占位形状。 |
| 反馈 | [进度条](https://www.ai-dc.ai/developer/ui#progress) | `aidc-progress` | 原生 <progress>：有明确进度的等待。 |

## 设计令牌

变量名沿用 shadcn/ui，另有 AIDC 的 `--signal`（信号橙）、`--success`、`--warning`。改外观就在自己的 CSS 里改变量——`ui.css` 全部在 `@layer` 里，应用自己的样式永远优先：

```css
:root { --radius: 0.5rem; }
```

- 明暗：`<html class="dark">`，或 `ui.theme.set({ mode: "dark" })`（`light` / `dark` / `system` 跟随系统）。
- 主题：`aidc`（缺省，主按钮实心黑）、`signal`（主按钮与焦点环换成信号橙）：`ui.theme.set({ theme: "signal" })`。

| 变量 | 浅色 | 深色 |
| --- | --- | --- |
| `--radius` | `0.375rem` | `0.375rem` |
| `--background` | `#ffffff` | `#0a0a0a` |
| `--foreground` | `#171717` | `#ededed` |
| `--card` | `#ffffff` | `#111111` |
| `--card-foreground` | `#171717` | `#ededed` |
| `--popover` | `#ffffff` | `#111111` |
| `--popover-foreground` | `#171717` | `#ededed` |
| `--primary` | `#171717` | `#ededed` |
| `--primary-foreground` | `#ffffff` | `#0a0a0a` |
| `--secondary` | `#f2f2f2` | `#1a1a1a` |
| `--secondary-foreground` | `#171717` | `#ededed` |
| `--muted` | `#f2f2f2` | `#1a1a1a` |
| `--muted-foreground` | `#666666` | `#a1a1a1` |
| `--accent` | `#f2f2f2` | `#1f1f1f` |
| `--accent-foreground` | `#171717` | `#ededed` |
| `--destructive` | `#d8001b` | `#ff4d5e` |
| `--destructive-foreground` | `#ffffff` | `#0a0a0a` |
| `--success` | `#0f7a3d` | `#3fb96b` |
| `--warning` | `#a15c00` | `#e5a44b` |
| `--border` | `rgba(0, 0, 0, 0.09)` | `rgba(255, 255, 255, 0.11)` |
| `--input` | `rgba(0, 0, 0, 0.12)` | `rgba(255, 255, 255, 0.15)` |
| `--ring` | `#006bff` | `#006bff` |
| `--signal` | `#e34400` | `#ff4d00` |
| `--signal-foreground` | `#ffffff` | `#ffffff` |
| `--chart-1` | `#ffc2a3` | `#ffc2a3` |
| `--chart-2` | `#ff8a4c` | `#ff8a4c` |
| `--chart-3` | `#ff4d00` | `#ff6b2e` |
| `--chart-4` | `#e34400` | `#ff4d00` |
| `--chart-5` | `#a83200` | `#e34400` |

## 脚本：ui 模块

```js
import { ui } from "/developer/sdk/v1/aidc.js";

ui.toast("已保存", { variant: "success" });
ui.toast("已删除 1 条记录", { action: { label: "撤销", onClick: undo } });
if (await ui.confirm({ title: "删除这张工单？", description: "删除后不能恢复。", destructive: true })) remove();
const reason = await ui.prompt({ title: "标记问题", label: "问题描述", required: true });   // 取消返回 null
ui.tabs("#orderTabs", { onChange: (panelId) => load(panelId) });
button.prepend(ui.loader("dial"));
header.prepend(ui.logo());
ui.theme.set({ theme: "signal", mode: "system" });
```

| 函数 | 返回 | 说明 |
| --- | --- | --- |
| `ui.toast(title, { description, variant, duration, action })` | `{ dismiss }` | 轻提示，`variant` 为 `default` / `success` / `error`；缺省 4 秒后消失，悬停暂停，最多同时 3 条 |
| `ui.confirm(options \| title)` | `Promise<boolean>` | 确认框（原生 `<dialog>`），代替 `window.confirm`；`destructive` 用危险样式 |
| `ui.prompt(options \| title)` | `Promise<string \| null>` | 输入框，代替 `window.prompt`；`required`、`multiline`、`defaultValue`；回车确认（中文输入法组词时的回车不算） |
| `ui.tabs(容器, { onChange })` | `{ select, value }` | 接管标签页：点击、方向键、Home / End |
| `ui.loader(variant, { label })` | 元素 | 加载动效；`ui.loaderHtml()` 返回标记 |
| `ui.logo({ wordmark })` | 元素 | AIDC 标识 + 字标，`wordmark: false` 只要标识；`ui.logoHtml()` 返回标记 |
| `ui.theme.get()` / `ui.theme.set({ theme, mode })` | `{ theme, mode }` | 主题与明暗（挂在 `<html>` 上） |
| `ui.ensureStyles()` | `<link>` | 页面没写样式表时补上（toast / confirm / prompt 会自动调用；首屏请直接写在 `<head>`） |
| `ui.stylesheetUrl()` | 字符串 | `ui.css` 的地址（与 `aidc.js` 同目录、同一个大版本） |
| `ui.toastHtml(title, options)` / `ui.dialogHtml(options, input)` | 标记 | 与 `toast` / `confirm` / `prompt` 同一份标记的纯函数，在 Node 里也能用（测试、服务端拼页面） |

只用浏览器原生能力（`<dialog>`、`aria-live`、CSS 变量），不引第三方库。

## 加载动效

八种都以品牌斜杆 `//` 为母题，只动 transform 与 opacity；系统开了「减少动态效果」时统一降级为缓慢的呼吸。按场景选，同一界面不混用：

| variant | 名称 | 用在哪 |
| --- | --- | --- |
| `dial` | 刻度轮 | 必须「像 loading」的场合，替代圆环 spinner（缺省） |
| `cadence` | 节拍 | 页面级等待、上传校验 |
| `sequence` | 递进 | 面板数据读取 |
| `sweep` | 掠光 | 对话生成等待 |
| `march` | 行进 | 执行中的条目，数据在流动 |
| `pulse` | 韵律 | 短暂的「思考中」 |
| `breathe` | 呼吸 | 侧栏、预览等伴随性等待 |
| `track` | 轨道 | 部署、发布、导出这类有产出的等待 |

大小跟字号（`font-size`），颜色跟 `--signal`，用 `color` 覆盖。

## 规则

- 按钮、表单、表格、对话框一律用界面 SDK 的类，不要在应用里再写一套；应用自己的 CSS 只写版面与业务特有的东西。
- 主按钮实心黑；信号橙只用于加载、选中与需要注意的状态（`aidc-badge--signal`）。
- 等待态只用 `//` 加载动效，不用圆环 spinner。
- 用 `ui.confirm` / `ui.prompt` / `ui.toast` 代替 `window.confirm` / `prompt` / `alert`。
- 手机优先：输入框在手机上是 16px 字号（iOS 不会自动放大），表格外面包 `aidc-table-wrap` 横向滚动，数字列加 `aidc-num`。
- 字体只用系统字体栈，不从外网加载。

## CLI

| 命令 | 做什么 |
| --- | --- |
| `aidc ui components [组件] [--json]` | 组件、类、分组；带组件 id 时给出示例标记与脚本 |
| `aidc ui tokens [--mode light\|dark] [--theme aidc\|signal] [--css]` | 设计令牌；`--css` 输出变量块 |
| `aidc ui add [目录] [--dry-run]` | 给已有应用接上界面 SDK（幂等） |

这三条都在本机完成，不需要登录。

## 给智能体

- 机器可读的登记表：`https://www.ai-dc.ai/developer/sdk/v1/ui.json`（令牌、全部组件的类与示例标记、`ui` 模块的函数、React 的对应物）。
- 做界面前先查登记表，照示例标记写；样例页：`https://www.ai-dc.ai/developer/ui`。
