@icen.ai/ui 0.5.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +256 -0
- package/README.md +47 -13
- package/dist/base.css +31 -4
- package/dist/behaviors/accordion.d.mts +4 -0
- package/dist/behaviors/accordion.mjs +4 -3
- package/dist/behaviors/ai-chat.d.mts +57 -0
- package/dist/behaviors/ai-chat.mjs +557 -0
- package/dist/behaviors/ai-composer.d.mts +112 -0
- package/dist/behaviors/ai-composer.mjs +2032 -0
- package/dist/behaviors/ai-core.d.mts +151 -0
- package/dist/behaviors/ai-core.mjs +491 -0
- package/dist/behaviors/ai-diff.d.mts +53 -0
- package/dist/behaviors/ai-diff.mjs +282 -0
- package/dist/behaviors/ai-panel.d.mts +98 -0
- package/dist/behaviors/ai-panel.mjs +1012 -0
- package/dist/behaviors/ai-provider.d.mts +198 -0
- package/dist/behaviors/ai-provider.mjs +1055 -0
- package/dist/behaviors/ai-tool.d.mts +18 -0
- package/dist/behaviors/ai-tool.mjs +2125 -0
- package/dist/behaviors/ai-tools.d.mts +81 -0
- package/dist/behaviors/ai-tools.mjs +1658 -0
- package/dist/behaviors/back-top.d.mts +20 -0
- package/dist/behaviors/back-top.mjs +49 -32
- package/dist/behaviors/carousel.d.mts +4 -0
- package/dist/behaviors/carousel.mjs +34 -16
- package/dist/behaviors/charts.d.mts +250 -0
- package/dist/behaviors/charts.mjs +1301 -448
- package/dist/behaviors/command-palette.d.mts +8 -0
- package/dist/behaviors/command-palette.mjs +24 -12
- package/dist/behaviors/context-menu.d.mts +36 -0
- package/dist/behaviors/context-menu.mjs +6 -2
- package/dist/behaviors/copy.d.mts +4 -0
- package/dist/behaviors/copy.mjs +7 -6
- package/dist/behaviors/datatable.d.mts +78 -0
- package/dist/behaviors/datatable.mjs +54 -11
- package/dist/behaviors/date-picker.d.mts +4 -0
- package/dist/behaviors/date-picker.mjs +104 -36
- package/dist/behaviors/dropdown.d.mts +4 -0
- package/dist/behaviors/dropdown.mjs +7 -3
- package/dist/behaviors/input.d.mts +4 -0
- package/dist/behaviors/input.mjs +29 -22
- package/dist/behaviors/modal.d.mts +11 -0
- package/dist/behaviors/modal.mjs +11 -6
- package/dist/behaviors/nav.d.mts +7 -0
- package/dist/behaviors/nav.mjs +25 -9
- package/dist/behaviors/notification.d.mts +172 -0
- package/dist/behaviors/notification.mjs +243 -89
- package/dist/behaviors/popover.d.mts +46 -0
- package/dist/behaviors/popover.mjs +2 -1
- package/dist/behaviors/select.d.mts +4 -0
- package/dist/behaviors/select.mjs +131 -26
- package/dist/behaviors/sidebar.d.mts +4 -0
- package/dist/behaviors/sidebar.mjs +4 -3
- package/dist/behaviors/slider.d.mts +4 -0
- package/dist/behaviors/slider.mjs +4 -3
- package/dist/behaviors/split-pane.d.mts +4 -0
- package/dist/behaviors/split-pane.mjs +13 -13
- package/dist/behaviors/tabs.d.mts +8 -0
- package/dist/behaviors/tabs.mjs +44 -19
- package/dist/behaviors/tag-input.d.mts +4 -0
- package/dist/behaviors/tag-input.mjs +12 -3
- package/dist/behaviors/theme.d.mts +25 -0
- package/dist/behaviors/theme.mjs +3 -1
- package/dist/behaviors/toast.d.mts +26 -0
- package/dist/behaviors/toast.mjs +24 -18
- package/dist/behaviors/tree.d.mts +4 -0
- package/dist/behaviors/tree.mjs +4 -3
- package/dist/behaviors/upload.d.mts +4 -0
- package/dist/behaviors/upload.mjs +63 -26
- package/dist/components/ai-chat.css +1037 -0
- package/dist/components/ai-chat.mjs +3 -0
- package/dist/components/ai-composer.mjs +3 -0
- package/dist/components/ai-context.mjs +2 -0
- package/dist/components/ai-diff.css +311 -0
- package/dist/components/ai-diff.mjs +3 -0
- package/dist/components/ai-files.mjs +3 -0
- package/dist/components/ai-message.mjs +3 -0
- package/dist/components/ai-panel.css +682 -0
- package/dist/components/ai-reasoning.mjs +3 -0
- package/dist/components/ai-subagent.mjs +2 -0
- package/dist/components/ai-todo.mjs +2 -0
- package/dist/components/ai-tool-call.mjs +2 -0
- package/dist/components/ai-tool.css +532 -0
- package/dist/components/ai-usage.mjs +3 -0
- package/dist/components/badge.css +4 -8
- package/dist/components/breadcrumb.css +1 -5
- package/dist/components/btn.css +4 -7
- package/dist/components/card.css +3 -6
- package/dist/components/chart-bar.css +10 -0
- package/dist/components/chart-calendar.mjs +5 -0
- package/dist/components/chart-gauge.css +1 -1
- package/dist/components/chart-heatmap.css +40 -0
- package/dist/components/chart-scatter.css +24 -0
- package/dist/components/chart-scatter.mjs +4 -0
- package/dist/components/charts.css +86 -1
- package/dist/components/charts.mjs +1 -0
- package/dist/components/command-palette.css +2 -2
- package/dist/components/content.css +8 -9
- package/dist/components/copy.css +8 -8
- package/dist/components/datatable.css +4 -8
- package/dist/components/date-picker.css +3 -7
- package/dist/components/empty.css +0 -4
- package/dist/components/feedback.css +10 -17
- package/dist/components/form.css +13 -14
- package/dist/components/input.css +2 -2
- package/dist/components/layout.css +36 -36
- package/dist/components/media.css +12 -8
- package/dist/components/menu.css +2 -2
- package/dist/components/modal.css +1 -1
- package/dist/components/nav.css +11 -6
- package/dist/components/notification.css +28 -28
- package/dist/components/pagination.css +1 -1
- package/dist/components/panel.css +16 -16
- package/dist/components/pill.css +15 -15
- package/dist/components/segmented.css +5 -5
- package/dist/components/segmented.mjs +1 -0
- package/dist/components/select.css +12 -20
- package/dist/components/sidebar.css +1 -1
- package/dist/components/split-pane.css +0 -1
- package/dist/components/stat.css +14 -18
- package/dist/components/steps.css +2 -2
- package/dist/components/switch.css +8 -12
- package/dist/components/table.css +46 -47
- package/dist/components/tabs.css +35 -46
- package/dist/components/tag-input.css +4 -4
- package/dist/components/toast.css +16 -13
- package/dist/components/tooltip.css +2 -2
- package/dist/index.d.mts +37 -0
- package/dist/index.mjs +7890 -1199
- package/dist/registry.json +113 -4
- package/dist/retro-effects.css +21 -17
- package/dist/tokens/colors.css +4 -1
- package/dist/tokens/retro-effects.css +21 -17
- package/dist/tokens/style-profiles.css +21 -0
- package/dist/tokens.css +25 -1
- package/dist/ui.css +3168 -433
- package/docs/spec/ai-native.md +659 -0
- package/package.json +15 -5
package/AGENTS.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# AGENTS.md — @icen.ai/ui
|
|
2
|
+
|
|
3
|
+
本文件面向 AI 编程助手,分两段:**第一段 = 消费方速查**(在用户项目里使用本包的 AI 先读这段,随 npm 发布于 `node_modules/@icen.ai/ui/AGENTS.md`);**第二段 = 本仓库开发约定**(给在本仓库内写代码的 AI)。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 第一段 · 消费方速查(在用户项目里用 @icen.ai/ui)
|
|
8
|
+
|
|
9
|
+
完整类型签名看包内 `dist/*.d.ts`;交互式文档 <https://ui.icen.ai>;AI 能力规格 `docs/spec/ai-native.md`;文档全文(喂模型)<https://ui.icen.ai/llms-full.txt>。
|
|
10
|
+
|
|
11
|
+
## 形态约束(先读,避免生成跑不通的代码)
|
|
12
|
+
|
|
13
|
+
- 零依赖纯 ESM + TypeScript;组件 = CSS + 行为 JS,**不绑定框架**(React/Vue 在 effect/mounted 里调行为函数即可)。
|
|
14
|
+
- 先引 tokens 再引组件:`@import '@icen.ai/ui/tokens.css';`(组件 CSS 全部消费 `--token-*` 变量)。
|
|
15
|
+
- **Astro 陷阱**:kit 入口的 css import 在页面 `<script>`(client bundle)里会被摇掉——Astro 项目必须在布局 frontmatter 引组件 CSS;Vite SPA / webpack 无此问题。
|
|
16
|
+
- 行为函数全部**幂等**且 **SSR 安全**(`window` 未定义静默返回);渲染只写 `textContent`,绝不 innerHTML。
|
|
17
|
+
- 自定义事件统一 `icen:` 前缀(图表 `icen:chart-*`,AI 族 `icen:ai-*`)。明暗 = html `data-theme="dark"`;密度 = 容器 `data-density`。
|
|
18
|
+
|
|
19
|
+
## 安装颗粒度
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bun add @icen.ai/ui # 或 npm i
|
|
23
|
+
bunx @icen.ai/ui list # 全部 slug
|
|
24
|
+
bunx @icen.ai/ui add btn toast # 按 slug 打印引入行
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
@import '@icen.ai/ui/tokens.css';
|
|
29
|
+
@import '@icen.ai/ui/ui.css'; /* 全量;或单引 @icen.ai/ui/components/btn.css */
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- **kit 一行入口**(CSS + behavior 一起):`import '@icen.ai/ui/kit/btn';`
|
|
33
|
+
- **只取行为 JS**:`import { renderChart } from '@icen.ai/ui/behaviors/charts';`
|
|
34
|
+
- slug 清单机器可读:`@icen.ai/ui/registry.json`。
|
|
35
|
+
|
|
36
|
+
## 图表通用层(behaviors/charts)— ChartSpec 纯 JSON,天然可被模型调用
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { renderChart } from '@icen.ai/ui/behaviors/charts';
|
|
40
|
+
const c = renderChart(el, {
|
|
41
|
+
type: 'line', // line|area|vbar|hbar|stack|donut|radar|heatmap|calendar|sparkline|gauge|scatter
|
|
42
|
+
title: '各版本能力数',
|
|
43
|
+
labels: ['v0.1','v0.2','v0.3'],
|
|
44
|
+
series: [{ name: '组件', values: [31,41,43] }, { name: 'AI 专属', values: [0,0,0] }],
|
|
45
|
+
format: { unit: ' 个', places: 0 }, // 值格式化描述符(chartFormatValue)
|
|
46
|
+
});
|
|
47
|
+
c.update(newSpec);
|
|
48
|
+
c.on('click', (e) => e.detail); // { index, seriesIndex, seriesName, label, value, pointerX, pointerY }
|
|
49
|
+
c.destroy();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- 省略 `type` 时 `inferChartType` 自动选型(ISO 日期/年月/周几/版本号 v0.8/纯年份等时序标签 → line)。
|
|
53
|
+
- 事件:`icen:chart-{move,out,click,dblclick,contextmenu,legend-toggle}`;内置 tooltip 门户(`tooltip:false` 关闭只派事件);图表头统一带**图例眼睛**(隐藏/显示全部指标)。
|
|
54
|
+
- 底层渲染器(同 chrome,非 AI 场景直用):`renderVBar/renderHBar/renderStack/renderDonut/renderLine/renderArea/renderRadar/renderHeatmap/renderCalendar/renderSparkline/renderGauge/renderScatter`。
|
|
55
|
+
|
|
56
|
+
## AI 原生族 — 7 态状态机 + kind 注册表
|
|
57
|
+
|
|
58
|
+
状态:`pending | running | streaming | approval | done | error | cancelled`。
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { renderAiMessage, createAiStream } from '@icen.ai/ui/behaviors/ai-chat';
|
|
62
|
+
import { renderAiToolCall, renderAiSubagent, initAiTool, initAiSubagent } from '@icen.ai/ui/behaviors/ai-tool';
|
|
63
|
+
|
|
64
|
+
renderAiMessage(el, {
|
|
65
|
+
role: 'assistant', model: 'kimi-k3',
|
|
66
|
+
content: [ // 多模态 parts(纯 string 也可)
|
|
67
|
+
{ type: 'text', text: '结论如下' },
|
|
68
|
+
{ type: 'image', url, alt }, // 另有 audio / video / file{mimeType,filename} / resource_link
|
|
69
|
+
],
|
|
70
|
+
meta: '刚刚 · 1.1k tok',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const t = renderAiToolCall(el, { id, name: 'WebSearch', kind: 'browser', status: 'running', input: { query } });
|
|
74
|
+
t.update({ status: 'done', output: '命中 24 条', durationMs: 1800 });
|
|
75
|
+
// 审批态:status:'approval' + approval:{reason} → 卡上按钮派 icen:ai-approve / icen:ai-reject
|
|
76
|
+
renderAiSubagent(el, { name: 'verify', input: { task }, activities: [/* 同工具卡 */] });
|
|
77
|
+
initAiTool(); initAiSubagent(); // 事件委托,页面级一次
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- 线格式归一 `normalizeContentParts`(OpenAI/Anthropic/MCP 通吃);取纯文本 `contentToText`;资源 URL `aiContentUrl`。
|
|
81
|
+
- kind 可扩展:`registerAiKind({ label, icon, tint, summarize })`;工具卡输入 JSON 默认折叠,`chart`/`error`/`approval`/多模态输出默认展开。
|
|
82
|
+
- 推理块 `.ai-reasoning` + `createAiStream(host)` 流入(host 在 `.ai-reasoning` 内时完成自动折叠并回填耗时)。
|
|
83
|
+
- 面板:`renderAiTodo/renderAiUsage/renderAiContext/renderAiAudit`(ai-panel)、`parseUnifiedDiff/renderAiDiff`(ai-diff)。
|
|
84
|
+
|
|
85
|
+
## 输入台 + 零接线绑定
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { initAiComposer, bindComposer, setComposerTodo, setComposerModels } from '@icen.ai/ui/behaviors/ai-composer';
|
|
89
|
+
|
|
90
|
+
initAiComposer(el);
|
|
91
|
+
setComposerModels(el, listAiProviders(), { provider: 'kimi', model: 'kimi-k3' });
|
|
92
|
+
bindComposer(el, { client, messages: chatEl }); // 一行接通发送→流式→停止→排队→错误→用量环
|
|
93
|
+
setComposerTodo(el, [ // 业界模式:plan 是工具调用,实时状态挂输入框
|
|
94
|
+
{ content: '搜集资料', status: 'done' },
|
|
95
|
+
{ content: '渲染图表', status: 'running', activeForm: '正在渲染图表' },
|
|
96
|
+
]); // chip 显示 x/y;全 done → is-done;有 error/cancelled → is-error;点开弹层看清单
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Provider 层(behaviors/ai-provider)
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { createAiClient, createAiAuditor, estimateCost } from '@icen.ai/ui/behaviors/ai-provider';
|
|
103
|
+
|
|
104
|
+
const client = createAiClient({ provider: 'kimi', model: 'kimi-k3', apiKey });
|
|
105
|
+
await client.chat({ messages }); // 或 client.stream(...)(SSE 逐 chunk)
|
|
106
|
+
const auditor = createAiAuditor(); // log() 条目含 cost(定价表估算)与 ttftMs
|
|
107
|
+
// 每次请求收尾派 icen:ai-done(detail: status/usage/cost/error/durationMs/ttftMs)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- 多模态出线:`toOpenAiContent/toAnthropicBlocks`(含 cache_control 与 tool_result 回传)。
|
|
111
|
+
- 用量双口径:`contextEstimate(history)` = 上下文估算(输入框用量环);计费走 `normalizeUsage` + `estimateCost`(定价表未命中不猜价)。别混用。
|
|
112
|
+
|
|
113
|
+
## 工具体系(behaviors/ai-tools)— 把 UI 能力注册成模型可调用工具
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { createAiToolArea, aiToolsToOpenAI, aiToolsToMcp, aiToolsManifest, parseAiToolArgs }
|
|
117
|
+
from '@icen.ai/ui/behaviors/ai-tools';
|
|
118
|
+
|
|
119
|
+
const area = createAiToolArea(mountEl, {
|
|
120
|
+
tools: ['render_chart', 'chart-*'], // 白名单支持 glob;缺省 = 全部已注册
|
|
121
|
+
max: 4, // 挂载上限,LRU 淘汰
|
|
122
|
+
});
|
|
123
|
+
area.call('render_chart', { type: 'line', labels, series }); // 宿主手动/模型触发
|
|
124
|
+
area.setTools(['render_chart']); area.setMax(2); area.onChange(cb);
|
|
125
|
+
|
|
126
|
+
aiToolsToOpenAI(); // → OpenAI tools 数组
|
|
127
|
+
aiToolsToMcp(); // → MCP tools/list 形态
|
|
128
|
+
aiToolsManifest(); // → 自描述 manifest
|
|
129
|
+
parseAiToolArgs('render_chart', raw); // 模型回包解析(容错 JSON 字符串/对象)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
自定义工具:`registerAiTool({ name, description, inputSchema, onCall })`;内置 `render_chart`。挂载区条目可折叠/移除,超限 LRU 自动回收。
|
|
133
|
+
|
|
134
|
+
## 深挖索引(复杂模块不设单独 AGENTS.md,类型真相在 .d.mts)
|
|
135
|
+
|
|
136
|
+
每个 behavior 的完整接口/类型/字段约束都随包发布在 `dist/behaviors/<模块>.d.mts`(JSDoc 注释含行为约束,如「el 传容器或内部任意元素,就近解析 `[data-ai-composer]`」)。**不建议也不需要按组件再写深层 AGENTS.md**——复制必漂移;读类型面即读文档:
|
|
137
|
+
|
|
138
|
+
| 深挖什么 | 去哪读 |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| AI 族全部签名与字段约束 | 包内 `dist/behaviors/ai-{core,chat,composer,tool,panel,diff,provider,tools}.d.mts` |
|
|
141
|
+
| 图表 ChartSpec 全字段 + 各类型 options | `dist/behaviors/charts.d.mts` |
|
|
142
|
+
| 上述类型面 + JSDoc 的纯文本快照 | <https://ui.icen.ai/llms-full.txt>(「API 类型面」节,构建期自动内嵌) |
|
|
143
|
+
| 设计动机 / 状态机语义 / 口径取舍 | `docs/spec/ai-native.md`(包内随发) |
|
|
144
|
+
| DOM 契约 / CSS 类名 | 各组件文档页 <https://ui.icen.ai/components/<slug>/,kit 入口同款 CSS 头注释 |
|
|
145
|
+
| 场景配方(选图决策树 / 聊天五步 / 主题切换) | skill `icen-ui`(skill.icen.ai,`curl -sL https://skill.icen.ai/install/ui.sh \| bash`)——按需触发的流程层,与本手册(事实层)互补 |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
# 第二段 · 本仓库开发约定
|
|
150
|
+
|
|
151
|
+
## 项目概览
|
|
152
|
+
|
|
153
|
+
`@icen.ai/ui` = Icen Design System 的单一事实源:设计 tokens + 无框架组件 CSS + 行为 JS。生态内所有前端(accounts / skill / editor / 未来 `*.icen.ai`)从这里取设计资产,**不允许在消费项目里另起一套样式**。
|
|
154
|
+
|
|
155
|
+
三层正交架构(移植自 opengal,见其 `docs/spec/theme-system.md`):
|
|
156
|
+
- **色彩预设**(`src/tokens/colors.css`):`clay`(默认)/ `piano` / `art` / `vangogh` / `ink` / `retro`,各含 `.dark`;挂在 `<html>` 类上,clay 是 `:root` 默认
|
|
157
|
+
- **风格配置**(`src/tokens/style-profiles.css`):`.style-modern` / `.style-retro` / `.style-terminal`;density / motion / typography / radius 全 token 化,与色彩正交
|
|
158
|
+
- **明暗**:`.dark` 类,与预设组合(`class="piano dark"`)
|
|
159
|
+
|
|
160
|
+
icen canonical:`clay` 亮色 + IBM Plex Mono/CJK 宋体(`src/tokens/typography.css`)。**clay 基值已原地校准为 accounts.icen.ai 线上视觉**(纸白底 #faf9f5、半透明线条、绿 #3d8a5a/黄 #b8860b/红 #c0392b)——改它 = 改全生态的品牌脸,慎重。
|
|
161
|
+
|
|
162
|
+
## 构建
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
bun install
|
|
166
|
+
bun run build # tsup(src/behaviors/*.ts → dist/behaviors/*.mjs + index.mjs + .d.mts 类型面)
|
|
167
|
+
# + bun scripts/dts-ext.mjs(.d.ts 改名 .d.mts:.mjs 的声明必须同名 .d.mts)
|
|
168
|
+
# + bun scripts/build-css.ts(css → dist/:tokens.css/ui.css/base.css/
|
|
169
|
+
# retro-effects.css/tokens/*/components/*/registry.json)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
无测试框架。验证 = `bunx tsc --noEmit` + `bun run build` 通过 + `site/` 文档站目视(`cd site && bun install && bun run build`)。
|
|
173
|
+
|
|
174
|
+
## 规模
|
|
175
|
+
|
|
176
|
+
- **56 个组件 CSS**(含 charts 基座 + 10 个细分图表 CSS(+v0.8 chart-scatter)+ 10 个 v0.5 新增:copy/tag/badge/scroll-area/notification/back-top/command-palette/date-picker/toolbar/split-pane + 4 个 v0.7 AI 族合并文件:ai-chat/ai-tool/ai-diff/ai-panel)
|
|
177
|
+
- **33 个 behaviors TS**(+v0.8 ai-tools;charts 通用层见图表节)(theme/tabs/toast/copy/input/select/slider/tag-input/upload/modal/dropdown/popover/context-menu/accordion/tree/carousel/charts[10 种图]/nav/sidebar/datatable/notification/back-top/command-palette/date-picker/split-pane + 6 个 AI 族:ai-core/ai-chat/ai-composer/ai-tool/ai-diff/ai-panel)
|
|
178
|
+
- **77 个组件 slug**(kit 一行入口 / CLI / 文档站侧栏共用 SLUGS 清单)
|
|
179
|
+
- **4 个 token 文件**:colors.css(6 预设 × 明暗)、style-profiles.css(modern/retro/terminal)、typography.css、retro-effects.css(可选)
|
|
180
|
+
|
|
181
|
+
## 发布(OIDC Trusted Publishing,已配好)
|
|
182
|
+
|
|
183
|
+
- 全自动:改 `package.json` 的 version → commit + push main → `git tag v<x.y.z> && git push origin v<x.y.z>` → `.github/workflows/publish.yml` 自动构建并发 npm(带 provenance,**不要设 NODE_AUTH_TOKEN**)。publish.yml 只挂 tag push——不要再加 release 触发器,否则对同一 tag 发 Release 会起两次 run 抢 publish。
|
|
184
|
+
- npm 包设置已是「Require 2FA and disallow tokens」:token 一律拒发,只有这条 CI 能发。
|
|
185
|
+
- 手动首发/排障流程见 cli 仓库 `docs/publish-npm.md`(EOTP 安全密钥流程、镜像源 registry 坑、422 repository 字段坑)。
|
|
186
|
+
|
|
187
|
+
## 约定
|
|
188
|
+
|
|
189
|
+
- **组件只消费 `--token-*`,不写死颜色/圆角/阴影/字体/时长/间距**(radius 用 `--radius-*`、阴影用 `--token-shadow-*`、动效用 `--duration-*`/`--ease-*`、间距用 `--density-*`、字号用 `--density-font-size-*`、控件高度用 `--density-input-height-*`)。
|
|
190
|
+
- 语义色纪律:仅绿 `--token-success` / 黄 `--token-warning` / 红 `--token-error` / 信息 `--token-info` + 品牌 `--token-accent`。
|
|
191
|
+
- **类名规范(0.6.0 收敛,breaking)**:
|
|
192
|
+
- 语义修饰符只认 `--success/--warning/--error/--info`:pill `.ok/.warn/.bad/.accent` → `.pill--success/.pill--warning/.pill--error/.pill--accent`;toast 类 `.ok/.err/.warn` → `.toast--success/.toast--error/.toast--warning`(JS 方法 `toast.ok()` 等不变)+ 新增 `.toast--info`;notification `.notif--*` → `.notification--*`;stat `.icon--ok/.icon--warn/.icon--bad` → `.icon--success/.icon--warning/.icon--error`,`.stat-num.accent/.warn/.bad/.info` → `.stat-num--accent/--warning/--error/--info`。
|
|
193
|
+
- 状态类一律 `.is-*`:`.page-tab.active` → `.is-active`、`.carousel-dot.active` → `.is-active`、`.leaving` → `.is-leaving`(toast/notification)、`.copy-btn.done` → `.is-done`。
|
|
194
|
+
- 根类去前缀:`.panel-block` → `.panel`、`.admin-table` → `.table`、`.page-tabs/.admin-tabs` → `.tabs`。
|
|
195
|
+
- tabs.css 的 `--scroll` 变体已删除(违反「tab 条永不滚动」纪律)。
|
|
196
|
+
- 滚动条纪律:全局细薄无上下箭头(`base.css` 统一负责,组件不再写滚动条样式);tab 条类组件**永不滚动**(`flex-wrap: wrap`)。
|
|
197
|
+
- **交互基元**:`base.css` 提供 6 个组件地基 `.pressable` / `.control` / `.field` / `.lift` / `.surface-elevated` / `.focus-ring`。`style-profiles.css` 的 `.style-retro` 段选择器引用这 6 个类名——改名必须同步修 style-profiles.css,否则 retro 风格失效。共享 keyframes(`icen-pop-in` / `icen-spin` / `icen-skeleton-pulse`)也在 base.css,组件动画复用、不各自重定义。
|
|
198
|
+
- **z-index 标尺**:浮层一律 portal 到 body,z-index 取 `--z-base/raised/sticky/chrome/toast/banner/dialog/popover/tooltip/fx`(0.6.0 新增 `--z-fx`,retro-effects 特效层用)。
|
|
199
|
+
- **浮层 portal 家族**:popover / dropdown / context-menu / command-palette / date-picker / **select**(0.6.0 起 panel 也 portal 到 body,与 popover 共用定位模式)。
|
|
200
|
+
- **事件前缀纪律**:behavior 派发的自定义事件统一 `icen:` 前缀(如 tabs 的 `icen:tab-change`、upload 的 `icen:upload`)。otp 为兼容双发 `icen:otp-complete`(新)与 `otp:complete`(deprecated,下个大版本删)。
|
|
201
|
+
- **a11y**:所有可聚焦元素必须有 `:focus-visible` 环;状态变化必须同步 `aria-*`;modal/dialog 必须有 focus trap + ESC 关闭 + 焦点还原。
|
|
202
|
+
- **动效守卫**:所有动画/过渡在 `@media (prefers-reduced-motion: reduce)` 下压缩为 ~0ms;触屏(`pointer: coarse`)关闭 `:hover` 抬升;高对比度模式(`forced-colors: active`)保留焦点环。
|
|
203
|
+
- 新组件 = `src/components/<name>.css` 一个文件 + 文档站 components 页加一节;交互行为放 `src/behaviors/<name>.ts` 并配套 CSS 类契约。每个 CSS 头注释必须列出完整 DOM 契约。
|
|
204
|
+
- behaviors 全部 SSR 守卫(`typeof document === 'undefined'`),文本赋值用 `textContent`(禁 innerHTML),init 函数全部幂等(重复调用安全,通过 WeakSet/MarkedElement.__icen*Init 标记)。initTabs/initNav/initBackTop 等返回**销毁函数**(移除监听 + 复位幂等标记,销毁后可重新 init)。
|
|
205
|
+
- 函数式组件族(charts / datatable):不写类契约,导出 `createX(el, opts) → handle`(charts 是 renderX);同样一文件 CSS + 文档页一节 + slugs.mjs 登记。datatable 右键菜单复用 context-menu,其依赖的额外 CSS 走 slugs.mjs 的 EXTRA_CSS(kit 入口自动带上)。
|
|
206
|
+
- **图表按需安装**:charts 细分为 11 个独立可安装的 kit 入口(`chart-line` / `chart-bar` / `chart-pie` / `chart-radar` / `chart-heatmap` / `chart-area` / `chart-stack` / `chart-gauge` / `chart-sparkline` / `chart-scatter` / `chart-calendar`)+ umbrella `charts`(一条 import 全拿)。**通用层(v0.8)**:`renderChart(el, spec)` 单一入口吃纯 JSON ChartSpec(type 别名归一 `normalizeChartSpec` / 自动选型 `inferChartType` / `data[]+dims` 任意维度 pivot / `format` 描述符替代回调——AI 调用层的直接底座);交互委托 `icen:chart-hover(enter/move/leave)/click/dblclick/contextmenu`(detail 含 index/seriesIndex/seriesName/label/value/指针坐标,右键默认 preventDefault)+ 内置 tooltip(portal,`tooltip:false` 关)+ 图例点击切换系列(`icen:chart-legend-toggle`);图例可见性统一由 renderChart 头部行(.chart-head = 标题 + 小眼睛)控制——根 `data-legend-hidden` 容器级 CSS 隐藏全部图型图例(`spec.legend=false` 定初值,事件 `icen:chart-legend-visibility`),图表体渲染进内层 .chart-body(底层渲染器按契约清空传入 el,头部不能同层);多系列 line(调色盘)/ vbar(分组 `stacked` 堆叠)已内建,标记统一 `data-chart-mark` + `data-chart-*` 数据位。共享基座在 `charts.css`(.chart / .chart-legend / .chart-labels / 调色盘),各类型专属样式在独立 `chart-<type>.css`;所有渲染函数仍在 `behaviors/charts.ts` 一个文件(tree-shake 友好)。子类型 slug → charts 行为模块的映射走 slugs.mjs 的 **`SLUG_BEHAVIOR`**(key=子类型 slug,value='charts'),kit 入口按 `SLUG_EXPORTS` 只 re-export 该类型的渲染函数。
|
|
207
|
+
- 组件 slug 映射(css 合并文件 / init 函数名 / behavior 模块名)的单一事实源是 `scripts/slugs.mjs`——新组件必须在此登记;`scripts/build-css.ts` 据此生成 kit 一行入口(`dist/components/<slug>.mjs` = import css + re-export behavior,消费侧 `@icen.ai/ui/kit/<slug>`)与 registry.json 的 slugs 面,CLI(`dist/cli.mjs`,`bunx @icen.ai/ui add <slug>` 打印引入行,不拷贝源码)与文档站都消费它。`slugs.mjs` 的类型声明在 `slugs.d.mts`,加导出成员时两处同步改。behavior 与 slug 不同名的(如 segmented 的 OTP 由 input.ts 的 initInput 提供)在 `SLUG_BEHAVIOR` 登记映射、在 `SLUG_EXPORTS` 登记 re-export,kit 入口才能正确带出。
|
|
208
|
+
- **kit 的 Astro 陷阱**:kit 入口里的 css import 在 Astro 页面 `<script>`(client bundle)里会被摇掉——Astro 项目必须在布局 frontmatter 里引组件 CSS(accounts 已踩过);Vite SPA / webpack 无此问题。文档站安装段已带此提示。
|
|
209
|
+
- 文档站(`site/`):站壳/代码块样式集中在 `site/src/styles/site.css`;可复用展示件在 `site/src/components/`(SiteHeader / SiteFooter / CodeBlock / PmInstall);轻量语法高亮在 `site/src/lib/highlight.ts`(单行 token 化,`.tok-*` 颜色消费语义 token)。站顶 ⌘K 全站搜索 = 自家 command-palette 组件直出(条目由 `COMPONENTS` 生成),既是功能也是门面条 demo。
|
|
210
|
+
- **skill `icen-ui` 源在本仓库**(`skill/icen-ui/`,SKILL.md + 中英产品页):只写 WHEN/WHICH 场景配方,不抄 API 签名(事实层在 AGENTS.md/.d.mts)。改完跑 `bun scripts/sync-skill.mjs` 单向同步到兄弟仓库 `../skill/skills/icen-ui/`(skill 仓库那份是产物,勿手改),在该仓库提交 push 后 CI 自动上 skill.icen.ai;`--check` 可挂 CI 防漂移。
|
|
211
|
+
- 文档站字体体系(site.css 顶部):prose/站壳 = `--site-font-sans`(系统无衬线),demo 区 = `--site-font-mono`(Plex Mono + CJK 无衬线回退),展示级大标题才用 `--font-heading` 宋体——小字号中文一律不走宋体/通用 monospace 回退。
|
|
212
|
+
- CSS 产物由 `scripts/build-css.ts` 生成,**不要手改 dist/**。`tokensExtras`(如 retro-effects.css)单独拷贝、暴露 exports,**不**进 `tokens.css`/`ui.css` 默认拼合。
|
|
213
|
+
- clay 的 12 基值 token 改动属于品牌级变更;新增色彩预设 = 在 colors.css 加 `.<name>` + `.<name>.dark` 两块完整 token 面(仿照现有 6 套),并注册到 `src/behaviors/theme.ts` 的 PRESETS 与文档站。
|
|
214
|
+
- 文档分组(`site/src/lib/components.ts` 的 `GROUPS`)= 侧栏顺序 = 索引页分组顺序:基础 / AI 原生 / 表单 / 数据展示 / 图表 / 浮层 / 反馈 / 导航(v0.8 调整:AI 上移第二位做拉新门面;「表格」并入数据展示;AI 组更名「AI 原生」)。侧栏分组为 `<details>` 折叠(可多开,首访全开,localStorage `icen.docs.nav.groups` 记忆,深链自动展开当前组)。
|
|
215
|
+
- **反馈三件正交**:`toast`(瞬时 2.6s 右下角)/ `alert`(内嵌页面流)/ `notification`(持久右上角栈,工程级——进度通知/confirm Promise/多按钮/hover 暂停/倒计时/优先级置顶/已读未读/持久化/多容器 createNotificationCenter)——三者各司其职不互相替代;`copy` 是原地按钮反馈(区别于 toast 的全局通知)。
|
|
216
|
+
- **徽章三件正交**:`pill`(行内状态徽章,语义色)/ `tag`(中性展示标签,可选关闭按钮与选中态)/ `badge`(角标式数字/圆点,挂外层元素角上,外层需 `position: relative`)。
|
|
217
|
+
- **组件 v2 增强(0.5.0 全量升级)**:所有基础组件已达到工程级深度——
|
|
218
|
+
- **select**:`data-select-search` 可搜索过滤(支持 `data-keywords` 辅助关键字 + 分组自动隐藏)、`data-select-multiple` 多选(chips 显示 + `data-select-max` 上限 + `.select-clear` 清除钮)、`.select-group` / `.select-group-label` 分组、`.select-empty` 无结果态。
|
|
219
|
+
- **input**:`maxlength`/`data-count` 字符计数器(接近上限转 warn、到顶转 error)、`data-mask="###-####-####"` 输入掩码(`#` 数字 / `A` 字母 / `*` 任意,原始值存 `data-raw-value`)、`data-trim` blur 去空白、全局 IME 组合态安全(compositionstart/end 挂起处理)。OTP 单元格(segmented 族)的跳格/粘贴分摊也由 initInput 接管。
|
|
220
|
+
- **upload**:`data-upload-list` 开启文件列表(图片自动缩略图、大小格式化、单文件 × 移除)、`data-max-size` / `data-max-files` 校验、accept 类型校验、事件 `icen:upload` / `icen:upload-error` / `icen:upload-remove`。
|
|
221
|
+
- **table**(静态表):`--zebra` 斑马纹、`caption` 标题、`--sticky-col` + `.col-fixed` 首列粘滞、`.row-expand` 展开行、行状态色(`row-success` / `row-warning` / `row-error`)、列宽工具类(`.col-narrow` / `.col-wide` / `.col-action`)。
|
|
222
|
+
- **stat**:横向布局(`--horizontal`)、迷你 sparkline 槽(`.stat-spark`)、SVG 进度环(`.stat-ring`)、加载骨架态(`.is-loading`)、图标语义色变体(`.icon--success/warning/error/info`)。
|
|
223
|
+
- **empty**:类型预设(`--error` / `--404` / `--search` / `--maintenance` / `--network`)、`--illustrated` 渐变背景、`--inline` 内嵌模式。
|
|
224
|
+
- **panel**:可折叠(`--collapsible` + `.is-collapsed`)、副标题(`.panel-subtitle`)、平面变体(`--flat`)、强调变体(`--accent`)。
|
|
225
|
+
- **slider**:带标签刻度(`.slider-marks` + `.slider-mark`)、拖拽值提示气泡(`.slider-tooltip`)、渐变填充(`--gradient`)。
|
|
226
|
+
- **card**:封面比例变体(`.card-media--square/tall/wide`)、卡片网格(`.card-group`)、卡片列表(`.card-list`)、状态色边(`--success/warning/error`)、角标位(`.card-badge`)。
|
|
227
|
+
- **feedback**:环形进度(`.progress-ring`)、条纹/渐变进度条(`--striped` / `--gradient`)、点阵/条形 spinner(`--dots` / `--bars`)、全屏遮罩(`.spinner-overlay`)、骨架组合体(`.skeleton-text-group` / `.skeleton-media` / `.skeleton-table`)、shimmer 波纹(`--shimmer`)、alert 增强(`.alert-actions` / `--banner` / `--inline`)。
|
|
228
|
+
- **switch**:色彩变体(`--success/warning/danger`)、带文字开关(`--text`)、开关行(`.switch-row`)、单选卡片(`.radio-card` + `.radio-card-grid`)、复选网格(`.checkbox-grid`)。
|
|
229
|
+
- **tabs**:可关闭 tab(`.page-tab-close`)、徽标(`.page-tab-badge`)、图标(`.page-tab-icon`)、分段式(`--segment`)、垂直(`--vertical`)、尺寸(`--sm/lg`)。
|
|
230
|
+
- **nav**:搜索槽(`.nav-search`)、通知徽标(`.nav-btn-badge`)、链接式(`.nav-link`)、用户区(`.nav-user`)。
|
|
231
|
+
- **sidebar**:头部/底部槽(`.sidebar-header` / `.sidebar-footer`)、折叠/轨道模式(`--collapsed`)、激活指示条、分隔线、徽章变体色。
|
|
232
|
+
- **content**:列表增强(`.list-item-leading/meta/trailing` + 选中/斑马)、时间线变体(右侧/交替/自定义尺寸/扩展内容槽)、描述列表变体(行内/垂直/可复制)、树增强(连接线 `.tree--lined` + 拖拽态 + 搜索高亮)、代码块(`.code-block`)、kbd 大号/按下态。
|
|
233
|
+
- **media**:头像变体(语义色/渐变/方形/通知环)、头像组溢出计数、评分只读/清除/计数/摘要、轮播缩略图/索引/渐入。
|
|
234
|
+
- **toast**:语义图标、关闭钮、富内容(标题+正文)、超时进度条、6 种容器定位。
|
|
235
|
+
- **pill**:可关闭(`.pill-close`)、图标、实时脉冲(`--live`)、纯数字(`--count`)、渐变(`--gradient`)。
|
|
236
|
+
- **form**:水平字段布局(`--horizontal`)、表单区块(`.form-section`)、操作区(`.form-actions`)、行内表单(`--inline`)、字段网格(`.form-grid`)、加载遮罩(`.form-loading`)、label 帮助图标 / 可选标记。
|
|
237
|
+
- layout.css 已 token 化(0.6.0):stack / divider / surface / grid 等布局工具类的间距全部消费 `--density-space-*`,无写死 px。
|
|
238
|
+
|
|
239
|
+
## AI 组件族(v0.7 新增)
|
|
240
|
+
|
|
241
|
+
规格唯一事实源:`docs/spec/ai-native.md`(类名 / 导出签名 / 事件名以它为准)。11 个 slug 在文档站侧栏单列「AI」分组,排在导航之后:
|
|
242
|
+
|
|
243
|
+
- **清单与 CSS 合并**:ai-chat / ai-message / ai-reasoning / ai-composer → `ai-chat.css`;ai-tool-call / ai-subagent → `ai-tool.css`;ai-diff / ai-files → `ai-diff.css`;ai-todo / ai-context / ai-usage → `ai-panel.css`。6 个 behavior:ai-core(状态机 / 注册表 / 适配器 / 格式化 / svgIcon)、ai-chat、ai-composer、ai-tool、ai-diff、ai-panel,全部经 `src/index.ts` `export *`。
|
|
244
|
+
- **`.ai-item` 基元**:行式 AI 条目的"基类"(icon / main / side 状态点+meta / detail),kind 与变体只覆盖局部变量 `--ai-item-tint` 等槽位、不改结构(`--btn-*` 继承模式的推广);第三方 kind 的 tint 由 behavior 内联 `--ai-item-tint: var(--token-<tint>)` 兜底。
|
|
245
|
+
- **7 态状态机**(一切 AI 条目共享,语义色与动画全库一致):`.is-pending / running / streaming / approval / done / error / cancelled`;状态点 `.ai-item-status` 纯 CSS 由 `.is-*` 驱动;**默认展开集**:chart kind / error / approval / 多模态回执首渲染自动展开,其余 kind 默认折叠(用户手动切换后策略不干预,data-ai-user-toggled)。
|
|
246
|
+
- **kind 注册表与 normalize 适配层**:组件只消费归一化 `AiToolCallModel` / `AiUsage` / `AiContentPart`,业界格式(OpenAI / Anthropic / AI SDK / AG-UI / MCP / 素朴)由 `normalizeToolCall` / `normalizeUsage` / `normalizeContentParts` 翻译;内置 kinds(shell/read/edit/write/rm/grep/glob/browser/search/fetch/mcp/skill/todo/plan/subagent/note),第三方 `registerAiKind()` 扩展;`inferKind` 从工具名推断。
|
|
247
|
+
- **标准化内容模型 `AiContent`**(v0.7.1,spec §10):`string | AiContentPart[]`,部件族 text / image / audio / video / file / resource-link(url XOR data+base64 约定);一套 parts 三处共用——renderAiMessage 消息渲染(多模态 + `.ai-msg--error` 错误变体 + `stream()` 流式节点)、ai-tool 的 IO 区(MCP content 数组零改动进卡片,`normalizeContentParts` 兼容 MCP 的 `resource_link` 下划线形态)、ai-provider 传输(两族 wire 映射 + anthropic `cache_control` + role 'tool' 回灌)。估算器 `estimateTokens`(CJK ×0.6 + 其余 ÷4 + 媒体经验值)与 `contextEstimate`(下一轮上下文口径)在 ai-core;**`contextEstimate`(环口径)与 `auditor.summary()`(累计计费口径)语义不同,禁止混喂同一个环**。
|
|
248
|
+
- **事件族 `icen:ai-*`**(全部 bubbles):send / stop / queue / dequeue / attach(钮选/粘贴/拖放三入口)/ copy / retry / approve / reject / diff-accept / diff-reject / todo-toggle / toggle / model-change / command / ref / usage(请求归一 usage)/ done(请求收尾:status/provider/model/usage/cost/error/durationMs/ttftMs——绑定层驱动事件)/ audit-clear;detail 见规格 §5。
|
|
249
|
+
- **`data-density` 三档**:verbose / normal / summary(summary 隐藏 meta、默认折叠 reasoning、压淡 tool/system 消息,纯 CSS)。
|
|
250
|
+
- **ai-usage 双形态**:分段条 `renderAiUsage` + 上下文窗口环形指示器 `renderAiUsageRing`(点击弹出完整分解,弹层复用 popover;状态档 <60% accent / 60–85% warning / >85% error+脉冲)。配套 `renderAiAudit` 审计面板(totals + byModel + 最近条目,清除钮派 `icen:ai-audit-clear`);`ai-context` 抽屉的 `model.audit` 自动渲染审计节(紧凑形态)。
|
|
251
|
+
- **ai-composer v2**(业界集大成):底部工具条 = 模型切换(多 provider 分组 + 搜索 + 缓存失效提示,选中模型 context 自动喂给环的 total;无 context 显式清除不留陈旧值)+ 上下文环挂载点 + 附件 + 发送/停止;`/` 命令弹层(拦截执行不进消息流);`@` 引用弹层(chip 分离渲染,textarea 不嵌 chip);空输入 `↑` 取回历史,运行中 `↑` 取回排队消息。附件三入口:钮选 / textarea 粘贴文件 / box 拖放(`.is-dragover` 高亮)。API:setComposerModels / setComposerCommands / setComposerRefs / setComposerUsage / bindComposer。
|
|
252
|
+
- **绑定层 `bindComposer`**(v0.7.1,spec §11,「零接线全链路」):事件驱动把 composer ↔ 消息区(renderAiMessage)↔ client(stream + 停止 + 排队自动续发 + 错误 → setError/fail)↔ 用量环(`usage.from: 'context'` 默认正确口径 | `'billing'` | AiAuditor)接成闭环;运行态自动管理(send→running / icen:ai-done→解除);不传 client 为纯状态绑定(渐进采用);重复绑定 WeakSet 防护,unbind 解绑。
|
|
253
|
+
- **ai-provider 适配层**(`src/behaviors/ai-provider.ts`,无 UI 不占 slug):注册表内置 openai/claude/deepseek/glm/kimi 五家(baseURL 可自定义,models 携带定价表——`estimateCost` 未命中返回 undefined 不猜价),`createAiClient` 填 key 即用(chat + stream 双族线协议,多模态 parts 传输:openai 族 image_url/input_audio/file_id、anthropic 族 image/audio/document(pdf) + `cacheControl` 断点 + role 'tool' 的 tool_result 回灌;video 两族诚实报 unsupported),usage 走 normalizeUsage 归一(含各家缓存字段),`createAiAuditor` 环形审计(条目含 cost 定价估算 + ttftMs 首 token 延迟;`summary()` 喂环 / `totals()` 喂审计面板 / `byModel()` 分组),每次请求派 `icen:ai-usage` 与 `icen:ai-done` 事件。浏览器直连:claude 官方支持(自动带直连头),openai 官方禁止,其余三家建议代理(browserDirect 字段诚实标注)。
|
|
254
|
+
- **AI 工具体系(v0.8,`src/behaviors/ai-tools.ts`,不绑定 slug)**:UI 能力注册为模型可调用的 AiToolDef(内置 render_chart,inputSchema=ChartSpec);createAiToolArea 挂载区三层控制(不 import=无 / 白名单+max LRU / 运行时 setTools·setMax·clear);模型侧导出 aiToolsToOpenAI/aiToolsToMcp/aiToolsManifest + parseAiToolArgs;工具回执内嵌图(**仅 output**;input 是机器参数保持 JSON)→ renderAiToolCall IO 区直接 renderChart;待办业界模式:TodoWrite 折叠卡(todo kind summarize x/y)+ `setComposerTodo` 输入区实时 chip(全完成/有失败图标反馈,点击弹层看清单);事件 icen:ai-tool-call/-result/-evict。规格 §13。
|
|
255
|
+
- **ai-core 不绑定任何 slug**:demo/消费方要用 `normalizeToolCall` 等时,在该页 behaviors 数组里显式加 `'ai-core'`(boot 的 glob 会加载并注入 `aiCoreMod` 参数)。
|
|
256
|
+
- **宽度自稳定契约**:所有 AI 输出容器根(`.ai-chat/.ai-reasoning/.ai-composer/.ai-item/.ai-tool/.ai-subagent/.ai-diff/.ai-files/.ai-todo/.ai-usage`)声明 `align-self: stretch`——在 flex/grid 居中宿主(shrink-wrap 上下文,如文档站 `.demo-stage`)里不随内容多少改变宽度,流式内容只允许纵向生长。**禁用 `width: 100%`** 做这件事(在 shrink-wrap 父级下会塌成 min-content)。消费方给组件包一层自己的布局 wrapper 时,wrapper 的宽度由消费方负责(demo 里直接 `style="width:100%"`)。
|
package/README.md
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
# @icen.ai/ui — Icen Design System
|
|
2
2
|
|
|
3
|
-
icen.ai 全生态的统一设计系统:**设计 tokens(6 色彩预设 × 明暗双模 × 3 风格 profile)+ 无框架组件 CSS(
|
|
3
|
+
icen.ai 全生态的统一设计系统:**设计 tokens(6 色彩预设 × 明暗双模 × 3 风格 profile)+ 无框架组件 CSS(56 组件)+ 行为 JS(33 behaviors)+ AI 原生组件族 + 图表通用层 + AI 工具体系**。工程形态与 `@icen.ai/cli` 一致(Bun + TS + tsup + ESM + MIT)。
|
|
4
4
|
|
|
5
5
|
- 色彩预设:`clay`(默认,陶土橙 × 纸白 = icen 品牌)/ `piano` / `art` / `vangogh` / `ink` / `retro`,各含 `.dark` 变体
|
|
6
6
|
- 风格配置:`.style-modern` / `.style-retro` / `.style-terminal`(几何 / 密度 / 动效 / 字体 token,与色彩正交)
|
|
7
7
|
- 可选效果层:`retro-effects.css`(CRT 扫描线 / 颗粒 / 像素边框 / 打字机光标 —— 仅 `.style-retro` 激活时生效,按需 import)
|
|
8
|
-
- 组件:btn / form / input / select / slider / switch / tag-input / upload / modal / popover / tooltip / menu(含 dropdown + context-menu)/ tabs / nav / sidebar / breadcrumb / pagination / steps / segmented / accordion / tree / carousel / charts / table / stat / card / panel / pill / empty / feedback / content / media(
|
|
9
|
-
-
|
|
8
|
+
- 组件:btn / form / input / select / slider / switch / tag-input / upload / modal / popover / tooltip / menu(含 dropdown + context-menu)/ tabs / nav / sidebar / breadcrumb / pagination / steps / segmented / accordion / tree / carousel / charts / table / stat / card / panel / pill / tag / badge / empty / feedback / content / media / toolbar / split-pane / date-picker / command-palette / notification / copy / scroll-area / back-top / datatable / layout(56 个组件 CSS,含 charts 基座 + 11 个细分图表 kit,贡献日历样式并入基座)
|
|
9
|
+
- 图表通用层(v0.8):`renderChart(el, spec)` 统一入口——纯 JSON 规格(type 缺省自动推断;data[] + dims 任意维度透视)、多系列折线/分组堆叠柱、内置 tooltip、`icen:chart-hover/click/dblclick/contextmenu` 交互事件族、图例点击切换系列;新增贡献日历 renderCalendar(GitHub 同款)与散点气泡 renderScatter
|
|
10
|
+
- AI 原生组件族(v0.7,11 个 slug):ai-chat(会话容器)/ ai-message(消息行)/ ai-reasoning(推理块)/ ai-composer(输入台)/ ai-tool-call(工具调用卡)/ ai-subagent(子智能体卡)/ ai-diff(差异审阅)/ ai-files(文件标签)/ ai-todo(任务清单)/ ai-context(上下文抽屉)/ ai-usage(用量条)——共享 7 态状态机与 kind 注册表,事件统一 `icen:ai-*` 前缀
|
|
11
|
+
- 标准化内容模型 `AiContent`(v0.7.1,对标 AI SDK v5 parts / MCP / OpenAI / Anthropic,2026-10 调研):一套 `AiContentPart[]`(text / image / audio / video / file / resource-link)表达任意输入——消息渲染(renderAiMessage 多模态 + 错误变体)、工具回执(MCP content 数组零改动进卡片)、传输层(parts → 两族 wire,含 anthropic `cache_control` 与 tool_result 回灌)全部只认这一套;`normalizeContentParts` 一函数归一四族来源,`estimateTokens` / `contextEstimate` 提供粗估与上下文口径
|
|
12
|
+
- ai-provider 适配层(v0.7.1 增强):五家厂商注册表(含定价表,`estimateCost` 未命中不猜价)、chat + stream 双族线协议多模态传输、审计闭环(`createAiAuditor` 条目含 cost 定价估算与 ttftMs 首 token 延迟,`renderAiAudit` 面板 + `ai-context` 抽屉审计节)、请求收尾派 `icen:ai-done`(status/usage/cost/error/durationMs/ttftMs)
|
|
13
|
+
- 绑定层 `bindComposer`(v0.7.1,「零接线全链路」):一行把 composer ↔ 消息区(renderAiMessage)↔ client(stream + 停止 + 排队续发 + 错误路径)↔ 上下文环(`usage.from: 'context'` 正确口径 / `'billing'` 计费口径 / auditor)接成闭环;不传 client 为纯状态绑定(渐进采用);附件三入口(钮选 / 粘贴 / 拖放)
|
|
14
|
+
- AI 工具体系(v0.8,`ai-tools`):UI 能力注册为模型可调用的工具(内置 `render_chart` 吃 ChartSpec 纯 JSON);`createAiToolArea` 挂载区三层控制(白名单 / max LRU / 运行时调节);模型侧 `aiToolsToOpenAI()` 直接给 tools 参数、`aiToolsManifest()` 贴 system prompt;工具回执内嵌图(output 为 chart spec → 工具卡展开区直接渲染)
|
|
15
|
+
- 行为:theme / tabs / toast / copy / input / select / slider / tag-input / upload / modal / dropdown / popover / context-menu / accordion / tree / carousel / charts / nav / sidebar / datatable / notification / back-top / command-palette / date-picker / split-pane / ai-core / ai-chat / ai-composer / ai-tool / ai-provider / ai-diff / ai-panel / ai-tools(33 个,全部 SSR 守卫 + 幂等 init + textContent-only)
|
|
10
16
|
|
|
11
17
|
## 使用
|
|
12
18
|
|
|
@@ -29,15 +35,38 @@ bun add @icen.ai/ui
|
|
|
29
35
|
import {
|
|
30
36
|
initTheme, toggleDark, setTheme,
|
|
31
37
|
initTabs, initCopy, initInput, initSelect, initSlider, initTagInput,
|
|
32
|
-
initUpload, initModal, initDropdown,
|
|
33
|
-
initAccordion, initTree, initCarousel,
|
|
38
|
+
initUpload, initModal, initDropdown, initContextMenu,
|
|
39
|
+
initAccordion, initTree, initCarousel, initNav, initSidebar,
|
|
40
|
+
openPopover, closePopover,
|
|
41
|
+
renderLine, renderVBar,
|
|
34
42
|
toast,
|
|
35
43
|
} from '@icen.ai/ui';
|
|
36
44
|
|
|
37
45
|
initTheme(); // 尽早调用(或内联同步脚本防闪烁,见 accounts Base.astro)
|
|
38
|
-
initTabs(); // [data-tabs] > [data-tab] + [data-tab-panel]
|
|
46
|
+
initTabs(); // [data-tabs] > [data-tab] + [data-tab-panel] 契约;切换派发 icen:tab-change
|
|
39
47
|
initCopy(); // .copy-btn[data-copy] 委托
|
|
40
|
-
toast.ok('已保存'); // 或 toast.action('已删除', '撤销', undo)
|
|
48
|
+
toast.ok('已保存'); // 或 toast.action('已删除', '撤销', undo);类名 .toast--success 等
|
|
49
|
+
renderLine(el, { labels, series }); // 函数式组件(charts / datatable):无 init,直接渲染
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
AI 全链路(零接线):
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { initAiChat } from '@icen.ai/ui/kit/ai-chat';
|
|
56
|
+
import { initAiComposer, bindComposer } from '@icen.ai/ui/kit/ai-composer';
|
|
57
|
+
import { createAiClient, createAiAuditor } from '@icen.ai/ui';
|
|
58
|
+
|
|
59
|
+
initAiChat();
|
|
60
|
+
initAiComposer();
|
|
61
|
+
const auditor = createAiAuditor({ persist: 'my-audit' });
|
|
62
|
+
const client = createAiClient({ provider: 'kimi', apiKey, auditor });
|
|
63
|
+
|
|
64
|
+
// 一行接通:发送 → 消息渲染 → 流式追加 → 停止/排队续发/错误路径 → 上下文环(正确口径)
|
|
65
|
+
const binding = bindComposer(composer, {
|
|
66
|
+
client,
|
|
67
|
+
messages: chatEl.querySelector('.ai-chat-scroll'),
|
|
68
|
+
usage: { from: 'context' }, // 或 'billing' / auditor
|
|
69
|
+
});
|
|
41
70
|
```
|
|
42
71
|
|
|
43
72
|
## 文档站(已上线 <https://ui.icen.ai>)
|
|
@@ -55,10 +84,10 @@ Pages 项目 `icen-ui`(aidoll 账号),自定义域 `ui.icen.ai` → CNAME
|
|
|
55
84
|
|
|
56
85
|
```bash
|
|
57
86
|
bun install
|
|
58
|
-
bun run build # tsup(behaviors → dist/*.mjs
|
|
87
|
+
bun run build # tsup(behaviors → dist/*.mjs + .d.mts 类型面)+ scripts/dts-ext.mjs + scripts/build-css.ts(css → dist/)
|
|
59
88
|
```
|
|
60
89
|
|
|
61
|
-
产物:`dist/tokens.css`(colors + style-profiles + typography 拼合)、`dist/ui.css`(tokens+base+组件)、`dist/base.css`、`dist/retro-effects.css`(按需)、`dist/tokens/*`、`dist/components/*`、`dist/behaviors/*.mjs
|
|
90
|
+
产物:`dist/tokens.css`(colors + style-profiles + typography 拼合)、`dist/ui.css`(tokens+base+组件)、`dist/base.css`、`dist/retro-effects.css`(按需)、`dist/tokens/*`、`dist/components/*`、`dist/behaviors/*.mjs` + `.d.mts`(34 个声明,JSDoc 约束注释存活)、`dist/index.mjs`、`dist/registry.json`(含 tokens/tokensExtras/base/components/behaviors 清单)。
|
|
62
91
|
|
|
63
92
|
## 目录
|
|
64
93
|
|
|
@@ -71,12 +100,17 @@ src/tokens/retro-effects.css # 可选效果层:CRT/扫描线/像素工具类
|
|
|
71
100
|
src/base.css # 元素基线 + 滚动条 + z-index 标尺 + 6 个交互基元
|
|
72
101
|
# .pressable / .control / .field / .lift / .surface-elevated / .focus-ring
|
|
73
102
|
# + a11y 工具类 .sr-only / .skip-link + 工具类 .mono/.dim/.faint/.numeric
|
|
74
|
-
src/components/*.css #
|
|
75
|
-
src/behaviors/*.ts #
|
|
103
|
+
src/components/*.css # 56 个无框架组件 CSS(含 4 个 AI 族合并文件)
|
|
104
|
+
src/behaviors/*.ts # 33 个 behaviors(含 8 个 AI 族)
|
|
76
105
|
scripts/build-css.ts # CSS 产物构建(拼合 + registry.json)
|
|
77
106
|
site/ # 文档站骨架(ui.icen.ai,独立 Astro 应用)
|
|
78
107
|
```
|
|
79
108
|
|
|
80
|
-
##
|
|
109
|
+
## 消费方接入
|
|
110
|
+
|
|
111
|
+
`@icen.ai/ui` 已发布 npm(OIDC trusted publishing,打 `v*` tag 即自动发版)。两种消费方式均可:
|
|
112
|
+
|
|
113
|
+
- **包引入(推荐)**:`bun add @icen.ai/ui`,按 `./tokens.css` / `./ui.css` / `./kit/<slug>` 引入。
|
|
114
|
+
- **vendor 复制**:accounts 目前走这条路——`bun run sync:ui`(accounts 仓库内)把本仓库 `dist/` 复制到 `src/styles/vendor/icen/`,布局里 import 对应 CSS/JS。适合需要锁定版本或离线构建的场景。
|
|
81
115
|
|
|
82
|
-
|
|
116
|
+
给 AI 代理的文档层:npm 包内 `AGENTS.md`(消费方速查)+ `dist/**/*.d.mts`(类型真相);站点 <https://ui.icen.ai/llms.txt>(索引)与 `/llms-full.txt`(全文 + 类型面快照);场景配方 skill `icen-ui`(skill.icen.ai)。
|
package/dist/base.css
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* ════════════════════════════════════════════════════════════════════════
|
|
8
8
|
* icen 组件契约(统一抽象)
|
|
9
9
|
* ════════════════════════════════════════════════════════════════════════
|
|
10
|
-
* 所有
|
|
10
|
+
* 所有 51 个组件遵循同一套抽象,由两层共同支撑:
|
|
11
11
|
*
|
|
12
12
|
* ── 层 1:--icen-* 原语 token(所有组件自洽消费)
|
|
13
|
-
* --icen-ring / --icen-ring-soft / --icen-ring-error
|
|
13
|
+
* --icen-ring / --icen-ring-soft / --icen-ring-error /
|
|
14
|
+
* --icen-ring-warning / --icen-ring-inset ← 聚焦环
|
|
14
15
|
* --icen-hover-border ← 悬停边框过渡
|
|
15
16
|
* --icen-disabled-opacity ← 禁用透明度
|
|
16
17
|
* --icen-press ← 按下形变
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
* - 滚动条:贴 --token-scrollbar-*(retro profile 另有硬覆盖)
|
|
57
58
|
* - 工具类:.mono / .dim / .faint / .sr-only / .skip-link
|
|
58
59
|
* - z-index 标尺:--z-* (浮层一律 portal 到 body)
|
|
60
|
+
* - 共享 keyframes:icen-pop-in(弹层进场)/ icen-spin(旋转)/ icen-skeleton-pulse(骨架脉冲)
|
|
59
61
|
* - 交互基元 class:.pressable / .control / .field / .lift / .surface-elevated / .focus-ring / .motion
|
|
60
62
|
* - 状态约定:[aria-disabled] / [aria-busy] / [data-loading] / [data-disabled] / [data-invalid]
|
|
61
63
|
*/
|
|
@@ -65,7 +67,7 @@
|
|
|
65
67
|
───────────────────────────────────────────────────────────────
|
|
66
68
|
所有组件消费的"原语级"变量:聚焦环、悬停、按下、禁用、过渡。
|
|
67
69
|
风格 profile(如 .style-retro)只需覆盖这里的少数变量,
|
|
68
|
-
即可让
|
|
70
|
+
即可让 51 个组件整体换肤;组件自身无需感知风格切换。
|
|
69
71
|
══════════════════════════════════════════════════════════════ */
|
|
70
72
|
:root {
|
|
71
73
|
/* ── 聚焦环(focus-visible) ── */
|
|
@@ -73,6 +75,9 @@
|
|
|
73
75
|
--icen-ring-soft: 0 0 0 3px color-mix(in srgb, var(--token-accent) 16%, transparent);
|
|
74
76
|
--icen-ring-error: 0 0 0 2px var(--token-bg), 0 0 0 4px color-mix(in srgb, var(--token-error) 50%, transparent);
|
|
75
77
|
--icen-ring-success: 0 0 0 2px var(--token-bg), 0 0 0 4px color-mix(in srgb, var(--token-success) 50%, transparent);
|
|
78
|
+
--icen-ring-warning: 0 0 0 2px var(--token-bg), 0 0 0 4px color-mix(in srgb, var(--token-warning) 50%, transparent);
|
|
79
|
+
/* inset 聚焦环:空间受限的网格/表头单元格用内阴影聚焦(2px 内描边 + 3px 内光晕) */
|
|
80
|
+
--icen-ring-inset: inset 0 0 0 2px color-mix(in srgb, var(--token-accent) 50%, transparent), inset 0 0 0 5px color-mix(in srgb, var(--token-accent) 14%, transparent);
|
|
76
81
|
|
|
77
82
|
/* ── 悬停 / 按下 / 禁用 ── */
|
|
78
83
|
--icen-hover-border: color-mix(in srgb, var(--token-accent) 30%, var(--token-line));
|
|
@@ -251,7 +256,8 @@ html {
|
|
|
251
256
|
banner → 全屏横幅(紧急公告,高于 toast)
|
|
252
257
|
dialog → 模态对话框 + backdrop
|
|
253
258
|
popover → 弹出层/下拉菜单(高于 dialog 因为常常 portal 出去)
|
|
254
|
-
tooltip →
|
|
259
|
+
tooltip → 工具提示
|
|
260
|
+
fx → 效果层(retro 扫描线/噪点等,永远在最上)
|
|
255
261
|
══════════════════════════════════════════════════════════════ */
|
|
256
262
|
:root {
|
|
257
263
|
--z-base: 0;
|
|
@@ -263,6 +269,27 @@ html {
|
|
|
263
269
|
--z-dialog: 500;
|
|
264
270
|
--z-popover: 620;
|
|
265
271
|
--z-tooltip: 700;
|
|
272
|
+
--z-fx: 1000;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* ══════════════════════════════════════════════════════════════
|
|
276
|
+
共享 keyframes(组件统一引用,不再各自重复定义)
|
|
277
|
+
icen-pop-in → 弹层进场(scale .96 + translateY -4px)
|
|
278
|
+
icen-spin → 旋转(spinner / 加载态)
|
|
279
|
+
icen-skeleton-pulse → 骨架脉冲
|
|
280
|
+
注:menu.css / popover.css 保留自己的 scale(.95)±8px 四方向版
|
|
281
|
+
(方向语义不同:按 data-side 反向位移),不并入 icen-pop-in。
|
|
282
|
+
══════════════════════════════════════════════════════════════ */
|
|
283
|
+
@keyframes icen-pop-in {
|
|
284
|
+
from { opacity: 0; transform: scale(0.96) translateY(-4px); }
|
|
285
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
286
|
+
}
|
|
287
|
+
@keyframes icen-spin {
|
|
288
|
+
to { transform: rotate(360deg); }
|
|
289
|
+
}
|
|
290
|
+
@keyframes icen-skeleton-pulse {
|
|
291
|
+
0%, 100% { opacity: 1; }
|
|
292
|
+
50% { opacity: 0.5; }
|
|
266
293
|
}
|
|
267
294
|
|
|
268
295
|
/* ══════════════════════════════════════════════════════════════
|
|
@@ -42,11 +42,12 @@ function setup(container) {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
function initAccordion(root
|
|
45
|
+
function initAccordion(root) {
|
|
46
46
|
if (typeof document === "undefined") return;
|
|
47
|
+
const scope = root ?? document;
|
|
47
48
|
const containers = [];
|
|
48
|
-
if (
|
|
49
|
-
containers.push(...Array.from(
|
|
49
|
+
if (scope instanceof Element && scope.matches(".accordion")) containers.push(scope);
|
|
50
|
+
containers.push(...Array.from(scope.querySelectorAll(".accordion")));
|
|
50
51
|
for (const c of containers) setup(c);
|
|
51
52
|
}
|
|
52
53
|
export {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { AiContent, AiContentPart } from './ai-core.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 初始化 root 下的全部 .ai-chat(root 自身匹配时包含自身)。
|
|
5
|
+
* 幂等;返回销毁函数:移除监听、断开观察器并复位幂等标记(可重新 init)。
|
|
6
|
+
*/
|
|
7
|
+
declare function initAiChat(root?: ParentNode): () => void;
|
|
8
|
+
interface AiStreamHandle {
|
|
9
|
+
/** textContent 级追加一段文本(不解析 HTML) */
|
|
10
|
+
append(text: string): void;
|
|
11
|
+
/** 流正常结束:移除 .is-streaming / aria-busy;reasoning 自动折叠 + 回填耗时 */
|
|
12
|
+
done(): void;
|
|
13
|
+
/** 流被取消:同 done,但不折叠 reasoning、不计耗时 */
|
|
14
|
+
cancel(): void;
|
|
15
|
+
/** 流失败:终止追加并给宿主挂 .is-error(错误文本展示由消费方/renderAiMessage.setError 负责) */
|
|
16
|
+
fail(): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 创建流式追加句柄:追加期间宿主挂 .is-streaming(aria-busy=true),
|
|
20
|
+
* 完成/取消移除。宿主在 .ai-reasoning 内时同步驱动 reasoning 状态:
|
|
21
|
+
* 流式中根挂 .is-streaming(CSS shimmer),done() 自动折叠并显示耗时。
|
|
22
|
+
*/
|
|
23
|
+
declare function createAiStream(el: HTMLElement): AiStreamHandle;
|
|
24
|
+
interface AiMessageModel {
|
|
25
|
+
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
26
|
+
/** 标准化内容:字符串(纯文本)或部件数组(文字/图片/音频/视频/文件/资源链接) */
|
|
27
|
+
content: AiContent;
|
|
28
|
+
/** 元信息行("12:04 · 1.2k tok") */
|
|
29
|
+
meta?: string;
|
|
30
|
+
/** assistant 消息的模型名标签 */
|
|
31
|
+
model?: string;
|
|
32
|
+
/** 错误态:消息挂 .ai-msg--error + 错误文本行 */
|
|
33
|
+
error?: string;
|
|
34
|
+
/** 初始即流式态(正文末文本 span 挂 .is-streaming) */
|
|
35
|
+
streaming?: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface AiMessageHandle {
|
|
38
|
+
/** 消息行元素(.ai-msg) */
|
|
39
|
+
el: HTMLElement;
|
|
40
|
+
/** 正文容器(.ai-msg-body):消费方追加自定义内容的挂载点 */
|
|
41
|
+
body: HTMLElement;
|
|
42
|
+
setMeta(meta: string): void;
|
|
43
|
+
setError(message: string): void;
|
|
44
|
+
/** 在正文末开一个流式文本节点,返回 createAiStream 句柄 */
|
|
45
|
+
stream(): AiStreamHandle;
|
|
46
|
+
}
|
|
47
|
+
/** 单个内容部件 → DOM 节点(文本一律 textContent;媒体 src 走 aiContentUrl 归一)。
|
|
48
|
+
* 导出供 ai-tool 的 IO 区复用(MCP 多模态回执;类名 .ai-msg-* 语义即「消息部件」,全局生效)。 */
|
|
49
|
+
declare function renderAiContentPart(part: AiContentPart): HTMLElement | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* DOM API 渲染一条消息(append 到容器末尾):多角色变体 + 多模态部件 + copy/retry
|
|
52
|
+
* 动作钮(委托归 initAiChat)+ meta/model 标签 + 错误变体。返回句柄可继续
|
|
53
|
+
* setMeta / setError / stream()。滚动跟随由 initAiChat 的钉底机制自动处理。
|
|
54
|
+
*/
|
|
55
|
+
declare function renderAiMessage(scrollEl: HTMLElement, model: AiMessageModel): AiMessageHandle;
|
|
56
|
+
|
|
57
|
+
export { type AiMessageHandle, type AiMessageModel, type AiStreamHandle, createAiStream, initAiChat, renderAiContentPart, renderAiMessage };
|