@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
@@ -77,3 +77,13 @@
77
77
  @media (prefers-reduced-motion: reduce) {
78
78
  .chart-vbar-bar, .chart-hbar-bar { transition: none; }
79
79
  }
80
+
81
+ /* ── 多系列:分组(簇内并排)与堆叠(列内向上叠)── */
82
+ .chart-vbar-track--multi { gap: 2px; }
83
+ .chart-vbar-track--multi > .chart-vbar-bar { flex: 1; min-width: 0; width: auto; }
84
+ .chart-vbar-track--multi.chart-vbar-track--stacked {
85
+ flex-direction: column;
86
+ justify-content: flex-end;
87
+ gap: 0;
88
+ }
89
+ .chart-vbar-track--multi.chart-vbar-track--stacked > .chart-vbar-bar { flex: none; width: 100%; }
@@ -0,0 +1,4 @@
1
+ // @icen.ai/ui — kit: chart-calendar(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './chart-heatmap.css';
3
+ import './charts.css';
4
+ export { renderCalendar } from '../behaviors/charts.mjs';
@@ -27,7 +27,7 @@
27
27
  gap: 1px;
28
28
  }
29
29
  .chart-gauge-value {
30
- font-size: 20px;
30
+ font-size: var(--density-font-size-2xl);
31
31
  font-weight: 700;
32
32
  font-variant-numeric: tabular-nums;
33
33
  color: var(--token-text);
@@ -50,3 +50,43 @@
50
50
  }
51
51
  .chart-heatmap-legend > span:first-child { margin-right: 2px; }
52
52
  .chart-heatmap-legend > span:last-child { margin-left: 2px; }
53
+
54
+ /* ── 贡献日历(renderCalendar):月份标签 + 星期列 + 周格 ──
55
+ * 布局 grid:左列星期标签(一/三/五/日),右列自上而下 月份行 / 周格 / 图例。
56
+ * 月份标签按周列定位:left = col / columns * 100%。 */
57
+ .chart-calendar {
58
+ display: grid;
59
+ grid-template-columns: 14px 1fr;
60
+ grid-template-rows: 14px auto auto;
61
+ column-gap: 4px;
62
+ row-gap: 3px;
63
+ }
64
+ .chart-calendar-months {
65
+ grid-column: 2;
66
+ position: relative;
67
+ font-size: var(--density-font-size-2xs);
68
+ color: var(--token-text-faint);
69
+ }
70
+ .chart-calendar-month {
71
+ position: absolute;
72
+ top: 0;
73
+ left: calc(var(--col, 0) / var(--columns, 1) * 100%);
74
+ white-space: nowrap;
75
+ }
76
+ .chart-calendar-weekdays {
77
+ grid-column: 1;
78
+ grid-row: 2;
79
+ display: grid;
80
+ grid-template-rows: repeat(7, 1fr);
81
+ font-size: var(--density-font-size-2xs);
82
+ color: var(--token-text-faint);
83
+ line-height: 1;
84
+ }
85
+ .chart-calendar .chart-heatmap-grid {
86
+ grid-column: 2;
87
+ grid-row: 2;
88
+ }
89
+ .chart-calendar .chart-heatmap-legend {
90
+ grid-column: 2;
91
+ grid-row: 3;
92
+ }
@@ -18,9 +18,3 @@
18
18
  height: 132px;
19
19
  flex-shrink: 0;
20
20
  }
