@icen.ai/ui 0.5.0 → 0.8.1

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.
Files changed (151) hide show
  1. package/AGENTS.md +262 -0
  2. package/README.md +52 -13
  3. package/dist/base.css +33 -5
  4. package/dist/behaviors/accordion.d.mts +7 -0
  5. package/dist/behaviors/accordion.mjs +37 -11
  6. package/dist/behaviors/ai-chat.d.mts +59 -0
  7. package/dist/behaviors/ai-chat.mjs +580 -0
  8. package/dist/behaviors/ai-composer.d.mts +137 -0
  9. package/dist/behaviors/ai-composer.mjs +2114 -0
  10. package/dist/behaviors/ai-core.d.mts +156 -0
  11. package/dist/behaviors/ai-core.mjs +504 -0
  12. package/dist/behaviors/ai-diff.d.mts +53 -0
  13. package/dist/behaviors/ai-diff.mjs +413 -0
  14. package/dist/behaviors/ai-panel.d.mts +101 -0
  15. package/dist/behaviors/ai-panel.mjs +1087 -0
  16. package/dist/behaviors/ai-provider.d.mts +198 -0
  17. package/dist/behaviors/ai-provider.mjs +1061 -0
  18. package/dist/behaviors/ai-tool.d.mts +26 -0
  19. package/dist/behaviors/ai-tool.mjs +2173 -0
  20. package/dist/behaviors/ai-tools.d.mts +84 -0
  21. package/dist/behaviors/ai-tools.mjs +1762 -0
  22. package/dist/behaviors/back-top.d.mts +20 -0
  23. package/dist/behaviors/back-top.mjs +64 -32
  24. package/dist/behaviors/carousel.d.mts +7 -0
  25. package/dist/behaviors/carousel.mjs +72 -27
  26. package/dist/behaviors/charts.d.mts +296 -0
  27. package/dist/behaviors/charts.mjs +1364 -440
  28. package/dist/behaviors/command-palette.d.mts +42 -0
  29. package/dist/behaviors/command-palette.mjs +167 -27
  30. package/dist/behaviors/context-menu.d.mts +53 -0
  31. package/dist/behaviors/context-menu.mjs +127 -39
  32. package/dist/behaviors/controls.d.mts +59 -0
  33. package/dist/behaviors/controls.mjs +601 -0
  34. package/dist/behaviors/copy.d.mts +7 -0
  35. package/dist/behaviors/copy.mjs +41 -15
  36. package/dist/behaviors/datatable.d.mts +90 -0
  37. package/dist/behaviors/datatable.mjs +279 -53
  38. package/dist/behaviors/date-picker.d.mts +4 -0
  39. package/dist/behaviors/date-picker.mjs +276 -69
  40. package/dist/behaviors/dropdown.d.mts +8 -0
  41. package/dist/behaviors/dropdown.mjs +171 -28
  42. package/dist/behaviors/events.d.mts +330 -0
  43. package/dist/behaviors/events.mjs +208 -0
  44. package/dist/behaviors/input.d.mts +4 -0
  45. package/dist/behaviors/input.mjs +52 -32
  46. package/dist/behaviors/modal.d.mts +30 -0
  47. package/dist/behaviors/modal.mjs +87 -16
  48. package/dist/behaviors/nav.d.mts +7 -0
  49. package/dist/behaviors/nav.mjs +57 -15
  50. package/dist/behaviors/notification.d.mts +188 -0
  51. package/dist/behaviors/notification.mjs +274 -140
  52. package/dist/behaviors/popover.d.mts +82 -0
  53. package/dist/behaviors/popover.mjs +92 -5
  54. package/dist/behaviors/select.d.mts +6 -0
  55. package/dist/behaviors/select.mjs +287 -45
  56. package/dist/behaviors/sidebar.d.mts +7 -0
  57. package/dist/behaviors/sidebar.mjs +44 -13
  58. package/dist/behaviors/slider.d.mts +7 -0
  59. package/dist/behaviors/slider.mjs +55 -43
  60. package/dist/behaviors/split-pane.d.mts +8 -0
  61. package/dist/behaviors/split-pane.mjs +70 -29
  62. package/dist/behaviors/tabs.d.mts +9 -0
  63. package/dist/behaviors/tabs.mjs +102 -26
  64. package/dist/behaviors/tag-input.d.mts +7 -0
  65. package/dist/behaviors/tag-input.mjs +66 -17
  66. package/dist/behaviors/theme.d.mts +36 -0
  67. package/dist/behaviors/theme.mjs +51 -5
  68. package/dist/behaviors/toast.d.mts +48 -0
  69. package/dist/behaviors/toast.mjs +98 -25
  70. package/dist/behaviors/tree.d.mts +7 -0
  71. package/dist/behaviors/tree.mjs +39 -15
  72. package/dist/behaviors/upload.d.mts +4 -0
  73. package/dist/behaviors/upload.mjs +79 -44
  74. package/dist/cli.mjs +1 -1
  75. package/dist/components/ai-chat.css +1037 -0
  76. package/dist/components/ai-chat.mjs +3 -0
  77. package/dist/components/ai-composer.mjs +3 -0
  78. package/dist/components/ai-context.mjs +2 -0
  79. package/dist/components/ai-diff.css +311 -0
  80. package/dist/components/ai-diff.mjs +3 -0
  81. package/dist/components/ai-files.mjs +3 -0
  82. package/dist/components/ai-message.mjs +3 -0
  83. package/dist/components/ai-panel.css +682 -0
  84. package/dist/components/ai-reasoning.mjs +3 -0
  85. package/dist/components/ai-subagent.mjs +2 -0
  86. package/dist/components/ai-todo.mjs +2 -0
  87. package/dist/components/ai-tool-call.mjs +2 -0
  88. package/dist/components/ai-tool.css +532 -0
  89. package/dist/components/ai-usage.mjs +3 -0
  90. package/dist/components/badge.css +5 -9
  91. package/dist/components/breadcrumb.css +0 -36
  92. package/dist/components/btn.css +4 -7
  93. package/dist/components/card.css +3 -6
  94. package/dist/components/chart-bar.css +10 -0
  95. package/dist/components/chart-calendar.mjs +4 -0
  96. package/dist/components/chart-gauge.css +1 -1
  97. package/dist/components/chart-heatmap.css +40 -0
  98. package/dist/components/chart-pie.css +0 -6
  99. package/dist/components/chart-scatter.css +24 -0
  100. package/dist/components/chart-scatter.mjs +4 -0
  101. package/dist/components/charts.css +87 -2
  102. package/dist/components/charts.mjs +1 -0
  103. package/dist/components/command-palette.css +2 -2
  104. package/dist/components/content.css +11 -25
  105. package/dist/components/copy.css +8 -8
  106. package/dist/components/datatable.css +5 -9
  107. package/dist/components/date-picker.css +4 -8
  108. package/dist/components/empty.css +0 -4
  109. package/dist/components/feedback.css +10 -17
  110. package/dist/components/form.css +13 -14
  111. package/dist/components/input.css +2 -2
  112. package/dist/components/layout.css +36 -36
  113. package/dist/components/media.css +12 -8
  114. package/dist/components/menu.css +2 -2
  115. package/dist/components/modal.css +1 -1
  116. package/dist/components/nav.css +11 -6
  117. package/dist/components/notification.css +27 -28
  118. package/dist/components/pagination.css +4 -2
  119. package/dist/components/pagination.mjs +1 -0
  120. package/dist/components/panel.css +16 -16
  121. package/dist/components/pill.css +15 -15
  122. package/dist/components/rating.mjs +1 -0
  123. package/dist/components/segmented.css +14 -6
  124. package/dist/components/segmented.mjs +1 -0
  125. package/dist/components/select.css +79 -24
  126. package/dist/components/sidebar.css +1 -1
  127. package/dist/components/split-pane.css +0 -1
  128. package/dist/components/stat.css +14 -18
  129. package/dist/components/steps.css +15 -5
  130. package/dist/components/steps.mjs +1 -0
  131. package/dist/components/switch.css +11 -13
  132. package/dist/components/switch.mjs +1 -0
  133. package/dist/components/table.css +46 -47
  134. package/dist/components/table.mjs +1 -0
  135. package/dist/components/tabs.css +57 -45
  136. package/dist/components/tag-input.css +5 -10
  137. package/dist/components/toast.css +16 -13
  138. package/dist/components/tooltip.css +2 -2
  139. package/dist/components/upload.css +0 -15
  140. package/dist/index.d.mts +39 -0
  141. package/dist/index.mjs +11243 -3312
  142. package/dist/registry.json +147 -9
  143. package/dist/retro-effects.css +23 -32
  144. package/dist/tokens/colors.css +4 -1
  145. package/dist/tokens/retro-effects.css +23 -32
  146. package/dist/tokens/style-profiles.css +24 -2
  147. package/dist/tokens/typography.css +2 -0
  148. package/dist/tokens.css +30 -3
  149. package/dist/ui.css +3420 -645
  150. package/docs/spec/ai-native.md +662 -0
  151. package/package.json +15 -5
