@icen.ai/ui 0.4.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.
Files changed (155) hide show
  1. package/AGENTS.md +256 -0
  2. package/README.md +47 -13
  3. package/dist/base.css +31 -4
  4. package/dist/behaviors/accordion.d.mts +4 -0
  5. package/dist/behaviors/accordion.mjs +4 -3
  6. package/dist/behaviors/ai-chat.d.mts +57 -0
  7. package/dist/behaviors/ai-chat.mjs +557 -0
  8. package/dist/behaviors/ai-composer.d.mts +112 -0
  9. package/dist/behaviors/ai-composer.mjs +2032 -0
  10. package/dist/behaviors/ai-core.d.mts +151 -0
  11. package/dist/behaviors/ai-core.mjs +491 -0
  12. package/dist/behaviors/ai-diff.d.mts +53 -0
  13. package/dist/behaviors/ai-diff.mjs +282 -0
  14. package/dist/behaviors/ai-panel.d.mts +98 -0
  15. package/dist/behaviors/ai-panel.mjs +1012 -0
  16. package/dist/behaviors/ai-provider.d.mts +198 -0
  17. package/dist/behaviors/ai-provider.mjs +1055 -0
  18. package/dist/behaviors/ai-tool.d.mts +18 -0
  19. package/dist/behaviors/ai-tool.mjs +2125 -0
  20. package/dist/behaviors/ai-tools.d.mts +81 -0
  21. package/dist/behaviors/ai-tools.mjs +1658 -0
  22. package/dist/behaviors/back-top.d.mts +20 -0
  23. package/dist/behaviors/back-top.mjs +101 -0
  24. package/dist/behaviors/carousel.d.mts +4 -0
  25. package/dist/behaviors/carousel.mjs +34 -16
  26. package/dist/behaviors/charts.d.mts +250 -0
  27. package/dist/behaviors/charts.mjs +1301 -448
  28. package/dist/behaviors/command-palette.d.mts +8 -0
  29. package/dist/behaviors/command-palette.mjs +189 -0
  30. package/dist/behaviors/context-menu.d.mts +36 -0
  31. package/dist/behaviors/context-menu.mjs +6 -2
  32. package/dist/behaviors/copy.d.mts +4 -0
  33. package/dist/behaviors/copy.mjs +7 -6
  34. package/dist/behaviors/datatable.d.mts +78 -0
  35. package/dist/behaviors/datatable.mjs +54 -11
  36. package/dist/behaviors/date-picker.d.mts +4 -0
  37. package/dist/behaviors/date-picker.mjs +296 -0
  38. package/dist/behaviors/dropdown.d.mts +4 -0
  39. package/dist/behaviors/dropdown.mjs +7 -3
  40. package/dist/behaviors/input.d.mts +4 -0
  41. package/dist/behaviors/input.mjs +184 -8
  42. package/dist/behaviors/modal.d.mts +11 -0
  43. package/dist/behaviors/modal.mjs +11 -6
  44. package/dist/behaviors/nav.d.mts +7 -0
  45. package/dist/behaviors/nav.mjs +25 -9
  46. package/dist/behaviors/notification.d.mts +172 -0
  47. package/dist/behaviors/notification.mjs +908 -0
  48. package/dist/behaviors/popover.d.mts +46 -0
  49. package/dist/behaviors/popover.mjs +2 -1
  50. package/dist/behaviors/select.d.mts +4 -0
  51. package/dist/behaviors/select.mjs +268 -34
  52. package/dist/behaviors/sidebar.d.mts +4 -0
  53. package/dist/behaviors/sidebar.mjs +4 -3
  54. package/dist/behaviors/slider.d.mts +4 -0
  55. package/dist/behaviors/slider.mjs +4 -3
  56. package/dist/behaviors/split-pane.d.mts +4 -0
  57. package/dist/behaviors/split-pane.mjs +102 -0
  58. package/dist/behaviors/tabs.d.mts +8 -0
  59. package/dist/behaviors/tabs.mjs +44 -19
  60. package/dist/behaviors/tag-input.d.mts +4 -0
  61. package/dist/behaviors/tag-input.mjs +12 -3
  62. package/dist/behaviors/theme.d.mts +25 -0
  63. package/dist/behaviors/theme.mjs +3 -1
  64. package/dist/behaviors/toast.d.mts +26 -0
  65. package/dist/behaviors/toast.mjs +24 -18
  66. package/dist/behaviors/tree.d.mts +4 -0
  67. package/dist/behaviors/tree.mjs +4 -3
  68. package/dist/behaviors/upload.d.mts +4 -0
  69. package/dist/behaviors/upload.mjs +205 -17
  70. package/dist/components/ai-chat.css +1037 -0
  71. package/dist/components/ai-chat.mjs +3 -0
  72. package/dist/components/ai-composer.mjs +3 -0
  73. package/dist/components/ai-context.mjs +2 -0
  74. package/dist/components/ai-diff.css +311 -0
  75. package/dist/components/ai-diff.mjs +3 -0
  76. package/dist/components/ai-files.mjs +3 -0
  77. package/dist/components/ai-message.mjs +3 -0
  78. package/dist/components/ai-panel.css +682 -0
  79. package/dist/components/ai-reasoning.mjs +3 -0
  80. package/dist/components/ai-subagent.mjs +2 -0
  81. package/dist/components/ai-todo.mjs +2 -0
  82. package/dist/components/ai-tool-call.mjs +2 -0
  83. package/dist/components/ai-tool.css +532 -0
  84. package/dist/components/ai-usage.mjs +3 -0
  85. package/dist/components/back-top.css +88 -0
  86. package/dist/components/back-top.mjs +3 -0
  87. package/dist/components/badge.css +127 -0
  88. package/dist/components/badge.mjs +2 -0
  89. package/dist/components/breadcrumb.css +53 -0
  90. package/dist/components/btn.css +4 -7
  91. package/dist/components/card.css +66 -1
  92. package/dist/components/chart-bar.css +10 -0
  93. package/dist/components/chart-calendar.mjs +5 -0
  94. package/dist/components/chart-gauge.css +1 -1
  95. package/dist/components/chart-heatmap.css +40 -0
  96. package/dist/components/chart-scatter.css +24 -0
  97. package/dist/components/chart-scatter.mjs +4 -0
  98. package/dist/components/charts.css +86 -1
  99. package/dist/components/charts.mjs +1 -0
  100. package/dist/components/command-palette.css +246 -0
  101. package/dist/components/command-palette.mjs +3 -0
  102. package/dist/components/content.css +221 -8
  103. package/dist/components/copy.css +93 -0
  104. package/dist/components/copy.mjs +3 -0
  105. package/dist/components/datatable.css +4 -8
  106. package/dist/components/date-picker.css +240 -0
  107. package/dist/components/date-picker.mjs +3 -0
  108. package/dist/components/empty.css +69 -3
  109. package/dist/components/feedback.css +241 -10
  110. package/dist/components/form.css +129 -11
  111. package/dist/components/form.mjs +2 -0
  112. package/dist/components/input.css +25 -2
  113. package/dist/components/layout.css +36 -36
  114. package/dist/components/media.css +153 -6
  115. package/dist/components/menu.css +2 -2
  116. package/dist/components/modal.css +1 -1
  117. package/dist/components/nav.css +106 -2
  118. package/dist/components/notification.css +387 -0
  119. package/dist/components/notification.mjs +3 -0
  120. package/dist/components/pagination.css +1 -1
  121. package/dist/components/panel.css +104 -9
  122. package/dist/components/pill.css +82 -12
  123. package/dist/components/scroll-area.css +112 -0
  124. package/dist/components/scroll-area.mjs +2 -0
  125. package/dist/components/segmented.css +80 -5
  126. package/dist/components/segmented.mjs +1 -0
  127. package/dist/components/select.css +104 -17
  128. package/dist/components/sidebar.css +119 -1
  129. package/dist/components/slider.css +113 -0
  130. package/dist/components/split-pane.css +111 -0
  131. package/dist/components/split-pane.mjs +3 -0
  132. package/dist/components/stat.css +131 -12
  133. package/dist/components/steps.css +98 -1
  134. package/dist/components/switch.css +145 -7
  135. package/dist/components/table.css +111 -29
  136. package/dist/components/tabs.css +132 -22
  137. package/dist/components/tag-input.css +4 -4
  138. package/dist/components/tag.css +164 -0
  139. package/dist/components/tag.mjs +2 -0
  140. package/dist/components/toast.css +122 -5
  141. package/dist/components/toolbar.css +101 -0
  142. package/dist/components/toolbar.mjs +2 -0
  143. package/dist/components/tooltip.css +2 -2
  144. package/dist/components/upload.css +61 -4
  145. package/dist/index.d.mts +37 -0
  146. package/dist/index.mjs +9409 -929
  147. package/dist/registry.json +176 -4
  148. package/dist/retro-effects.css +21 -17
  149. package/dist/tokens/colors.css +4 -1
  150. package/dist/tokens/retro-effects.css +21 -17
  151. package/dist/tokens/style-profiles.css +21 -0
  152. package/dist/tokens.css +25 -1
  153. package/dist/ui.css +9616 -2839
  154. package/docs/spec/ai-native.md +659 -0
  155. package/package.json +15 -5