21
- .chart-donut-center {
22
- font-variant-numeric: tabular-nums;
23
- text-anchor: middle;
24
- fill: var(--token-text);
25
- font-weight: 600;
26
- }
@@ -0,0 +1,24 @@
1
+ /*
2
+ * @icen.ai/ui — Component: chart-scatter(散点/气泡图专属样式)
3
+ * 依赖:charts.css(基座 .chart / tooltip / [data-chart-mark] 交互态)
4
+ *
5
+ * DOM 由 behaviors/charts.ts 的 renderScatter 构建:
6
+ * .chart-scatter > svg(360×220 viewBox,网格 + 刻度 + circle[data-chart-mark])
7
+ * 气泡第三维:points[].size 映射半径 3–10;hover/点击/右键走通用层委托。
8
+ */
9
+
10
+ .chart-scatter svg {
11
+ width: 100%;
12
+ height: auto;
13
+ display: block;
14
+ }
15
+ .chart-scatter circle[data-chart-mark] {
16
+ transition: fill-opacity var(--icen-tx-fast);
17
+ }
18
+ .chart-scatter circle[data-chart-mark]:hover {
19
+ fill-opacity: 0.85;
20
+ }
21
+
22
+ @media (prefers-reduced-motion: reduce) {
23
+ .chart-scatter circle[data-chart-mark] { transition: none; }
24
+ }
@@ -0,0 +1,4 @@
1
+ // @icen.ai/ui — kit: chart-scatter(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './chart-scatter.css';
3
+ import './charts.css';
4
+ export { renderScatter } from '../behaviors/charts.mjs';
@@ -70,7 +70,7 @@
70
70
  flex: 1;
71
71
  min-width: 0;
72
72
  text-align: center;
73
- font-size: 9px;
73
+ font-size: var(--density-font-size-2xs);
74
74
  color: var(--token-text-faint);
75
75
  overflow: hidden;
76
76
  text-overflow: ellipsis;
@@ -78,10 +78,95 @@
78
78
  font-variant-numeric: tabular-nums;
79
79
  }
80
80
 
81
- /* ── 调色盘:多系列图(donut/stack/line/radar)由 behavior 选用 ── */
81
+ /* ── 调色盘:手写 markup 调色盘;behavior 走 charts.ts PALETTE_VARS 内联同值(两处值需人工同步) ── */
82
82
  .chart-tone-1 { --chart-tone: var(--token-accent); }
83
83
  .chart-tone-2 { --chart-tone: var(--token-accent-cold); }
84
84
  .chart-tone-3 { --chart-tone: var(--token-success); }
85
85
  .chart-tone-4 { --chart-tone: var(--token-warning); }
86
86
  .chart-tone-5 { --chart-tone: var(--token-info); }
87
87
  .chart-tone-6 { --chart-tone: color-mix(in srgb, var(--token-text) 50%, transparent); }