@@ -28,7 +28,15 @@
28
28
  * …
29
29
  * </div>
30
30
  *
31
- * segmented/toggle 是纯 CSS 状态组件(.is-active);stepper/otp 由 behaviors/input.ts 接线。
31
+ * segmented / stepper 由 behaviors/controls.ts 接线(initSegmented / initStepper,
32
+ * 派 icen:segmented-change / icen:stepper-change);otp 由 behaviors/input.ts 的
33
+ * initInput 接线;toggle-group 为纯 CSS 状态组件(.is-active,使用方自行驱动)。
34
+ *
35
+ * 增强段(正文"增强"段):
36
+ * 变体:segmented--block / segmented--ghost / toggle-group--block / toggle-group--ghost
37
+ * 槽位:stepper-unit(步进器单位后缀)/ otp-separator(OTP 分组分隔符)
38
+ * 状态类(消费方施加):.otp.is-complete(behavior 接线中)/ .otp.is-error /
39
+ * .segmented.is-error / .segmented.is-disabled / .toggle-group.is-error / .toggle-group.is-disabled
32
40
  */
33
41
 
34
42
  /* ── 分段控件(排他单选) ── */
@@ -72,7 +80,7 @@
72
80
  .segmented-item.is-active,
73
81
  .segmented-item[aria-checked='true'] {
74
82
  background: var(--token-accent);
75
- color: var(--token-on-accent, #fff);
83
+ color: var(--token-on-accent);
76
84
  box-shadow: var(--token-shadow-elevated);
77
85
  }
78
86
  .segmented-item:disabled,
@@ -130,7 +138,7 @@
130
138
  .toggle-item.is-active,
131
139
  .toggle-item[aria-checked='true'] {
132
140
  background: var(--token-accent);
133
- color: var(--token-on-accent, #fff);
141
+ color: var(--token-on-accent);
134
142
  box-shadow: var(--token-shadow-card);
135
143
  }
136
144
  .toggle-item:disabled,
@@ -181,7 +189,7 @@
181
189
  }
182
190
  .stepper-btn:focus-visible {
183
191
  outline: none;
184
- box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--token-accent) 50%, transparent);
192
+ box-shadow: var(--icen-ring-inset);
185
193
  }