@@ -0,0 +1,240 @@
1
+ /*
2
+ * @icen.ai/ui — Component: date-picker(日期选择,与 behaviors/date-picker 配套)
3
+ *
4
+ * 契约:
5
+ * <div class="date-picker" data-date-picker data-date-picker-input="id">
6
+ * <button class="date-picker-trigger" type="button" aria-haspopup="dialog">
7
+ * <span class="date-picker-value">2026-07-29</span>
8
+ * <svg class="date-picker-icon">…日历 svg…</svg>
9
+ * </button>
10
+ * <input type="hidden" id="id" name="date" value="2026-07-29" />
11
+ * </div>
12
+ *
13
+ * 或直接挂到普通 input(behavior 自动包裹触发器外观):
14
+ * <input class="date-picker-input" data-date-picker value="2026-07-29" />
15
+ *
16
+ * behavior 渲染面板(portal 到 body):
17
+ * <div class="date-picker-panel" role="dialog">
18
+ * <div class="date-picker-head">
19
+ * <button class="date-picker-nav" data-prev>‹</button>
20
+ * <span class="date-picker-title">2026 年 7 月</span>
21
+ * <button class="date-picker-nav" data-next>›</button>
22
+ * </div>
23
+ * <div class="date-picker-weekdays">
24
+ * <span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span><span>日</span>
25
+ * </div>
26
+ * <div class="date-picker-grid">
27
+ * <button class="date-picker-cell [.is-outside] [.is-today] [.is-selected] [.is-disabled]">15</button>
28
+ * …
29
+ * </div>
30
+ * <div class="date-picker-foot">
31
+ * <button class="date-picker-today">今天</button>
32
+ * <button class="date-picker-clear">清除</button>
33
+ * </div>
34
+ * </div>
35
+ *
36
+ * 可选 data-*:
37
+ * data-date-picker-format="yyyy-mm-dd" 显示格式
38
+ * data-date-picker-min="2026-01-01" 最小可选日期
39
+ * data-date-picker-max="2026-12-31" 最大可选日期
40
+ * data-date-picker-week-start="0" 周首日(0=周日,1=周一,默认 1)
41
+ */
42
+
43
+ .date-picker {
44
+ display: inline-block;
45
+ position: relative;
46
+ }
47
+
48
+ .date-picker-trigger {
49
+ display: inline-flex;
50
+ align-items: center;
51
+ gap: var(--density-space-xs);
52
+ width: 100%;
53
+ height: var(--density-input-height-md);
54
+ padding: 0 var(--density-space-sm);
55
+ background: var(--token-control);
56
+ border: 1px solid var(--token-outline-soft);
57
+ border-radius: var(--radius-md);
58
+ box-shadow: var(--token-shadow-inset);
59
+ color: var(--token-text);
60
+ font: inherit;
61
+ font-size: var(--density-font-size-md);
62
+ cursor: pointer;
63
+ -webkit-tap-highlight-color: transparent;
64
+ transition: border-color var(--icen-tx-fast), box-shadow var(--icen-tx-fast);
65
+ }
66
+ .date-picker-trigger:hover { border-color: var(--icen-hover-border); }
67
+ .date-picker-trigger.is-open {
68
+ border-color: var(--icen-control-focus-border);
69
+ box-shadow: var(--icen-control-focus-shadow);
70
+ }
71
+ .date-picker-trigger:focus-visible {
72
+ outline: none;
73
+ border-color: var(--icen-control-focus-border);
74
+ box-shadow: var(--icen-control-focus-shadow);
75
+ }
76
+ .date-picker-value {
77
+ flex: 1;
78
+ min-width: 0;
79
+ text-align: left;
80
+ overflow: hidden;
81
+ text-overflow: ellipsis;
82
+ white-space: nowrap;
83
+ font-variant-numeric: tabular-nums;
84
+ }
85
+ .date-picker-value.is-empty { color: color-mix(in srgb, var(--token-text-faint) 60%, transparent); }
86
+ .date-picker-icon { display: inline-flex; color: var(--token-text-faint); flex-shrink: 0; }
87
+ .date-picker-icon svg { width: 14px; height: 14px; }
88
+
89
+ /* 尺寸 */
90
+ .date-picker-trigger.date-picker-trigger--sm { height: var(--density-input-height-sm); font-size: var(--density-font-size-sm); }
91
+ .date-picker-trigger.date-picker-trigger--lg { height: var(--density-input-height-lg); font-size: var(--density-font-size-lg); }
92
+
93
+ /* ── 面板 ── */
94
+ .date-picker-panel {
95
+ position: fixed;
96
+ z-index: var(--z-popover);
97
+ width: 280px;
98
+ padding: var(--density-space-sm);
99
+ background: var(--token-card-strong);
100
+ border: 1px solid var(--token-outline);
101
+ border-radius: var(--radius-md);
102
+ box-shadow: var(--token-shadow-overlay);
103
+ font-size: var(--density-font-size-sm);
104
+ animation: icen-pop-in var(--icen-tx-fast);
105
+ }
106
+
107
+ .date-picker-head {
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: space-between;
111
+ margin-bottom: var(--density-space-sm);
112
+ }
113
+ .date-picker-title {
114
+ font-weight: 600;
115
+ color: var(--token-text);
116
+ font-variant-numeric: tabular-nums;
117
+ flex: 1;
118
+ text-align: center;
119
+ }
120
+ .date-picker-nav {
121
+ display: inline-flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ width: 24px;
125
+ height: 24px;
126
+ padding: 0;
127
+ border: 0;
128
+ border-radius: var(--radius-xs);
129
+ background: transparent;
130
+ color: var(--token-text-muted);
131
+ font-size: var(--density-font-size-md);
132
+ cursor: pointer;
133
+ -webkit-tap-highlight-color: transparent;
134
+ transition: color var(--icen-tx-fast), background-color var(--icen-tx-fast);
135
+ }
136
+ .date-picker-nav:hover { color: var(--token-text); background: var(--token-fill-1); }
137
+ .date-picker-nav:focus-visible {
138
+ outline: none;
139
+ box-shadow: var(--icen-ring);
140
+ }
141
+
142
+ .date-picker-weekdays {
143
+ display: grid;
144
+ grid-template-columns: repeat(7, 1fr);
145
+ gap: 2px;
146
+ margin-bottom: 4px;
147
+ }
148
+ .date-picker-weekdays > span {
149
+ display: flex;
150
+ align-items: center;
151
+ justify-content: center;
152
+ height: 24px;
153
+ font-size: var(--density-font-size-xs);
154
+ color: var(--token-text-faint);
155
+ font-weight: 500;
156
+ }
157
+
158
+ .date-picker-grid {
159
+ display: grid;
160
+ grid-template-columns: repeat(7, 1fr);
161
+ gap: 2px;
162
+ }
163
+ .date-picker-cell {
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ height: 30px;
168
+ padding: 0;
169
+ border: 0;
170
+ border-radius: var(--radius-sm);
171
+ background: transparent;
172
+ color: var(--token-text);
173
+ font: inherit;
174
+ font-size: var(--density-font-size-sm);
175
+ font-variant-numeric: tabular-nums;
176
+ cursor: pointer;
177
+ -webkit-tap-highlight-color: transparent;
178
+ transition: background-color var(--icen-tx-fast), color var(--icen-tx-fast);
179
+ }
180
+ .date-picker-cell:hover { background: var(--token-fill-1); }
181
+ .date-picker-cell.is-today {
182
+ border: 1px solid var(--token-accent);
183
+ color: var(--token-accent);
184
+ font-weight: 600;
185
+ }
186
+ .date-picker-cell.is-selected {
187
+ background: var(--token-accent);
188
+ color: var(--token-on-accent);
189
+ font-weight: 600;
190
+ }
191
+ .date-picker-cell.is-selected.is-today { border-color: var(--token-on-accent); }
192
+ .date-picker-cell.is-outside {
193
+ color: var(--token-text-faint);
194
+ opacity: 0.55;
195
+ }
196
+ .date-picker-cell.is-disabled {
197
+ color: var(--token-text-faint);
198
+ opacity: 0.35;
199
+ cursor: not-allowed;
200
+ pointer-events: none;
201
+ }
202
+ .date-picker-cell:focus-visible {
203
+ outline: none;
204
+ box-shadow: var(--icen-ring);
205
+ }
206
+
207
+ .date-picker-foot {
208
+ display: flex;
209
+ justify-content: space-between;
210
+ gap: var(--density-space-xs);
211
+ margin-top: var(--density-space-sm);
212
+ padding-top: var(--density-space-sm);
213
+ border-top: 1px solid var(--token-line-soft);
214
+ }
215
+ .date-picker-today,
216
+ .date-picker-clear {
217
+ padding: 4px var(--density-space-sm);
218
+ border: 0;
219
+ border-radius: var(--radius-xs);
220
+ background: transparent;
221
+ color: var(--token-accent);
222
+ font: inherit;
223
+ font-size: var(--density-font-size-xs);
224
+ cursor: pointer;
225
+ -webkit-tap-highlight-color: transparent;
226
+ transition: background-color var(--icen-tx-fast);
227
+ }
228
+ .date-picker-today:hover,
229
+ .date-picker-clear:hover { background: var(--token-fill-1); }
230
+ .date-picker-today:focus-visible,
231
+ .date-picker-clear:focus-visible {
232
+ outline: none;
233
+ box-shadow: var(--icen-ring);
234
+ }
235
+ .date-picker-clear { color: var(--token-text-muted); }
236
+
237
+ @media (prefers-reduced-motion: reduce) {
238
+ .date-picker-panel { animation: none; }
239
+ .date-picker-cell { transition: none; }
240
+ }
@@ -0,0 +1,3 @@
1
+ // @icen.ai/ui — kit: date-picker(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './date-picker.css';
3
+ export * from '../behaviors/date-picker.mjs';
@@ -1,16 +1,25 @@
1
1
  /*
2
- * @icen.ai/ui — Component: empty(空状态 / 无数据 / 404 占位)
2
+ * @icen.ai/ui — Component: empty(空状态 / 无数据 / 404 / 错误 / 维护中)
3
3
  *
4
4
  * 完整契约:
5
- * <div class="empty [.empty--sm|.empty--lg]">
5
+ * <div class="empty [.empty--sm|.empty--lg]
6
+ * [.empty--error|.empty--404|.empty--search|.empty--maintenance|.empty--network]">
6
7
  * <div class="empty-icon">…svg…</div> ← 可选,缺省显示默认占位
7
8
  * <h3 class="empty-title">暂无数据</h3>
8
9
  * <p class="empty-desc">描述文案(可选)</p>
9
10
  * <div class="empty-action"> ← 可选:操作按钮位
10
11
  * <button class="btn btn-primary">…</button>
12
+ * <button class="btn">…</button>
11
13
  * </div>
12
14
  * </div>
13
15
  *
16
+ * 类型预设(改图标底色与基调):
17
+ * .empty--error 错误(红底图标)
18
+ * .empty--404 404(中性大字)
19
+ * .empty--search 搜索无结果
20
+ * .empty--maintenance 维护中(warning 底)
21
+ * .empty--network 网络错误(info 底)
22
+ *
14
23
  * 规格源自 opengal src/presentation/ui/data/display.tsx 的 EmptyState。
15
24
  */