88
+
89
+ /* ═══ 通用层(renderChart):标题 / 内置 tooltip / 标记交互 / 图例切换 ═══ */
90
+
91
+ /* 标题(spec.title 有值时渲染) */
92
+ .chart-title {
93
+ font-size: var(--density-font-size-sm);
94
+ font-weight: 600;
95
+ color: var(--token-text);
96
+ }
97
+
98
+ /* 头部行(renderChart 渲染):标题 + 图例可见性小眼睛 */
99
+ .chart-head {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: space-between;
103
+ gap: var(--density-space-sm);
104
+ min-height: 24px;
105
+ }
106
+ .chart-legend-eye {
107
+ display: inline-flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ width: 22px;
111
+ height: 22px;
112
+ padding: 0;
113
+ border: 0;
114
+ border-radius: var(--radius-xs);
115
+ background: transparent;
116
+ color: var(--token-text-faint);
117
+ cursor: pointer;
118
+ transition: color var(--icen-tx-fast), background-color var(--icen-tx-fast);
119
+ }
120
+ .chart-legend-eye:hover { color: var(--token-text); background: var(--token-fill-1); }
121
+ .chart-legend-eye:focus-visible { outline: none; box-shadow: var(--icen-ring); }
122
+ .chart-legend-eye svg { width: 13px; height: 13px; }
123
+
124
+ /* 图例统一隐藏:容器级继承——data-legend-hidden 一条选择器覆盖全部图型的图例 */
125
+ [data-legend-hidden='true'] .chart-legend,
126
+ [data-legend-hidden='true'] .chart-legend--toggle,
127
+ [data-legend-hidden='true'] .chart-heatmap-legend {
128
+ display: none;
129
+ }
130
+
131
+ /* 内置 tooltip:portal 到 body 的单例,position: fixed 跟随指针 */
132
+ .chart-tooltip {
133
+ position: fixed;
134
+ z-index: var(--z-tooltip);
135
+ max-width: 280px;
136
+ padding: 3px 8px;
137
+ border-radius: var(--radius-sm);
138
+ background: var(--token-card-strong);
139
+ border: 1px solid var(--token-line);
140
+ box-shadow: var(--token-shadow-elevated);
141
+ font-size: var(--density-font-size-xs);
142
+ font-variant-numeric: tabular-nums;
143
+ color: var(--token-text);
144
+ pointer-events: none;
145
+ }
146
+ .chart-tooltip[hidden] { display: none; }
147
+
148
+ /* 数据标记(各图型的条/点/段/格):交互态由委托层挂 .is-hover,图例切换挂 .is-off */
149
+ [data-chart-mark] { cursor: pointer; }
150
+ [data-chart-mark].is-hover { filter: brightness(1.12); }
151
+ [data-chart-mark].is-off { opacity: 0.16; pointer-events: none; }
152
+ svg circle[data-chart-mark].is-hover { stroke-width: 3; }
153
+
154
+ /* 可切换图例:行可点(Enter/Space 等价),.is-off 划线淡化 */
155
+ .chart-legend--toggle .chart-legend-toggle-item {
156
+ cursor: pointer;
157
+ border-radius: var(--radius-xs);
158
+ padding: 2px 4px;
159
+ margin: -2px -4px;
160
+ transition: background-color var(--icen-tx-fast);
161
+ }
162
+ .chart-legend--toggle .chart-legend-toggle-item:hover { background: var(--token-fill-1); }
163
+ .chart-legend--toggle .chart-legend-toggle-item:focus-visible {
164
+ outline: none;
165
+ box-shadow: var(--icen-ring);
166
+ }
167
+ .chart-legend--toggle .is-off .chart-legend-label,
168
+ .chart-legend--toggle .is-off .chart-legend-value {
169
+ color: var(--token-text-faint);
170
+ text-decoration: line-through;
171
+ }
172
+ .chart-legend--toggle .is-off .chart-legend-dot { opacity: 0.3; }
@@ -9,4 +9,5 @@ import './chart-area.css';
9
9
  import './chart-stack.css';
10
10
  import './chart-gauge.css';
11
11
  import './chart-sparkline.css';
12
+ import './chart-scatter.css';
12
13
  export * from '../behaviors/charts.mjs';
@@ -188,7 +188,7 @@
188
188
 
189
189
  .command-palette-item-kbd {
190
190
  font-family: var(--font-mono);
191
- font-size: 10px;
191
+ font-size: var(--density-font-size-2xs);
192
192
  color: var(--token-text-faint);
193
193
  padding: 1px 5px;
194
194
  border: 1px solid var(--token-line-soft);
@@ -219,7 +219,7 @@
219
219
  }
220
220
  .command-palette-foot-hint kbd {
221
221
  font-family: var(--font-mono);
222
- font-size: 10px;
222
+ font-size: var(--density-font-size-2xs);
223
223
  padding: 1px 4px;
224
224
  border: 1px solid var(--token-line-soft);
225
225
  border-radius: var(--radius-xs);
@@ -3,6 +3,9 @@
3
3
  *
4
4
  * 包含:kbd(按键)/ code-inline(行内代码)/ list(分隔列表)/ accordion(手风琴,behaviors/accordion 配套)/
5
5
  * timeline(时间线)/ desc(描述列表)/ tree(树形,behaviors/tree 配套)
6
+ * markup API 变体(正文"增强"段,behavior 不产出、由手写 markup 消费):
7
+ * .accordion--cards / .code-inline--success|--warning|--error /
8
+ * .timeline--right|--alternate|--sm / .timeline-extra / .timeline-dot--sm 及 dot 语义色(--error 等)
6
9
  * 规格源自 opengal src/presentation/ui/data/exhibit.tsx 与 data/display.tsx。
7
10
  */
8
11
 
@@ -28,7 +31,7 @@
28
31
  height: 16px;
29
32
  min-width: 16px;
30
33
  padding: 0 4px;
31
- font-size: 10px;
34
+ font-size: var(--density-font-size-2xs);
32
35
  }
33
36
  .kbd-group {
34
37
  display: inline-flex;
@@ -192,11 +195,11 @@
192
195
  z-index: 1;
193
196
  }