186
194
  .stepper-btn:disabled,
187
195
  .stepper-btn[aria-disabled='true'] {
@@ -225,7 +233,7 @@
225
233
  height: 48px;
226
234
  padding: 0;
227
235
  text-align: center;
228
- font-size: 18px;
236
+ font-size: var(--density-font-size-xl);
229
237
  font-weight: 600;
230
238
  font-family: var(--font-mono);
231
239
  color: var(--token-text);
@@ -254,7 +262,7 @@
254
262
  cursor: not-allowed;
255
263
  }
256
264
  .otp--sm .otp-cell { width: 32px; height: 40px; font-size: var(--density-font-size-lg); }
257
- .otp--lg .otp-cell { width: 48px; height: 56px; font-size: 22px; }
265
+ .otp--lg .otp-cell { width: 48px; height: 56px; font-size: var(--density-font-size-2xl); }
258
266
 
259
267
  @media (prefers-reduced-motion: reduce) {
260
268
  .segmented-item, .toggle-item, .stepper-btn, .stepper, .otp-cell { transition: none; }
@@ -1,2 +1,3 @@
1
1
  // @icen.ai/ui — kit: segmented(CSS + behavior 一行入口;构建产物,勿手改)
2
2
  import './segmented.css';
3
+ export { initInput, initSegmented, initStepper } from '../behaviors/controls.mjs';
@@ -4,14 +4,24 @@
4
4
  *
5
5
  * 完整契约:
6
6
  * <div class="select [data-disabled] [.is-open]" data-select
7
- * [data-select-search] [data-select-multiple] [data-select-max="3"]>
7
+ * [data-select-search] [data-select-multiple] [data-select-max="3"]
8
+ * [data-panel-width="220"] [data-panel-min="200"]
9
+ * [data-panel-max="360"] [data-panel-max-height="320"]>
10
+ * <!-- 选项 .select-option 内支持任意 HTML(色卡 / 图标 / 多行);
11
+ * 面板尺寸为全库统一 PanelSizing 契约(behaviors/popover.ts) -->
8
12
  * <button class="select-trigger pressable focus-ring [--sm|--lg]" aria-haspopup="listbox"
9
13
  * aria-expanded="false" [disabled]>
10
14
  * <span class="select-leading">…</span> 可选前缀
11
- * <span class="select-value is-empty">占位</span>
12
- * <button class="select-clear" aria-label="清除">×</button> ← 多选时可选
15
+ * <span class="select-value is-empty">占位</span> 多选有选中时 behavior 隐藏它
16
+ * <span class="select-chips"> ← 多选选中态由 behavior 渲染(勿手写)
17
+ * <span class="select-chip">标签<span class="select-chip-x" role="button" tabindex="-1">×</span></span>
18
+ * <span class="select-chip select-chip--more">+2</span>
19
+ * </span>
13
20
  * <svg class="select-chevron">…</svg>
14
21
  * </button>
22
+ * <button class="select-clear" aria-label="清除全部" hidden>×</button> ← 多选可选:
23
+ * 与 trigger 同级(不能嵌套在 button 内),绝对定位叠进 trigger 右侧;
24
+ * behavior 在有选中时自动显示
15
25
  * <div class="select-panel" hidden role="listbox">
16
26
  * <div class="select-search"> ← data-select-search 时 behavior 自动注入
17
27
  * <input class="select-search-input" placeholder="搜索…" />
@@ -26,7 +36,8 @@
26
36
  * </div>
27
37
  *
28
38
  * behavior 维护:
29
- * - .select.is-open ↔ aria-expanded;panel [hidden] 切换;ESC/外部点击关闭
39
+ * - .select.is-open ↔ aria-expanded;panel portal 到 body(inline left/top/width/max-height,
40
+ * 带 data-side="top|bottom";多选时带 data-select-multiple);ESC/外部点击关闭
30
41
  * - 选项 .is-focused 跟随键盘箭头;Enter 选中 → 写回 .select-value / .is-selected
31
42
  * - data-select-search:面板顶部自动插入搜索框,实时过滤(含 data-keywords),空分组自动隐藏
32
43
  * - data-select-multiple:点击切选不关面板,trigger 显示计数,hidden input 同步逗号分隔值
@@ -35,7 +46,6 @@
35
46
  .select {
36
47
  position: relative;
37
48
  width: 100%;
38
- isolation: isolate; /* panel 不被后面兄弟节点的层叠遮挡 */
39
49
  }
40
50
 
41
51
  .select-trigger {
@@ -105,12 +115,11 @@
105
115
  }
106
116
  .select.is-open .select-chevron { transform: rotate(180deg); }
107
117
 
118
+ /* 面板由 behavior portal 到 body:left/top/width/max-height 由 JS 按 trigger rect inline 写入;
119
+ data-side="top|bottom" 标记翻侧方向;多选时 panel 带 data-select-multiple 属性。 */
108
120
  .select-panel {
109
121
  position: absolute;
110
- top: calc(100% + 4px);
111
- left: 0;
112
- min-width: 100%;
113
- max-height: 240px;
122
+ z-index: var(--z-popover);
114
123
  overflow-y: auto;
115
124
  overscroll-behavior: contain;
116
125
  padding: 4px;
@@ -118,17 +127,10 @@
118
127
  border: 1px solid var(--token-outline);
119
128
  border-radius: var(--radius-md);
120
129
  box-shadow: var(--token-shadow-overlay);
121
- z-index: var(--z-popover);
122
130
  transform-origin: top center;
123
- animation: select-panel-in var(--icen-tx-fast);
124
- }
125
- @keyframes select-panel-in {
126
- from { opacity: 0; transform: scale(0.96) translateY(-4px); }
127
- to { opacity: 1; transform: scale(1) translateY(0); }
128
- }
129
- @media (prefers-reduced-motion: reduce) {
130
- .select-panel { animation: none; }
131
+ animation: icen-pop-in var(--icen-tx-fast);
131
132
  }
133
+ .select-panel[data-side='top'] { transform-origin: bottom center; }
132
134
 
133
135
  /* 选项 */
134
136
  .select-option {
@@ -217,8 +219,13 @@
217
219
  box-shadow: var(--icen-control-focus-shadow);
218
220
  }
219
221
 
220
- /* ── 多选清除按钮 ── */
222
+ /* ── 多选清除按钮(与 trigger 同级,绝对定位叠进 trigger 右侧、chevron 左侧) ── */
221
223
  .select-clear {
224
+ position: absolute;
225
+ top: 50%;
226
+ right: calc(var(--density-space-sm) + 20px);
227
+ transform: translateY(-50%);
228
+ z-index: 1;
222
229
  display: inline-flex;
223
230
  align-items: center;
224
231
  justify-content: center;
@@ -230,7 +237,7 @@
230
237
  border-radius: var(--radius-xs);
231
238
  background: transparent;
232
239
  color: var(--token-text-faint);
233
- font-size: 15px;
240
+ font-size: var(--density-font-size-lg);
234
241
  line-height: 1;
235
242
  cursor: pointer;
236
243
  transition: color var(--icen-tx-fast), background-color var(--icen-tx-fast);
@@ -239,6 +246,53 @@
239
246
  color: var(--token-text);
240
247
  background: var(--token-fill-1);
241
248
  }
249
+ /* 有清除钮时 trigger 右侧让位(clear 18px + chevron 14px + 间距) */
250
+ .select:has(.select-clear) .select-trigger { padding-right: 52px; }
251
+
252
+ /* ── 多选 chips(tag 式展示;× 仅 hover 对应 chip 时出现) ── */
253
+ .select-chips {
254
+ display: inline-flex;
255
+ flex-wrap: wrap;
256
+ align-items: center;
257
+ gap: 4px;
258
+ flex: 1;
259
+ min-width: 0;
260
+ }
261
+ .select-chip {
262
+ display: inline-flex;
263
+ align-items: center;
264
+ max-width: 100%;
265
+ font-size: var(--density-font-size-xs);
266
+ line-height: 1.6;
267
+ color: var(--token-text);
268
+ background: var(--token-fill-2);
269
+ border-radius: var(--radius-sm);
270
+ padding: 0 7px;
271
+ white-space: nowrap;
272
+ }
273
+ .select-chip-x {
274
+ display: inline-flex;
275
+ align-items: center;
276
+ justify-content: center;
277
+ width: 0;
278
+ opacity: 0;
279
+ overflow: hidden;
280
+ margin-left: 0;
281
+ color: var(--token-text-faint);
282
+ border-radius: var(--radius-full);
283
+ font-size: var(--density-font-size-md);
284
+ line-height: 1;
285
+ cursor: pointer;
286
+ transition:
287
+ width var(--icen-tx-fast),
288
+ opacity var(--icen-tx-fast),
289
+ margin-left var(--icen-tx-fast),
290
+ color var(--icen-tx-fast);
291
+ }
292
+ .select-chip:hover .select-chip-x,
293
+ .select-chip-x:focus-visible { width: 12px; opacity: 1; margin-left: 4px; }
294
+ .select-chip-x:hover { color: var(--token-error); }
295
+ .select-chip--more { color: var(--token-text-muted); background: var(--token-fill-1); }
242
296
 
243
297
  /* ── 多选 trigger 高度自适应(chips 可能换行) ── */
244
298
  .select[data-select-multiple] .select-trigger {
@@ -249,8 +303,8 @@
249
303
  flex-wrap: wrap;
250
304
  }
251
305
 
252
- /* 多选项 checkbox 指示器(选项左侧) */
253
- .select[data-select-multiple] .select-option::before {
306
+ /* 多选项 checkbox 指示器(选项左侧;panel portal 到 body,属性在 panel 上) */
307
+ .select-panel[data-select-multiple] .select-option::before {
254
308
  content: '';
255
309
  width: 14px;
256
310
  height: 14px;
@@ -259,11 +313,11 @@
259
313
  border-radius: var(--radius-xs);
260
314
  transition: background-color var(--icen-tx-fast), border-color var(--icen-tx-fast);
261
315
  }
262
- .select[data-select-multiple] .select-option.is-selected::before {
316
+ .select-panel[data-select-multiple] .select-option.is-selected::before {
263
317
  background: var(--token-accent);
264
318
  border-color: var(--token-accent);
265
319
  }
266
- .select[data-select-multiple] .select-option.is-selected::after {
320
+ .select-panel[data-select-multiple] .select-option.is-selected::after {
267
321
  content: '✓';
268
322
  margin-left: auto;
269
323
  }
@@ -271,5 +325,6 @@
271
325
  @media (prefers-reduced-motion: reduce) {
272
326
  .select-panel { animation: none; }
273
327
  .select-search-input,
328
+ .select-chip-x,
274
329
  .select-clear { transition: none; }
275
330
  }
@@ -170,7 +170,7 @@
170
170
  padding: 0 5px;
171
171
  font-size: var(--density-font-size-xs);
172
172
  font-weight: 600;
173
- color: var(--token-on-accent, #fff);
173
+ color: var(--token-on-accent);
174
174
  background: var(--token-accent);
175
175
  border-radius: var(--radius-full);
176
176
  font-variant-numeric: tabular-nums;
@@ -82,7 +82,6 @@
82
82
  }
83
83
  .split-pane--vertical .split-pane-first {
84
84
  flex: 0 0 var(--split, 50%);
85
- flex-basis: var(--split, 50%);
86
85
  min-height: 0;
87
86
  }
88
87
  .split-pane--vertical .split-pane-second {
@@ -6,10 +6,10 @@
6
6
  * <div class="stat-card [.stat-card--interactive] [.stat-card--horizontal]">
7
7
  * <div class="stat-head">
8
8
  * <span class="stat-label">用户总数</span>
9
- * <span class="stat-icon [.icon--ok|.icon--warn|.icon--bad|.icon--info]>…svg…</span>
9
+ * <span class="stat-icon [.icon--success|.icon--warning|.icon--error|.icon--info]>…svg…</span>
10
10
  * </div>
11
11
  * <div class="stat-body">
12
- * <div class="stat-num [.accent|.warn|.bad|.info]">0</div>
12
+ * <div class="stat-num [--accent|--warning|--error|--info]">0</div>
13
13
  * <div class="stat-delta [.up|.down|.flat]"> ← 可选趋势
14
14
  * <svg>…arrow…</svg> +12.4%
15
15
  * <span class="stat-period">比上周</span>
@@ -85,10 +85,10 @@
85
85
  background: var(--token-accent-subtle);
86
86
  color: var(--token-accent);
87
87
  }
88
- .stat-icon.icon--ok { background: color-mix(in srgb, var(--token-success) 14%, transparent); color: var(--token-success); }
89
- .stat-icon.icon--warn { background: color-mix(in srgb, var(--token-warning) 14%, transparent); color: var(--token-warning); }
90
- .stat-icon.icon--bad { background: color-mix(in srgb, var(--token-error) 14%, transparent); color: var(--token-error); }
91
- .stat-icon.icon--info { background: color-mix(in srgb, var(--token-info) 14%, transparent); color: var(--token-info); }
88
+ .stat-icon.icon--success { background: color-mix(in srgb, var(--token-success) 14%, transparent); color: var(--token-success); }
89
+ .stat-icon.icon--warning { background: color-mix(in srgb, var(--token-warning) 14%, transparent); color: var(--token-warning); }
90
+ .stat-icon.icon--error { background: color-mix(in srgb, var(--token-error) 14%, transparent); color: var(--token-error); }
91
+ .stat-icon.icon--info { background: color-mix(in srgb, var(--token-info) 14%, transparent); color: var(--token-info); }
92
92
  .stat-icon svg { width: 14px; height: 14px; }
93
93
 
94
94
  /* ── 内容体(数字 + 趋势) ── */
@@ -99,17 +99,17 @@
99
99
  }
100
100
 
101
101
  .stat-num {
102
- font-size: 24px;
102
+ font-size: var(--density-font-size-2xl);
103
103
  font-weight: 700;
104
104
  line-height: 1.2;
105
105
  font-variant-numeric: tabular-nums lining-nums;
106
106
  color: var(--token-text);
107
107
  word-break: break-all;
108
108
  }
109
- .stat-num.accent { color: var(--token-accent); }
110
- .stat-num.warn { color: var(--token-warning); }
111
- .stat-num.bad { color: var(--token-error); }
112
- .stat-num.info { color: var(--token-info); }
109
+ .stat-num--accent { color: var(--token-accent); }
110
+ .stat-num--warning { color: var(--token-warning); }
111
+ .stat-num--error { color: var(--token-error); }
112
+ .stat-num--info { color: var(--token-info); }
113
113
 
114
114
  .stat-delta {
115
115
  display: inline-flex;
@@ -134,7 +134,7 @@
134
134
  gap: 2px;
135
135
  }
136
136
  .stat-card--sm .stat-num { font-size: var(--density-font-size-lg); }
137
- .stat-card--sm .stat-label { font-size: 10px; }
137
+ .stat-card--sm .stat-label { font-size: var(--density-font-size-2xs); }
138
138
 
139
139
  /* ── 横向布局(dashboard 顶栏:标签左 + 数字右) ── */
140
140
  .stat-card--horizontal {
@@ -215,20 +215,16 @@
215
215
  margin: 0 1px;
216
216
  }
217
217
 
218
- /* ── 加载态骨架 ── */
218
+ /* ── 加载态骨架(脉冲 keyframes 见 base.css 的 icen-skeleton-pulse) ── */
219
219
  .stat-card.is-loading .stat-num,
220
220
  .stat-card.is-loading .stat-delta {
221
221
  color: transparent;
222
222
  background: var(--token-fill-2);
223
223
  border-radius: var(--radius-xs);
224
- animation: stat-skeleton 1.5s ease-in-out infinite;
224
+ animation: icen-skeleton-pulse var(--stat-skeleton-duration, 1.5s) ease-in-out infinite;
225
225
  }
226
226
  .stat-card.is-loading .stat-num { height: 28px; }
227
227
  .stat-card.is-loading .stat-delta { height: 14px; width: 60%; }
228
- @keyframes stat-skeleton {
229
- 0%, 100% { opacity: 1; }
230
- 50% { opacity: 0.5; }
231
- }
232
228
  @media (prefers-reduced-motion: reduce) {
233
229
  .stat-card.is-loading .stat-num,
234
230
  .stat-card.is-loading .stat-delta { animation: none; }
@@ -2,7 +2,8 @@
2
2
  * @icen.ai/ui — Component: steps(步骤条,横向默认 / .steps--vertical 垂直)
3
3
  *
4
4
  * 完整契约:
5
- * <ol class="steps [steps--vertical] [steps--sm|steps--lg]" aria-label="进度">
5
+ * <ol class="steps [steps--vertical] [steps--sm|steps--lg] [steps--dots|steps--progress|steps--connected]"
6
+ * aria-label="进度">
6
7
  * <li class="step [step--finish|step--process|step--wait] [.step--error]">
7
8
  * <span class="step-line"></span> ← 横向:右半段连线;末步自动隐藏
8
9
  * <span class="step-dot">[✓ | ! | 数字]</span>
@@ -14,7 +15,11 @@
14
15
  * </ol>
15
16
  *
16
17
  * 四态:finish / process / wait / error(错误用 error 色 dot + 描边)。
17
- * 点击 .step[role=button] 由使用方实现跳转。
18
+ * 变体与增强(正文下半段):--dots 纯点紧凑 / --progress 步骤间填充进度线 / --connected 虚线连接;
19
+ * dot 内可放 svg.step-dot-icon;step-desc 旁可加 .step-extra 富化描述;
20
+ * 可点击步骤标 .step--clickable(与 controls.ts initSteps 的 JSDoc 同口径)。
21
+ * 点击 .step[role=button] 由 behaviors/controls.ts 的 initSteps() 接线(激活态位移 +
22
+ * icen:step-change);跳转业务仍由使用方监听事件实现。
18
23
  */
19
24
 
20
25
  .steps {
@@ -61,7 +66,7 @@
61
66
  }
62
67
  .step--finish .step-dot {
63
68
  background: var(--token-accent);
64
- color: var(--token-on-accent, #fff);
69
+ color: var(--token-on-accent);
65
70
  }
66
71
  .step--process .step-dot {
67
72
  background: var(--token-accent-subtle);
@@ -141,7 +146,9 @@
141
146
  padding: 0;
142
147
  font-size: 0;
143
148
  }
144
- .steps--dots .step-line { top: 20px; }
149
+ /* 线 top = dot 高度一半:默认 32→16 / sm 24→12 / lg 40→20 / dots 10→5;
150
+ 本块位于尺寸变体之后,同等特异性下靠源顺序生效,dots 与 --sm/--lg 叠加时以 5px 为准 */
151
+ .steps--dots .step-line { top: 5px; }
145
152
  .steps--dots .step-title,
146
153
  .steps--dots .step-desc { display: none; }
147
154
  .steps--dots .step { flex: none; }
@@ -152,7 +159,7 @@
152
159
  width: 16px;
153
160
  height: 16px;
154
161
  }
155
- .step--finish .step-dot-icon { color: var(--token-on-accent, #fff); }
162
+ .step--finish .step-dot-icon { color: var(--token-on-accent); }
156
163
  .step--process .step-dot-icon { color: var(--token-accent); }
157
164
  .step--wait .step-dot-icon { color: var(--token-text-faint); }
158
165
  .step--error .step-dot-icon { color: var(--token-error); }
@@ -171,8 +178,11 @@
171
178
  }
172
179
 
173
180
  /* ── 进度条变体(步骤间的填充进度,非离散连线) ── */
181
+ /* 高度 1px→3px 时线只向下扩张 2px,整体上移 1px 使线心与 1px 基线居中一致;
182
+ 用 translateY 而非改 top,可同时适配 --sm(12px)/--lg(20px) 的不同基线 */
174
183
  .steps--progress .step-line {
175
184
  height: 3px;
185
+ transform: translateY(-1px);
176
186
  border-radius: var(--radius-full);
177
187
  background: var(--token-fill-2);
178
188
  overflow: hidden;
@@ -1,2 +1,3 @@
1
1
  // @icen.ai/ui — kit: steps(CSS + behavior 一行入口;构建产物,勿手改)
2
2
  import './steps.css';
3
+ export { initSteps } from '../behaviors/controls.mjs';
@@ -1,6 +1,8 @@
1
1
  /*
2
2
  * @icen.ai/ui — Component: switch / checkbox / radio(开关 / 勾选 / 单选行)
3
- * 纯 CSS 状态组件:状态全走 aria-checked / .is-selected;交互由使用方或演示页脚本切换。
3
+ * 状态组件:状态全走 aria-checked / .is-selected;交互由 behaviors/controls.ts 的
4
+ * initSwitch() 接线(点击 / Space / Enter 翻转并派 icen:switch-change / icen:check-change),
5
+ * 不接线时亦可由使用方自行驱动状态。
4
6
  *
5
7
  * 完整契约:
6
8
  * <button class="switch [--sm|--lg]" role="switch" aria-checked="true|false" [disabled]>
@@ -59,8 +61,8 @@
59
61
  width: var(--switch-thumb);
60
62
  height: var(--switch-thumb);
61
63
  border-radius: var(--radius-full);
62
- background: var(--token-on-accent, #fff);
63
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
64
+ background: var(--token-on-accent);
65
+ box-shadow: 0 1px 2px color-mix(in srgb, var(--token-text) 15%, transparent);
64
66
  transition: transform var(--icen-tx-bounce);
65
67
  }
66
68
  .switch[aria-checked='true'] .switch-thumb {
@@ -71,10 +73,6 @@
71
73
  .switch--sm { --switch-w: 36px; --switch-h: 20px; --switch-thumb: 16px; }
72
74
  .switch--lg { --switch-w: 56px; --switch-h: 28px; --switch-thumb: 24px; }
73
75
 
74
- @media (prefers-reduced-motion: reduce) {
75
- .switch-thumb { transition: none; }
76
- }
77
-
78
76
  /* ── 勾选框(✓ 与半选横条均 ::before 自绘) ── */
79
77
  .checkbox {
80
78
  --checkbox-size: 16px;
@@ -104,7 +102,7 @@
104
102
  .checkbox[aria-checked='mixed'] {
105
103
  background: var(--token-accent);
106
104
  border-color: var(--token-accent);
107
- color: var(--token-on-accent, #fff);
105
+ color: var(--token-on-accent);
108
106
  }
109
107
  .checkbox::before {
110
108
  content: '';
@@ -224,7 +222,7 @@
224
222
  display: flex;
225
223
  align-items: center;
226
224
  justify-content: center;
227
- font-size: 9px;
225
+ font-size: var(--density-font-size-2xs);
228
226
  font-weight: 700;
229
227
  color: transparent;
230
228
  }
@@ -234,9 +232,9 @@
234
232
  left: 6px;
235
233
  top: 50%;
236
234
  transform: translateY(-50%);
237
- font-size: 9px;
235
+ font-size: var(--density-font-size-2xs);
238
236
  font-weight: 700;
239
- color: var(--token-on-accent, #fff);
237
+ color: var(--token-on-accent);
240
238
  }
241
239
  .switch--text[aria-checked='false']::before {
242
240
  content: 'OFF';
@@ -244,7 +242,7 @@
244
242
  right: 6px;
245
243
  top: 50%;
246
244
  transform: translateY(-50%);
247
- font-size: 9px;
245
+ font-size: var(--density-font-size-2xs);
248
246
  font-weight: 700;
249
247
  color: var(--token-text-faint);
250
248
  }
@@ -304,7 +302,7 @@
304
302
  background-color var(--icen-tx-fast);
305
303
  }
306
304
  .radio-card:hover {
307
- border-color: var(--token-hover-border, color-mix(in srgb, var(--token-accent) 30%, transparent));
305
+ border-color: var(--icen-hover-border);
308
306
  }
309
307
  .radio-card-head {
310
308
  display: flex;
@@ -1,2 +1,3 @@
1
1
  // @icen.ai/ui — kit: switch(CSS + behavior 一行入口;构建产物,勿手改)
2
2
  import './switch.css';
3
+ export { initSwitch } from '../behaviors/controls.mjs';