16
25
 
@@ -25,6 +34,7 @@
25
34
  .empty--sm { padding: var(--density-space-md); }
26
35
  .empty--lg { padding: 64px var(--density-space-md); }
27
36
 
37
+ /* ── 图标 ── */
28
38
  .empty-icon {
29
39
  width: 48px;
30
40
  height: 48px;
@@ -44,6 +54,28 @@
44
54
  .empty--lg .empty-icon svg { width: 32px; height: 32px; }
45
55
  .empty--sm .empty-icon svg { width: 18px; height: 18px; }
46
56
 
57
+ /* ── 类型预设 ── */
58
+ .empty--error .empty-icon {
59
+ background: color-mix(in srgb, var(--token-error) 10%, transparent);
60
+ border-color: color-mix(in srgb, var(--token-error) 20%, transparent);
61
+ color: var(--token-error);
62
+ }
63
+ .empty--maintenance .empty-icon {
64
+ background: color-mix(in srgb, var(--token-warning) 10%, transparent);
65
+ border-color: color-mix(in srgb, var(--token-warning) 20%, transparent);
66
+ color: var(--token-warning);
67
+ }
68
+ .empty--network .empty-icon {
69
+ background: color-mix(in srgb, var(--token-info) 10%, transparent);
70
+ border-color: color-mix(in srgb, var(--token-info) 20%, transparent);
71
+ color: var(--token-info);
72
+ }
73
+ .empty--search .empty-icon {
74
+ background: var(--token-fill-1);
75
+ color: var(--token-text-muted);
76
+ }
77
+
78
+ /* ── 标题与描述 ── */
47
79
  .empty-title {
48
80
  font-size: var(--density-font-size-md);
49
81
  font-weight: 500;
@@ -51,15 +83,49 @@
51
83
  margin: 0;
52
84
  line-height: 1.4;
53
85
  }
86
+ .empty--error .empty-title { color: var(--token-error); }
54
87
  .empty-desc {
55
88
  font-size: var(--density-font-size-sm);
56
89
  color: var(--token-text-muted);
57
90
  margin: 4px 0 0;
58
- max-width: 240px;
91
+ max-width: 280px;
59
92
  line-height: 1.5;
60
93
  }
94
+
95
+ /* ── 操作区 ── */
61
96
  .empty-action {
62
97
  margin-top: var(--density-space-md);
63
98
  display: flex;
64
99
  gap: var(--density-space-xs);
100
+ flex-wrap: wrap;
101
+ justify-content: center;
102
+ }
103
+
104
+ /* ── 404 大数字 ── */
105
+ .empty--404 .empty-title {
106
+ font-size: 72px;
107
+ font-weight: 800;
108
+ font-family: var(--font-heading);
109
+ line-height: 1;
110
+ letter-spacing: -0.02em;
111
+ color: var(--token-text-faint);
112
+ margin-bottom: var(--density-space-sm);
113
+ }
114
+ .empty--404 .empty-icon { display: none; }
115
+
116
+ /* ── 插画背景(使用方可用渐变背景替代图标) ── */
117
+ .empty--illustrated {
118
+ background: radial-gradient(ellipse at center,
119
+ color-mix(in srgb, var(--token-accent) 6%, transparent) 0%,
120
+ transparent 70%);
121
+ border-radius: var(--radius-lg);
122
+ }
123
+
124
+ /* ── 内嵌模式(empty 嵌入卡片/面板内时无 padding) ── */
125
+ .empty--inline {
126
+ padding: var(--density-space-md) var(--density-space-sm);
127
+ }
128
+
129
+ @media (max-width: 480px) {
130
+ .empty--404 .empty-title { font-size: 56px; }
65
131
  }
@@ -15,6 +15,8 @@
15
15
  box-shadow: var(--token-shadow-inset);
16
16
  border: 1px solid transparent;
17
17
  font-size: var(--density-font-size-sm);
18
+ position: relative;
19
+ overflow: hidden;
18
20
  }
