Files
inkreach-agent/docs/plugins/07-ui-components.md
T

84 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# L6 · 公共 UI 组件库(@aso/plugin-sdk/ui)
> 前置:[L4 React 组件](./05-renderer-components.md)。约 15 分钟。
> 目标:不写一行样式代码,做出与宿主完全一致的界面;等待/失败/空态不再自造。
## 为什么必须用它
[DESIGN.md](./DESIGN.md) 定了「长什么样」,组件库把它变成「直接拿来的代码」:
- **token 同源**:组件内部只用宿主已核验的类词汇(`text-ink-3`、`rounded-control`…)
与 `var(--token)` 内联变量,深浅色自动跟随宿主主题
- **三态统一**:等待 `LoadingState` / 失败 `ErrorState`(可重试)/ 空 `EmptyState`,
任何插件的加载体验与宿主一致
- **零依赖**:`./ui` 是 sdk 源码子路径,随 esbuild 一起打进 renderer.mjs,
不引任何第三方 UI 库(shadcn/ui 的思路——无运行时、复制即用——由宿主承担)
## 导入与构建
```tsx
import { Button, LoadingState, ErrorState, EmptyState, SettingRow, Switch } from '@aso/plugin-sdk/ui'
```
构建命令与 L4 完全相同——`ui` 随 sdk 源码一起被打包,react 别名照旧:
```bash
esbuild src/renderer.tsx --bundle --format=esm --outfile=renderer.mjs \
--jsx=automatic --alias:react=./shims/react.js --alias:react/jsx-runtime=./shims/jsx-runtime.js
```
## 组件目录
| 组件 | 用途 | 要点 |
|---|---|---|
| `Spinner` | 行内旋转指示 | `size` 默认 14px |
| `LoadingState` | 区块等待态 | `label` 可换文案,默认「加载中…」 |
| `ErrorState` | 区块失败态 | `title`/`description`(错误消息)、`onRetry` 提供即显示重试按钮 |
| `EmptyState` | 区块空态 | `title` + `description` + `action`(如「去设置」按钮) |
| `Button` | 按钮 | `variant: 'primary' \| 'ghost' \| 'danger'`;`loading` 置灰并显示 Spinner,防重复提交 |
| `Input` / `Textarea` | 输入 | 与宿主设置页同款外观 |
| `Select` | 下拉 | `options: { value, label }[]`,原生 select |
| `Switch` | 开关 | 受控 `checked`/`onCheckedChange`,或非受控 `defaultChecked` |
| `Card` | 卡片容器 | `rounded-card` + line 边框 |
| `SettingRow` | 设置行 | label 左 / 控件右(DESIGN §4 标准模式) |
| `Badge` | 徽标 | `tone: 'neutral' \| 'accent' \| 'danger'`,版本/来源/状态元信息 |
全部组件接受 `className` 追加布局类(flex/间距/尺寸),**不接受**颜色/字号覆写——
那两条路(hex、新字号)被 [L7 检验器](./08-performance.md)拦截。
## 三态铁律
任何异步区块的状态机:**loading → data | error | empty**,四选一,不允许第五种
(白屏、裸文本「加载中」、无反馈的失败)。模板(plugin-template)是完整示范:
```tsx
{error !== null ? (
<ErrorState title="数据加载失败" description={error} onRetry={() => setTick((t) => t + 1)} />
) : items === null ? (
<LoadingState label="正在拉取数据…" />
) : items.length === 0 ? (
<EmptyState title="暂无数据" description="检查数据源后点击重新加载" />
) : (
items.map((it) => <SettingRow key={it} label={it} />)
)}
```
配套铁律:异步调用必须 `withTimeout`/`withRetry`(见 L7),否则 loading 态可能永远挂着。
## 宿主兜底
renderer 模块的读源与 import 宿主各限时 8 秒:超时或失败时,插件槽位自动呈现宿主侧
「插件界面装载失败」ErrorState(带重试按钮);组件渲染期崩溃由 PluginErrorBoundary
兜底。也就是说**装载失败不需要插件自己处理**,插件只需管好自己组件内部的异步。
## 边界
- 仅 renderer 组件槽位可用(渲染在宿主 DOM,复用宿主样式表);iframe 插件页是物理
隔离文档,请按 [DESIGN.md](./DESIGN.md) §4 的 HTML 模式自行实现
- 需要新形态时:先用现有组件拼;确实不够,在 DESIGN.md 约束内组合 token,
并把新形态沉淀回团队公共代码,而不是留在单个插件里
---
**上一篇** [L5 自诊断](./06-self-diagnosis.md) · **下一篇**:[L7 性能与检验](./08-performance.md)——超时、重试与机器关卡。