194
197
  .timeline-dot svg { width: 13px; height: 13px; }
195
- .timeline-dot--accent { background: var(--token-accent); color: var(--token-on-accent, #fff); border-color: transparent; }
196
- .timeline-dot--success { background: var(--token-success); color: var(--token-on-accent, #fff); border-color: transparent; }
197
- .timeline-dot--warning { background: var(--token-warning); color: var(--token-on-accent, #fff); border-color: transparent; }
198
- .timeline-dot--error { background: var(--token-error); color: var(--token-on-accent, #fff); border-color: transparent; }
199
- .timeline-dot--info { background: var(--token-info); color: var(--token-on-accent, #fff); border-color: transparent; }
198
+ .timeline-dot--accent { background: var(--token-accent); color: var(--token-on-accent); border-color: transparent; }
199
+ .timeline-dot--success { background: var(--token-success); color: var(--token-on-accent); border-color: transparent; }
200
+ .timeline-dot--warning { background: var(--token-warning); color: var(--token-on-accent); border-color: transparent; }
201
+ .timeline-dot--error { background: var(--token-error); color: var(--token-on-accent); border-color: transparent; }
202
+ .timeline-dot--info { background: var(--token-info); color: var(--token-on-accent); border-color: transparent; }
200
203
 
201
204
  .timeline-body { flex: 1; min-width: 0; }
202
205
  .timeline-head {
@@ -362,9 +365,9 @@
362
365
  min-width: 16px;
363
366
  height: 16px;
364
367
  padding: 0 4px;
365
- font-size: 10px;
368
+ font-size: var(--density-font-size-2xs);
366
369
  font-weight: 600;
367
- color: var(--token-on-accent, #fff);
370
+ color: var(--token-on-accent);
368
371
  background: var(--token-accent);
369
372
  border-radius: var(--radius-full);
370
373
  flex-shrink: 0;
@@ -454,7 +457,6 @@
454
457
  /* 自定义 dot 尺寸 */
455
458
  .timeline-dot--sm { width: 20px; height: 20px; }
456
459
  .timeline-dot--sm svg { width: 10px; height: 10px; }
457
- .timeline-dot--sm ~ .timeline-item:not(:last-child)::after,
458
460
  .timeline--sm .timeline-item:not(:last-child)::after { left: 10px; top: 24px; }
459
461
 
460
462
  /* 时间线自定义内容槽(放标签、卡片等) */
@@ -532,22 +534,6 @@
532
534
  background: var(--token-outline-soft);
533
535
  }
534
536
 
535
- /* 树节点复选框(checkbox tree) */
536
- .tree-checkbox {
537
- width: 14px;
538
- height: 14px;
539
- flex-shrink: 0;
540
- margin-right: 2px;
541
- }
542
-
543
- /* 树拖拽视觉态 */
544
- .tree-node.is-dragging {
545
- opacity: 0.5;
546
- }
547
- .tree-node.is-drop-target {
548
- box-shadow: inset 0 -2px var(--token-accent);
549
- }
550
-
551
537
  /* 树搜索高亮 */
552
538
  .tree-label mark {
553
539
  background: color-mix(in srgb, var(--token-warning) 30%, transparent);
@@ -6,17 +6,17 @@
6
6
  * <button class="copy-btn" data-copy="复制的文本">复制</button>
7
7
  * <button class="copy-btn copy-btn--ghost" data-copy="…">复制链接</button>
8
8
  *
9
- * 图标变体(带 data-copy-icon,仅加 .done 类;由 CSS 显隐子 svg):
9
+ * 图标变体(带 data-copy-icon,仅加 .is-done 类;由 CSS 显隐子 svg):
10
10
  * <button class="copy-btn copy-btn--icon" data-copy="…" data-copy-icon aria-label="复制">
11
11
  * <svg class="copy-icon-clipboard">…</svg>
12
12
  * <svg class="copy-icon-check" hidden>…</svg>
13
13
  * </button>
14
14
  *
15
15
  * behavior 自动处理:
16
- * - 文本变体:成功后 textContent = '已复制' + 加 .done 类,1.4s 后还原
17
- * - 图标变体(带 data-copy-icon):仅加 .done 类(不改文本),由 CSS 切换 svg 显隐
16
+ * - 文本变体:成功后 textContent = '已复制' + 加 .is-done 类,1.4s 后还原
17
+ * - 图标变体(带 data-copy-icon):仅加 .is-done 类(不改文本),由 CSS 切换 svg 显隐
18
18
  *
19
- * 与 toast.ok 区别:copy 是"原地反馈"(按钮自身闪烁),toast 是"全局通知"。
19
+ * 与 toast.toast--success 区别:copy 是"原地反馈"(按钮自身闪烁),toast 是"全局通知"。
20
20
  */
21
21
 
22
22
  .copy-btn {
@@ -65,7 +65,7 @@
65
65
  }
66
66
 
67
67
  /* ── 成功闪烁态(behavior 1.4s 后移除)── */
68
- .copy-btn.done {
68
+ .copy-btn.is-done {
69
69
  color: var(--token-success);
70
70
  border-color: color-mix(in srgb, var(--token-success) 40%, transparent);
71
71
  background: color-mix(in srgb, var(--token-success) 10%, transparent);
@@ -83,10 +83,10 @@
83
83
  flex-shrink: 0;
84
84
  }
85
85
 
86
- /* .done 时显隐切换:剪贴板 → 对勾 */
86
+ /* .is-done 时显隐切换:剪贴板 → 对勾 */
87
87
  .copy-btn[data-copy-icon] .copy-icon-check { display: none; }
88
- .copy-btn[data-copy-icon].done .copy-icon-clipboard { display: none; }
89
- .copy-btn[data-copy-icon].done .copy-icon-check { display: inline-block; }
88
+ .copy-btn[data-copy-icon].is-done .copy-icon-clipboard { display: none; }
89
+ .copy-btn[data-copy-icon].is-done .copy-icon-check { display: inline-block; }
90
90
 
91
91
  @media (prefers-reduced-motion: reduce) {
92
92
  .copy-btn { transition: none; }
@@ -125,11 +125,11 @@
125
125
  .dt-cell--head.is-sortable:hover { color: var(--token-text); }
126
126
  .dt-cell--head:focus-visible {
127
127
  outline: none;
128
- box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--token-accent) 50%, transparent);
128
+ box-shadow: var(--icen-ring-inset);
129
129
  }
130
130
  .dt-head-title { overflow: hidden; text-overflow: ellipsis; }
131
131
  .dt-sort {
132
- font-size: 10px;
132
+ font-size: var(--density-font-size-2xs);
133
133
  color: var(--token-text-faint);
134
134
  flex-shrink: 0;
135
135
  }
@@ -165,11 +165,7 @@
165
165
  border-radius: var(--radius-md);
166
166
  box-shadow: var(--token-shadow-overlay);
167
167
  font-weight: 400;
168
- animation: dt-panel-in var(--icen-tx-fast);
169
- }
170
- @keyframes dt-panel-in {
171
- from { opacity: 0; transform: scale(0.96) translateY(-4px); }
172
- to { opacity: 1; transform: scale(1) translateY(0); }
168
+ animation: icen-pop-in var(--icen-tx-fast);
173
169
  }
174
170
  .dt-filter-list {
175
171
  max-height: 220px;
@@ -400,7 +396,7 @@
400
396
  .dt-resize:active::after {
401
397
  background: var(--token-accent);
402
398
  }
403
- .dt-cell--head { /* 确保相对定位供 resize 绝对定位 */ }
399
+ /* .dt-cell--head 已在主规则 position: relative,供 resize 手柄绝对定位 */
404
400
 
405
401
  /* ── 冻结列(左固定 sticky) ── */
406
402
  .datatable--frozen .dt-cell--frozen {
@@ -422,7 +418,7 @@
422
418
  background: color-mix(in srgb, var(--token-accent-subtle) 82%, var(--token-fill-1));
423
419
  }
424
420
 
425
- /* ── 行状态类(rowClassName 产出) ── */
421
+ /* ── 行状态类(rowClassName 产出;--danger/--warning/--success 同族语义行底色) ── */
426
422
  .dt-row--danger { background: color-mix(in srgb, var(--token-error) 6%, transparent) !important; }
427
423
  .dt-row--danger:hover { background: color-mix(in srgb, var(--token-error) 10%, transparent) !important; }
428
424
  .dt-row--warning { background: color-mix(in srgb, var(--token-warning) 6%, transparent) !important; }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 契约:
5
5
  * <div class="date-picker" data-date-picker data-date-picker-input="id">
6
- * <button class="date-picker-trigger" type="button" aria-haspopup="dialog">
6
+ * <button class="date-picker-trigger [--sm|--lg]" type="button" aria-haspopup="dialog">
7
7
  * <span class="date-picker-value">2026-07-29</span>
8
8
  * <svg class="date-picker-icon">…日历 svg…</svg>
9
9
  * </button>
@@ -101,11 +101,7 @@
101
101
  border-radius: var(--radius-md);
102
102
  box-shadow: var(--token-shadow-overlay);
103
103
  font-size: var(--density-font-size-sm);
104
- animation: icen-dp-in var(--icen-tx-fast);
105
- }
106
- @keyframes icen-dp-in {
107
- from { opacity: 0; transform: scale(0.96) translateY(-4px); }
108
- to { opacity: 1; transform: scale(1) translateY(0); }
104
+ animation: icen-pop-in var(--icen-tx-fast);
109
105
  }
110
106
 
111
107
  .date-picker-head {
@@ -189,10 +185,10 @@
189
185
  }
190
186
  .date-picker-cell.is-selected {
191
187
  background: var(--token-accent);
192
- color: var(--token-on-accent, #fff);
188
+ color: var(--token-on-accent);
193
189
  font-weight: 600;
194
190
  }
195
- .date-picker-cell.is-selected.is-today { border-color: var(--token-on-accent, #fff); }
191
+ .date-picker-cell.is-selected.is-today { border-color: var(--token-on-accent); }
196
192
  .date-picker-cell.is-outside {
197
193
  color: var(--token-text-faint);
198
194
  opacity: 0.55;
@@ -129,7 +129,3 @@
129
129
  @media (max-width: 480px) {
130
130
  .empty--404 .empty-title { font-size: 56px; }
131
131
  }
132
-
133
- @media (prefers-reduced-motion: reduce) {
134
- .empty { animation: none; }
135
- }
@@ -149,7 +149,7 @@
149
149
  /* 不定进度(spinner 替代品):bar 来回扫动 */
150
150
  .progress--indeterminate .progress-bar {
151
151
  width: 35% !important;
152
- animation: progress-sweep 1.4s var(--ease-in-out, var(--ease-out)) infinite;
152
+ animation: progress-sweep var(--progress-sweep-duration, 1.4s) var(--ease-in-out, var(--ease-out)) infinite;
153
153
  }
154
154
  @keyframes progress-sweep {
155
155
  0% { transform: translateX(-100%); }
@@ -173,7 +173,7 @@
173
173
  border: 2px solid color-mix(in srgb, var(--token-accent) 30%, transparent);
174
174
  border-top-color: var(--token-accent);
175
175
  border-radius: var(--radius-full);
176
- animation: icen-spin 0.8s linear infinite;
176
+ animation: icen-spin var(--spinner-duration, 0.8s) linear infinite;
177
177
  }
178
178
  .spinner--sm { width: 14px; height: 14px; border-width: 2px; }
179
179
  .spinner--lg { width: 32px; height: 32px; border-width: 3px; }
@@ -181,8 +181,6 @@
181
181
  .spinner--error { border-color: color-mix(in srgb, var(--token-error) 30%, transparent); border-top-color: var(--token-error); }
182
182
  .spinner--muted { border-color: color-mix(in srgb, var(--token-text-faint) 30%, transparent); border-top-color: var(--token-text-faint); }
183
183
 
184
- @keyframes icen-spin { to { transform: rotate(360deg); } }
185
-
186
184
  /* 文字+spinner 组合 */
187
185
  .spinner-wrap {
188
186
  display: inline-flex;
@@ -197,7 +195,7 @@
197
195
  display: block;
198
196
  background: color-mix(in srgb, var(--token-text) 8%, transparent);
199
197
  border-radius: var(--radius-sm);
200
- animation: icen-skeleton-pulse 2s ease-in-out infinite;
198
+ animation: icen-skeleton-pulse var(--skeleton-duration, 2s) ease-in-out infinite;
201
199
  }
202
200
  .skeleton--circle { border-radius: var(--radius-full); }
203
201
  .skeleton--text {
@@ -206,11 +204,6 @@
206
204
  }
207
205
  .skeleton--text:last-child { width: 60%; }
208
206
 
209
- @keyframes icen-skeleton-pulse {
210
- 0%, 100% { opacity: 1; }
211
- 50% { opacity: 0.5; }
212
- }
213
-
214
207
  /* ── alert 增强 ── */
215
208
 
216
209
  /* 可操作横幅(含按钮) */
@@ -304,7 +297,7 @@
304
297
  transparent
305
298
  );
306
299
  background-size: 20px 20px;
307
- animation: progress-stripe 1s linear infinite;
300
+ animation: progress-stripe var(--progress-stripe-duration, 1s) linear infinite;
308
301
  }
309
302
  @keyframes progress-stripe {
310
303
  from { background-position: 0 0; }
@@ -336,7 +329,7 @@
336
329
  height: 6px;
337
330
  border-radius: var(--radius-full);
338
331
  background: var(--token-accent);
339
- animation: spinner-dot 1.4s ease-in-out infinite;
332
+ animation: spinner-dot var(--spinner-dots-duration, 1.4s) ease-in-out infinite;
340
333
  }
341
334
  .spinner--dots span:nth-child(2) { animation-delay: 0.2s; }
342
335
  .spinner--dots span:nth-child(3) { animation-delay: 0.4s; }
@@ -362,7 +355,7 @@
362
355
  height: 100%;
363
356
  background: var(--token-accent);
364
357
  border-radius: 1px;
365
- animation: spinner-bar 1s ease-in-out infinite;
358
+ animation: spinner-bar var(--spinner-bars-duration, 1s) ease-in-out infinite;
366
359
  }
367
360
  .spinner--bars span:nth-child(2) { animation-delay: 0.1s; }
368
361
  .spinner--bars span:nth-child(3) { animation-delay: 0.2s; }
@@ -376,7 +369,7 @@
376
369
  .spinner-overlay {
377
370
  position: fixed;
378
371
  inset: 0;
379
- z-index: var(--z-overlay, 9999);
372
+ z-index: var(--z-dialog);
380
373
  display: flex;
381
374
  align-items: center;
382
375
  justify-content: center;
@@ -422,7 +415,7 @@
422
415
  height: 12px;
423
416
  border-radius: var(--radius-xs);
424
417
  background: color-mix(in srgb, var(--token-text) 8%, transparent);
425
- animation: icen-skeleton-pulse 2s ease-in-out infinite;
418
+ animation: icen-skeleton-pulse var(--skeleton-duration, 2s) ease-in-out infinite;
426
419
  }
427
420
 
428
421
  /* 骨架波纹变体(shimmer,区别于 pulse) */
@@ -434,7 +427,7 @@
434
427
  color-mix(in srgb, var(--token-text) 6%, transparent) 100%
435
428
  );
436
429
  background-size: 200% 100%;
437
- animation: skeleton-shimmer 1.5s ease-in-out infinite;
430
+ animation: skeleton-shimmer var(--skeleton-shimmer-duration, 1.5s) ease-in-out infinite;
438
431
  }
439
432
  @keyframes skeleton-shimmer {
440
433
  from { background-position: 200% 0; }
@@ -446,7 +439,7 @@
446
439
  .progress--indeterminate .progress-bar { animation: none; width: 100% !important; }
447
440
  .progress--striped .progress-bar { animation: none; }
448
441
  .progress-ring-fill { transition: none; }
449
- .spinner { animation-duration: 2s; }
442
+ .spinner { --spinner-duration: 2s; }
450
443
  .spinner--dots span,
451
444
  .spinner--bars span { animation: none; }
452
445
  .skeleton,
@@ -148,7 +148,7 @@
148
148
 
149
149
  /* label 带可选标记 */
150
150
  .form-label-optional {
151
- font-size: 10px;
151
+ font-size: var(--density-font-size-2xs);
152
152
  font-weight: 400;
153
153
  color: var(--token-text-faint);
154
154
  margin-left: 4px;
@@ -229,11 +229,11 @@
229
229
  border: 2px solid color-mix(in srgb, var(--token-accent) 30%, transparent);
230
230
  border-top-color: var(--token-accent);
231
231
  border-radius: 50%;
232
- animation: form-loading-spin 0.8s linear infinite;
232
+ animation: icen-spin var(--form-loading-duration, 0.8s) linear infinite;
233
233
  }
234
- @keyframes form-loading-spin { to { transform: rotate(360deg); } }
235
234
 
236
- /* ── 页面级横幅:操作结果 / 引导提示 ── */
235
+ /* ── 页面级横幅:操作结果 / 引导提示 ──
236
+ 与 feedback.css 的 alert 共享同一套视觉 token(--banner-* 仅在底色/文字/描边上分流) */
237
237
  .banner-ok,
238
238
  .banner-err {
239
239
  display: flex;
@@ -245,23 +245,22 @@
245
245
  font-size: var(--density-font-size-sm);
246
246
  line-height: 1.5;
247
247
  border: 1px solid transparent;
248
+ background: var(--banner-bg);
249
+ color: var(--banner-fg);
250
+ border-color: var(--banner-border);
248
251
  }
249
252
  .banner-ok {
250
- background: var(--token-success-muted);
251
- color: var(--token-success);
252
- border-color: color-mix(in srgb, var(--token-success) 25%, transparent);
253
+ --banner-bg: var(--token-success-muted);
254
+ --banner-fg: var(--token-success);
255
+ --banner-border: color-mix(in srgb, var(--token-success) 25%, transparent);
253
256
  }
254
257
  .banner-err {
255
- background: var(--token-error-muted);
256
- color: var(--token-error);
257
- border-color: color-mix(in srgb, var(--token-error) 25%, transparent);
258
+ --banner-bg: var(--token-error-muted);
259
+ --banner-fg: var(--token-error);
260
+ --banner-border: color-mix(in srgb, var(--token-error) 25%, transparent);
258
261
  }
259
262
  /* block 模式:横向占满,带图标位 */
260
263
  .banner-block {
261
264
  align-items: center;
262
265
  padding: var(--density-space-md);
263
266
  }
264
-
265
- @media (prefers-reduced-motion: reduce) {
266
- .banner-ok, .banner-err { transition: none; }
267
- }
@@ -19,6 +19,7 @@
19
19
 
20
20
  /* ── 单行 ── */
21
21
  .input-wrap {
22
+ position: relative;
22
23
  display: flex;
23
24
  align-items: center;
24
25
  gap: var(--density-space-xs);
@@ -140,6 +141,7 @@
140
141
 
141
142
  /* ── 多行:同一 wrapper 视觉,内部 textarea 裸奔 ── */
142
143
  .textarea-wrap {
144
+ position: relative;
143
145
  display: flex;
144
146
  width: 100%;
145
147
  background: var(--token-control);
@@ -189,7 +191,6 @@
189
191
  }
190
192
 
191
193
  /* 字符计数(behavior 可挂到 .input-wrap 或 .textarea-wrap 内尾部) */
192
- .input-wrap { position: relative; }
193
194
  .input-wrap--has-counter { padding-right: calc(var(--density-space-sm) + 48px); }
194
195
  .input-wrap--has-counter .input-counter {
195
196
  position: absolute;
@@ -214,7 +215,6 @@
214
215
  .input-counter.is-warn { color: var(--token-warning); }
215
216
  .input-counter.is-error { color: var(--token-error); font-weight: 600; }
216
217
 
217
- .textarea-wrap { position: relative; }
218
218
  .textarea-wrap .textarea { padding-bottom: 24px; }
219
219
 
220
220
  /* 有计数器时的 textarea 底部留空 */