19
21
  .alert--info { background: var(--token-info-muted); border-color: color-mix(in srgb, var(--token-info) 18%, transparent); }
20
22
  .alert--success { background: var(--token-success-muted); border-color: color-mix(in srgb, var(--token-success) 18%, transparent); }
@@ -147,7 +149,7 @@
147
149
  /* 不定进度(spinner 替代品):bar 来回扫动 */
148
150
  .progress--indeterminate .progress-bar {
149
151
  width: 35% !important;
150
- 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;
151
153
  }
152
154
  @keyframes progress-sweep {
153
155
  0% { transform: translateX(-100%); }
@@ -171,7 +173,7 @@
171
173
  border: 2px solid color-mix(in srgb, var(--token-accent) 30%, transparent);
172
174
  border-top-color: var(--token-accent);
173
175
  border-radius: var(--radius-full);
174
- animation: icen-spin 0.8s linear infinite;
176
+ animation: icen-spin var(--spinner-duration, 0.8s) linear infinite;
175
177
  }
176
178
  .spinner--sm { width: 14px; height: 14px; border-width: 2px; }
177
179
  .spinner--lg { width: 32px; height: 32px; border-width: 3px; }
@@ -179,8 +181,6 @@
179
181
  .spinner--error { border-color: color-mix(in srgb, var(--token-error) 30%, transparent); border-top-color: var(--token-error); }
180
182
  .spinner--muted { border-color: color-mix(in srgb, var(--token-text-faint) 30%, transparent); border-top-color: var(--token-text-faint); }
181
183
 
182
- @keyframes icen-spin { to { transform: rotate(360deg); } }
183
-
184
184
  /* 文字+spinner 组合 */
185
185
  .spinner-wrap {
186
186
  display: inline-flex;
@@ -195,7 +195,7 @@
195
195
  display: block;
196
196
  background: color-mix(in srgb, var(--token-text) 8%, transparent);
197
197
  border-radius: var(--radius-sm);
198
- animation: icen-skeleton-pulse 2s ease-in-out infinite;
198
+ animation: icen-skeleton-pulse var(--skeleton-duration, 2s) ease-in-out infinite;
199
199
  }
200
200
  .skeleton--circle { border-radius: var(--radius-full); }
201
201
  .skeleton--text {
@@ -204,14 +204,245 @@
204
204
  }
205
205
  .skeleton--text:last-child { width: 60%; }
206
206
 
207
- @keyframes icen-skeleton-pulse {
208
- 0%, 100% { opacity: 1; }
209
- 50% { opacity: 0.5; }
207
+ /* ── alert 增强 ── */
208
+
209
+ /* 可操作横幅(含按钮) */
210
+ .alert-actions {
211
+ display: flex;
212
+ gap: var(--density-space-xs);
213
+ margin-top: var(--density-space-xs);
214
+ }
215
+ .alert--banner {
216
+ border-radius: 0;
217
+ border-left: none;
218
+ border-right: none;
219
+ border-top: none;
220
+ }
221
+ .alert--inline {
222
+ display: inline-flex;
223
+ padding: 2px var(--density-space-sm);
224
+ font-size: var(--density-font-size-xs);
225
+ }
226
+ .alert--inline .alert-close { width: 16px; height: 16px; }
227
+
228
+ /* ── result 增强 ── */
229
+ .result-list {
230
+ margin: var(--density-space-sm) 0 0;
231
+ padding: var(--density-space-sm);
232
+ background: var(--token-bg-soft);
233
+ border-radius: var(--radius-md);
234
+ font-size: var(--density-font-size-sm);
235
+ color: var(--token-text-muted);
236
+ text-align: left;
237
+ max-width: 400px;
238
+ max-height: 200px;
239
+ overflow-y: auto;
240
+ }
241
+ .result-list-item {
242
+ display: flex;
243
+ align-items: flex-start;
244
+ gap: var(--density-space-xs);
245
+ padding: 2px 0;
246
+ font-variant-numeric: tabular-nums;
247
+ word-break: break-all;
248
+ }
249
+
250
+ /* ── progress 环形 ── */
251
+ .progress-ring-wrap {
252
+ display: inline-flex;
253
+ align-items: center;
254
+ justify-content: center;
255
+ position: relative;
256
+ }
257
+ .progress-ring {
258
+ transform: rotate(-90deg);
259
+ }
260
+ .progress-ring-track {
261
+ fill: none;
262
+ stroke: var(--token-fill-2);
263
+ stroke-width: 4;
264
+ }
265
+ .progress-ring-fill {
266
+ fill: none;
267
+ stroke: var(--token-accent);
268
+ stroke-width: 4;
269
+ stroke-linecap: round;
270
+ transition: stroke-dashoffset var(--icen-tx-slow) var(--ease-out);
271
+ }
272
+ .progress-ring-text {
273
+ position: absolute;
274
+ font-size: var(--density-font-size-sm);
275
+ font-weight: 600;
276
+ font-variant-numeric: tabular-nums;
277
+ color: var(--token-text);
278
+ }
279
+ .progress-ring--sm .progress-ring-track,
280
+ .progress-ring--sm .progress-ring-fill { stroke-width: 3; }
281
+ .progress-ring--lg .progress-ring-track,
282
+ .progress-ring--lg .progress-ring-fill { stroke-width: 6; }
283
+ .progress-ring--success .progress-ring-fill { stroke: var(--token-success); }
284
+ .progress-ring--warning .progress-ring-fill { stroke: var(--token-warning); }
285
+ .progress-ring--error .progress-ring-fill { stroke: var(--token-error); }
286
+
287
+ /* progress 条纹动画 */
288
+ .progress--striped .progress-bar {
289
+ background-image: linear-gradient(
290
+ 45deg,
291
+ color-mix(in srgb, var(--token-card-strong) 20%, transparent) 25%,
292
+ transparent 25%,
293
+ transparent 50%,
294
+ color-mix(in srgb, var(--token-card-strong) 20%, transparent) 50%,
295
+ color-mix(in srgb, var(--token-card-strong) 20%, transparent) 75%,
296
+ transparent 75%,
297
+ transparent
298
+ );
299
+ background-size: 20px 20px;
300
+ animation: progress-stripe var(--progress-stripe-duration, 1s) linear infinite;
301
+ }
302
+ @keyframes progress-stripe {
303
+ from { background-position: 0 0; }
304
+ to { background-position: 20px 0; }
305
+ }
306
+
307
+ /* progress 渐变 */
308
+ .progress--gradient .progress-bar {
309
+ background: linear-gradient(90deg,
310
+ var(--token-accent),
311
+ color-mix(in srgb, var(--token-accent) 60%, var(--token-info)));
312
+ }
313
+
314
+ /* ── spinner 变体 ── */
315
+
316
+ /* 点阵 spinner */
317
+ .spinner--dots {
318
+ width: auto;
319
+ height: auto;
320
+ border: none;
321
+ display: inline-flex;
322
+ gap: 3px;
323
+ background: none;
324
+ animation: none;
325
+ }
326
+ .spinner--dots span {
327
+ display: block;
328
+ width: 6px;
329
+ height: 6px;
330
+ border-radius: var(--radius-full);
331
+ background: var(--token-accent);
332
+ animation: spinner-dot var(--spinner-dots-duration, 1.4s) ease-in-out infinite;
333
+ }
334
+ .spinner--dots span:nth-child(2) { animation-delay: 0.2s; }
335
+ .spinner--dots span:nth-child(3) { animation-delay: 0.4s; }
336
+ @keyframes spinner-dot {
337
+ 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
338
+ 40% { opacity: 1; transform: scale(1); }
339
+ }
340
+
341
+ /* 条形 spinner */
342
+ .spinner--bars {
343
+ width: auto;
344
+ height: auto;
345
+ border: none;
346
+ display: inline-flex;
347
+ align-items: flex-end;
348
+ gap: 2px;
349
+ background: none;
350
+ animation: none;
351
+ }
352
+ .spinner--bars span {
353
+ display: block;
354
+ width: 3px;
355
+ height: 100%;
356
+ background: var(--token-accent);
357
+ border-radius: 1px;
358
+ animation: spinner-bar var(--spinner-bars-duration, 1s) ease-in-out infinite;
359
+ }
360
+ .spinner--bars span:nth-child(2) { animation-delay: 0.1s; }
361
+ .spinner--bars span:nth-child(3) { animation-delay: 0.2s; }
362
+ .spinner--bars span:nth-child(4) { animation-delay: 0.3s; }
363
+ @keyframes spinner-bar {
364
+ 0%, 100% { transform: scaleY(0.4); }
365
+ 50% { transform: scaleY(1); }
366
+ }
367
+
368
+ /* 全屏遮罩 spinner */
369
+ .spinner-overlay {
370
+ position: fixed;
371
+ inset: 0;
372
+ z-index: var(--z-dialog);
373
+ display: flex;
374
+ align-items: center;
375
+ justify-content: center;
376
+ background: color-mix(in srgb, var(--token-card-strong) 70%, transparent);
377
+ backdrop-filter: blur(2px);
378
+ -webkit-backdrop-filter: blur(2px);
379
+ flex-direction: column;
380
+ gap: var(--density-space-md);
381
+ }
382
+ .spinner-overlay-text {
383
+ font-size: var(--density-font-size-sm);
384
+ color: var(--token-text-muted);
385
+ }
386
+
387
+ /* ── skeleton 复合体 ── */
388
+
389
+ /* 骨架文本(多行模拟) */
390
+ .skeleton-text-group {
391
+ display: flex;
392
+ flex-direction: column;
393
+ gap: var(--density-space-xs);
394
+ flex: 1;
395
+ }
396
+
397
+ /* 头像 + 文本行组合(评论/消息占位) */
398
+ .skeleton-media {
399
+ display: flex;
400
+ align-items: center;
401
+ gap: var(--density-space-sm);
402
+ }
403
+
404
+ /* 表格骨架 */
405
+ .skeleton-table {
406
+ display: flex;
407
+ flex-direction: column;
408
+ gap: var(--density-space-sm);
409
+ }
410
+ .skeleton-table-row {
411
+ display: flex;
412
+ gap: var(--density-space-sm);
413
+ }
414
+ .skeleton-table-row > * {
415
+ height: 12px;
416
+ border-radius: var(--radius-xs);
417
+ background: color-mix(in srgb, var(--token-text) 8%, transparent);
418
+ animation: icen-skeleton-pulse var(--skeleton-duration, 2s) ease-in-out infinite;
419
+ }
420
+
421
+ /* 骨架波纹变体(shimmer,区别于 pulse) */
422
+ .skeleton--shimmer {
423
+ background: linear-gradient(
424
+ 90deg,
425
+ color-mix(in srgb, var(--token-text) 6%, transparent) 0%,
426
+ color-mix(in srgb, var(--token-text) 12%, transparent) 50%,
427
+ color-mix(in srgb, var(--token-text) 6%, transparent) 100%
428
+ );
429
+ background-size: 200% 100%;
430
+ animation: skeleton-shimmer var(--skeleton-shimmer-duration, 1.5s) ease-in-out infinite;
431
+ }
432
+ @keyframes skeleton-shimmer {
433
+ from { background-position: 200% 0; }
434
+ to { background-position: -200% 0; }
210
435
  }
211
436
 
212
437
  @media (prefers-reduced-motion: reduce) {
213
438
  .progress-bar { transition: none; }
214
439
  .progress--indeterminate .progress-bar { animation: none; width: 100% !important; }
215
- .spinner { animation-duration: 2s; }
216
- .skeleton { animation: none; opacity: 0.6; }
440
+ .progress--striped .progress-bar { animation: none; }
441
+ .progress-ring-fill { transition: none; }
442
+ .spinner { --spinner-duration: 2s; }
443
+ .spinner--dots span,
444
+ .spinner--bars span { animation: none; }
445
+ .skeleton,
446
+ .skeleton-table-row > * { animation: none; opacity: 0.6; }
447
+ .skeleton--shimmer { animation: none; }
217
448
  }