@icen.ai/ui 0.10.0 → 0.11.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.
- package/AGENTS.md +79 -309
- package/LICENSE +21 -0
- package/README.md +43 -54
- package/dist/base.css +68 -22
- package/dist/behaviors/accordion.mjs +1 -1
- package/dist/behaviors/ai-branch.mjs +14 -0
- package/dist/behaviors/ai-chat.d.mts +2 -2
- package/dist/behaviors/ai-chat.mjs +66 -10
- package/dist/behaviors/ai-composer.d.mts +5 -5
- package/dist/behaviors/ai-composer.mjs +34 -10
- package/dist/behaviors/ai-core.mjs +14 -0
- package/dist/behaviors/ai-diff.mjs +14 -0
- package/dist/behaviors/ai-feedback.mjs +14 -0
- package/dist/behaviors/ai-panel.d.mts +2 -2
- package/dist/behaviors/ai-panel.mjs +14 -0
- package/dist/behaviors/ai-provider.d.mts +1 -1
- package/dist/behaviors/ai-provider.mjs +14 -0
- package/dist/behaviors/ai-threads.mjs +14 -0
- package/dist/behaviors/ai-tool.d.mts +1 -1
- package/dist/behaviors/ai-tool.mjs +299 -24
- package/dist/behaviors/ai-tools.mjs +1179 -28
- package/dist/behaviors/autocomplete.d.mts +1 -1
- package/dist/behaviors/autocomplete.mjs +14 -0
- package/dist/behaviors/back-top.mjs +14 -0
- package/dist/behaviors/carousel.mjs +21 -1
- package/dist/behaviors/cascader.mjs +27 -0
- package/dist/behaviors/charts.d.mts +12 -1
- package/dist/behaviors/charts.mjs +298 -20
- package/dist/behaviors/code-block.mjs +22 -1
- package/dist/behaviors/command-palette.mjs +3 -3
- package/dist/behaviors/context-menu.d.mts +2 -2
- package/dist/behaviors/context-menu.mjs +14 -0
- package/dist/behaviors/controls.d.mts +8 -8
- package/dist/behaviors/controls.mjs +50 -6
- package/dist/behaviors/copy.mjs +14 -0
- package/dist/behaviors/datatable.d.mts +3 -3
- package/dist/behaviors/datatable.mjs +89 -11
- package/dist/behaviors/date-picker.mjs +14 -0
- package/dist/behaviors/events.d.mts +10 -10
- package/dist/behaviors/events.mjs +11 -1
- package/dist/behaviors/form-validate.d.mts +10 -1
- package/dist/behaviors/form-validate.mjs +279 -4611
- package/dist/behaviors/i18n.d.mts +14 -0
- package/dist/behaviors/i18n.mjs +28 -0
- package/dist/behaviors/input.mjs +16 -0
- package/dist/behaviors/kb-access.d.mts +2 -2
- package/dist/behaviors/kb-access.mjs +21 -2
- package/dist/behaviors/kb-acl.d.mts +2 -2
- package/dist/behaviors/kb-acl.mjs +14 -0
- package/dist/behaviors/kb-answer.d.mts +2 -2
- package/dist/behaviors/kb-answer.mjs +298 -23
- package/dist/behaviors/kb-audit.d.mts +2 -2
- package/dist/behaviors/kb-audit.mjs +14 -0
- package/dist/behaviors/kb-canvas.d.mts +3 -3
- package/dist/behaviors/kb-canvas.mjs +14 -0
- package/dist/behaviors/kb-chain.d.mts +2 -2
- package/dist/behaviors/kb-chain.mjs +14 -0
- package/dist/behaviors/kb-checkpoint.d.mts +2 -2
- package/dist/behaviors/kb-checkpoint.mjs +14 -0
- package/dist/behaviors/kb-chunks.d.mts +2 -2
- package/dist/behaviors/kb-chunks.mjs +14 -0
- package/dist/behaviors/kb-citation.d.mts +2 -2
- package/dist/behaviors/kb-citation.mjs +33 -6
- package/dist/behaviors/kb-clarify.d.mts +2 -2
- package/dist/behaviors/kb-clarify.mjs +14 -0
- package/dist/behaviors/kb-conflict.d.mts +2 -2
- package/dist/behaviors/kb-conflict.mjs +14 -0
- package/dist/behaviors/kb-connector.d.mts +2 -2
- package/dist/behaviors/kb-connector.mjs +14 -0
- package/dist/behaviors/kb-core.d.mts +1 -1
- package/dist/behaviors/kb-core.mjs +14 -0
- package/dist/behaviors/kb-eval.d.mts +2 -2
- package/dist/behaviors/kb-eval.mjs +21 -2
- package/dist/behaviors/kb-explain.d.mts +2 -2
- package/dist/behaviors/kb-explain.mjs +26 -2
- package/dist/behaviors/kb-filter.d.mts +2 -2
- package/dist/behaviors/kb-filter.mjs +21 -2
- package/dist/behaviors/kb-gap.d.mts +2 -2
- package/dist/behaviors/kb-gap.mjs +14 -0
- package/dist/behaviors/kb-hittest.d.mts +2 -2
- package/dist/behaviors/kb-hittest.mjs +15 -1
- package/dist/behaviors/kb-metadata.mjs +19 -0
- package/dist/behaviors/kb-passage.d.mts +2 -2
- package/dist/behaviors/kb-passage.mjs +14 -0
- package/dist/behaviors/kb-pipeline.d.mts +2 -2
- package/dist/behaviors/kb-pipeline.mjs +14 -0
- package/dist/behaviors/kb-qa.mjs +14 -0
- package/dist/behaviors/kb-rerank.d.mts +2 -2
- package/dist/behaviors/kb-rerank.mjs +14 -0
- package/dist/behaviors/kb-retrieval.d.mts +2 -2
- package/dist/behaviors/kb-retrieval.mjs +31 -4
- package/dist/behaviors/kb-review.d.mts +2 -2
- package/dist/behaviors/kb-review.mjs +21 -2
- package/dist/behaviors/kb-sandbox.d.mts +2 -2
- package/dist/behaviors/kb-sandbox.mjs +20 -1
- package/dist/behaviors/kb-segment.d.mts +2 -2
- package/dist/behaviors/kb-segment.mjs +15 -1
- package/dist/behaviors/kb-select.mjs +21 -2
- package/dist/behaviors/kb-sources.d.mts +2 -2
- package/dist/behaviors/kb-sources.mjs +14 -0
- package/dist/behaviors/kb-sql.d.mts +2 -2
- package/dist/behaviors/kb-sql.mjs +14 -0
- package/dist/behaviors/kb-trace.d.mts +2 -2
- package/dist/behaviors/kb-trace.mjs +22 -3
- package/dist/behaviors/kb-visibility.d.mts +2 -2
- package/dist/behaviors/kb-visibility.mjs +14 -0
- package/dist/behaviors/kb-who-can.d.mts +2 -2
- package/dist/behaviors/kb-who-can.mjs +21 -2
- package/dist/behaviors/mentions.d.mts +1 -1
- package/dist/behaviors/mentions.mjs +14 -0
- package/dist/behaviors/modal.d.mts +1 -1
- package/dist/behaviors/modal.mjs +14 -0
- package/dist/behaviors/motion.d.mts +16 -0
- package/dist/behaviors/motion.mjs +39 -0
- package/dist/behaviors/notification.d.mts +2 -2
- package/dist/behaviors/notification.mjs +27 -3
- package/dist/behaviors/panel.mjs +14 -0
- package/dist/behaviors/scroll-area.d.mts +17 -0
- package/dist/behaviors/scroll-area.mjs +59 -0
- package/dist/behaviors/select.mjs +21 -2
- package/dist/behaviors/slider.mjs +3 -3
- package/dist/behaviors/split-pane.mjs +2 -0
- package/dist/behaviors/suggestions.mjs +14 -0
- package/dist/behaviors/tabs.mjs +10 -0
- package/dist/behaviors/tag-input.mjs +14 -0
- package/dist/behaviors/theme-switcher.d.mts +2 -2
- package/dist/behaviors/theme-switcher.mjs +30 -2
- package/dist/behaviors/theme.mjs +16 -2
- package/dist/behaviors/toast.d.mts +2 -2
- package/dist/behaviors/toast.mjs +14 -0
- package/dist/behaviors/tree-select.mjs +27 -0
- package/dist/behaviors/tree.mjs +4 -4
- package/dist/behaviors/upload.mjs +14 -0
- package/dist/behaviors/url-safety.d.mts +8 -0
- package/dist/behaviors/url-safety.mjs +11 -0
- package/dist/components/accordion.d.mts +2 -0
- package/dist/components/ai-branch.d.mts +2 -0
- package/dist/components/ai-chat.css +40 -33
- package/dist/components/ai-chat.d.mts +2 -0
- package/dist/components/ai-composer.d.mts +2 -0
- package/dist/components/ai-context.d.mts +2 -0
- package/dist/components/ai-context.mjs +1 -0
- package/dist/components/ai-diff.css +6 -6
- package/dist/components/ai-diff.d.mts +2 -0
- package/dist/components/ai-feedback.d.mts +2 -0
- package/dist/components/ai-files.d.mts +2 -0
- package/dist/components/ai-message.d.mts +2 -0
- package/dist/components/ai-panel.css +12 -10
- package/dist/components/ai-reasoning.d.mts +2 -0
- package/dist/components/ai-subagent.d.mts +2 -0
- package/dist/components/ai-subagent.mjs +1 -0
- package/dist/components/ai-threads.d.mts +2 -0
- package/dist/components/ai-todo.d.mts +2 -0
- package/dist/components/ai-todo.mjs +1 -0
- package/dist/components/ai-tool-call.d.mts +2 -0
- package/dist/components/ai-tool-call.mjs +1 -0
- package/dist/components/ai-tool.css +16 -16
- package/dist/components/ai-usage.d.mts +2 -0
- package/dist/components/alert.d.mts +2 -0
- package/dist/components/autocomplete.css +12 -12
- package/dist/components/autocomplete.d.mts +2 -0
- package/dist/components/avatar.d.mts +2 -0
- package/dist/components/back-top.css +47 -16
- package/dist/components/back-top.d.mts +2 -0
- package/dist/components/badge.css +1 -1
- package/dist/components/badge.d.mts +2 -0
- package/dist/components/breadcrumb.css +26 -8
- package/dist/components/breadcrumb.d.mts +2 -0
- package/dist/components/btn.css +67 -62
- package/dist/components/btn.d.mts +2 -0
- package/dist/components/card.css +22 -22
- package/dist/components/card.d.mts +2 -0
- package/dist/components/carousel.d.mts +2 -0
- package/dist/components/cascader.css +56 -35
- package/dist/components/cascader.d.mts +2 -0
- package/dist/components/chart-area.css +46 -0
- package/dist/components/chart-area.d.mts +2 -0
- package/dist/components/chart-bar.css +66 -19
- package/dist/components/chart-bar.d.mts +2 -0
- package/dist/components/chart-calendar.d.mts +2 -0
- package/dist/components/chart-gauge.css +23 -3
- package/dist/components/chart-gauge.d.mts +2 -0
- package/dist/components/chart-graph.css +15 -3
- package/dist/components/chart-graph.d.mts +2 -0
- package/dist/components/chart-heatmap.css +117 -95
- package/dist/components/chart-heatmap.d.mts +2 -0
- package/dist/components/chart-line.css +54 -0
- package/dist/components/chart-line.d.mts +2 -0
- package/dist/components/chart-map.d.mts +2 -0
- package/dist/components/chart-pie.css +29 -0
- package/dist/components/chart-pie.d.mts +2 -0
- package/dist/components/chart-radar.css +50 -1
- package/dist/components/chart-radar.d.mts +2 -0
- package/dist/components/chart-scatter.css +39 -2
- package/dist/components/chart-scatter.d.mts +2 -0
- package/dist/components/chart-sparkline.css +20 -0
- package/dist/components/chart-sparkline.d.mts +2 -0
- package/dist/components/chart-stack.css +16 -5
- package/dist/components/chart-stack.d.mts +2 -0
- package/dist/components/charts.css +109 -35
- package/dist/components/charts.d.mts +2 -0
- package/dist/components/code-block.css +10 -8
- package/dist/components/code-block.d.mts +2 -0
- package/dist/components/command-palette.css +45 -38
- package/dist/components/command-palette.d.mts +2 -0
- package/dist/components/content.css +62 -57
- package/dist/components/context-menu.d.mts +2 -0
- package/dist/components/copy.css +10 -10
- package/dist/components/copy.d.mts +2 -0
- package/dist/components/datatable.css +72 -59
- package/dist/components/datatable.d.mts +2 -0
- package/dist/components/date-picker.css +26 -26
- package/dist/components/date-picker.d.mts +2 -0
- package/dist/components/desc.d.mts +2 -0
- package/dist/components/dropdown.d.mts +2 -0
- package/dist/components/empty.css +16 -16
- package/dist/components/empty.d.mts +2 -0
- package/dist/components/feedback.css +31 -29
- package/dist/components/form-validate.css +20 -6
- package/dist/components/form-validate.d.mts +2 -0
- package/dist/components/form.css +17 -14
- package/dist/components/form.d.mts +2 -0
- package/dist/components/icon.css +21 -10
- package/dist/components/icon.d.mts +2 -0
- package/dist/components/input.css +22 -22
- package/dist/components/input.d.mts +2 -0
- package/dist/components/kb-access.d.mts +2 -0
- package/dist/components/kb-acl.d.mts +2 -0
- package/dist/components/kb-agent.css +134 -55
- package/dist/components/kb-answer.d.mts +2 -0
- package/dist/components/kb-audit.d.mts +2 -0
- package/dist/components/kb-canvas.d.mts +2 -0
- package/dist/components/kb-chain.d.mts +2 -0
- package/dist/components/kb-checkpoint.d.mts +2 -0
- package/dist/components/kb-chunks.d.mts +2 -0
- package/dist/components/kb-citation.d.mts +2 -0
- package/dist/components/kb-clarify.d.mts +2 -0
- package/dist/components/kb-conflict.d.mts +2 -0
- package/dist/components/kb-connector.d.mts +2 -0
- package/dist/components/kb-data.css +192 -80
- package/dist/components/kb-eval.d.mts +2 -0
- package/dist/components/kb-explain.d.mts +2 -0
- package/dist/components/kb-filter.d.mts +2 -0
- package/dist/components/kb-gap.d.mts +2 -0
- package/dist/components/kb-ground.css +165 -73
- package/dist/components/kb-hittest.d.mts +2 -0
- package/dist/components/kb-ingest.css +308 -113
- package/dist/components/kb-metadata.d.mts +2 -0
- package/dist/components/kb-ops.css +189 -75
- package/dist/components/kb-passage.d.mts +2 -0
- package/dist/components/kb-perm.css +158 -87
- package/dist/components/kb-pipeline.d.mts +2 -0
- package/dist/components/kb-qa.d.mts +2 -0
- package/dist/components/kb-rerank.d.mts +2 -0
- package/dist/components/kb-retrieval.d.mts +2 -0
- package/dist/components/kb-review.d.mts +2 -0
- package/dist/components/kb-sandbox.d.mts +2 -0
- package/dist/components/kb-search.css +353 -116
- package/dist/components/kb-segment.d.mts +2 -0
- package/dist/components/kb-sources.d.mts +2 -0
- package/dist/components/kb-sql.d.mts +2 -0
- package/dist/components/kb-trace.d.mts +2 -0
- package/dist/components/kb-visibility.d.mts +2 -0
- package/dist/components/kb-who-can.d.mts +2 -0
- package/dist/components/kb.css +60 -36
- package/dist/components/kb.d.mts +2 -0
- package/dist/components/kbd.d.mts +2 -0
- package/dist/components/layout.css +10 -10
- package/dist/components/layout.d.mts +2 -0
- package/dist/components/list.d.mts +2 -0
- package/dist/components/material-chinese.css +10 -0
- package/dist/components/material-soviet.css +10 -0
- package/dist/components/material.css +61 -18
- package/dist/components/media-card.d.mts +2 -0
- package/dist/components/media.css +22 -27
- package/dist/components/mentions.css +18 -17
- package/dist/components/mentions.d.mts +2 -0
- package/dist/components/menu.css +32 -28
- package/dist/components/modal.css +30 -27
- package/dist/components/modal.d.mts +2 -0
- package/dist/components/nav.css +48 -44
- package/dist/components/nav.d.mts +2 -0
- package/dist/components/notification.css +31 -25
- package/dist/components/notification.d.mts +2 -0
- package/dist/components/pagination.css +15 -13
- package/dist/components/pagination.d.mts +2 -0
- package/dist/components/panel.css +19 -20
- package/dist/components/panel.d.mts +2 -0
- package/dist/components/pill.css +8 -7
- package/dist/components/pill.d.mts +2 -0
- package/dist/components/popover.css +32 -23
- package/dist/components/popover.d.mts +2 -0
- package/dist/components/progress.d.mts +2 -0
- package/dist/components/rating.d.mts +2 -0
- package/dist/components/result.d.mts +2 -0
- package/dist/components/scroll-area.css +84 -112
- package/dist/components/scroll-area.d.mts +2 -0
- package/dist/components/scroll-area.mjs +1 -0
- package/dist/components/segmented.css +47 -29
- package/dist/components/segmented.d.mts +2 -0
- package/dist/components/select.css +80 -41
- package/dist/components/select.d.mts +2 -0
- package/dist/components/sidebar.css +42 -38
- package/dist/components/sidebar.d.mts +2 -0
- package/dist/components/skeleton.d.mts +2 -0
- package/dist/components/slider.css +26 -22
- package/dist/components/slider.d.mts +2 -0
- package/dist/components/spinner.d.mts +2 -0
- package/dist/components/split-pane.css +16 -7
- package/dist/components/split-pane.d.mts +2 -0
- package/dist/components/stat.css +17 -16
- package/dist/components/stat.d.mts +2 -0
- package/dist/components/steps.css +26 -26
- package/dist/components/steps.d.mts +2 -0
- package/dist/components/switch.css +78 -39
- package/dist/components/switch.d.mts +2 -0
- package/dist/components/table.css +54 -32
- package/dist/components/table.d.mts +2 -0
- package/dist/components/tabs.css +53 -32
- package/dist/components/tabs.d.mts +2 -0
- package/dist/components/tag-input.css +12 -8
- package/dist/components/tag-input.d.mts +2 -0
- package/dist/components/tag.css +20 -12
- package/dist/components/tag.d.mts +2 -0
- package/dist/components/theme-switcher.css +7 -7
- package/dist/components/theme-switcher.d.mts +2 -0
- package/dist/components/timeline.d.mts +2 -0
- package/dist/components/toast.css +34 -30
- package/dist/components/toast.d.mts +2 -0
- package/dist/components/toolbar.css +19 -18
- package/dist/components/toolbar.d.mts +2 -0
- package/dist/components/tooltip.css +9 -8
- package/dist/components/tooltip.d.mts +2 -0
- package/dist/components/tree-select.css +63 -40
- package/dist/components/tree-select.d.mts +2 -0
- package/dist/components/tree.d.mts +2 -0
- package/dist/components/upload.css +9 -11
- package/dist/components/upload.d.mts +2 -0
- package/dist/index.d.mts +85 -84
- package/dist/index.mjs +14644 -13935
- package/dist/react.d.mts +18 -18
- package/dist/react.mjs +377 -36
- package/dist/registry.json +797 -64
- package/dist/tokens/colors.css +6 -0
- package/dist/tokens/design.css +137 -31
- package/dist/tokens/dtcg.json +9592 -2008
- package/dist/tokens/figma.variables.json +7451 -2141
- package/dist/tokens/motion.css +51 -0
- package/dist/tokens/pixel.css +10 -0
- package/dist/tokens/scrollbars.css +40 -0
- package/dist/tokens/tokens-studio.json +8390 -1142
- package/dist/tokens/typography.css +33 -0
- package/dist/tokens.css +402 -82
- package/dist/ui.css +4196 -2163
- package/dist/vue.d.mts +17 -17
- package/dist/vue.mjs +371 -34
- package/docs/spec/charts-scale-a11y.md +115 -0
- package/docs/spec/design-philosophy.md +5 -5
- package/docs/spec/design-primitives.md +10 -10
- package/docs/spec/motion-typography.md +105 -0
- package/docs/spec/scrollbars.md +46 -0
- package/package.json +17 -3
package/README.md
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
# @icen.ai/ui
|
|
2
2
|
|
|
3
|
-
icen.ai
|
|
3
|
+
[](https://www.npmjs.com/package/@icen.ai/ui)
|
|
4
|
+
[](https://github.com/icen-ai/ui/actions/workflows/ci.yml)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
7
|
+
icen.ai 的设计系统:<!-- meta:components -->122<!-- /meta:components --> 个组件入口、<!-- meta:charts.total -->14<!-- /meta:charts.total --> 种图表与 AI 原生组件族共用一套设计令牌——换肤不改代码,UI 可直接交给模型调用。无框架、零运行时依赖(`dependencies` 为空,可验证)、SSR 安全,MIT。
|
|
4
8
|
|
|
5
9
|
- 文档与实时演示:<https://ui.icen.ai>
|
|
6
|
-
- 安装:`bun add @icen.ai/ui`
|
|
10
|
+
- 安装:`bun add @icen.ai/ui`(或 `npm i`)
|
|
7
11
|
|
|
8
12
|
## 快速开始
|
|
9
13
|
|
|
10
14
|
```css
|
|
11
|
-
|
|
12
|
-
@import '@icen.ai/ui/ui.css';
|
|
15
|
+
/* 全量一行(ui.css 已含 tokens + base + 全部组件) */
|
|
16
|
+
@import '@icen.ai/ui/ui.css';
|
|
17
|
+
|
|
18
|
+
/* 或按需:tokens 底座 + 单引组件 CSS
|
|
19
|
+
@import '@icen.ai/ui/tokens.css';
|
|
20
|
+
@import '@icen.ai/ui/components/btn.css'; */
|
|
13
21
|
```
|
|
14
22
|
|
|
15
23
|
```html
|
|
16
24
|
<html data-icen-preset="piano" data-icen-theme="dark">
|
|
25
|
+
<!-- 明暗是二态契约:暗色 = 写 data-icen-theme="dark";亮色 = 移除该属性(没有 "light" 值) -->
|
|
17
26
|
```
|
|
18
27
|
|
|
19
28
|
```ts
|
|
@@ -25,75 +34,55 @@ toast.success('已保存');
|
|
|
25
34
|
renderLine(el, { labels, series }); // 函数式组件:无 init,直接渲染
|
|
26
35
|
```
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
纯静态页面走 `<link rel="stylesheet" href="./node_modules/@icen.ai/ui/ui.css">`;ES module 直引需经静态服务器(`file://` 直开会被浏览器 CORS 拦截)。更多入坑答案见 [docs/faq.md](docs/faq.md)。
|
|
29
38
|
|
|
30
39
|
## 里面有什么
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
**主题。** 六套预设,明暗双模,现代、中式、苏式与像素四种原语,全部挂在 `<html>` 的 data 属性上。配色与原语独立:中式推荐「东韵」,苏式推荐「巨构」,也可自由组合其它主题。自建品牌只需在自家 CSS 写一块 token 面,组件代码一行不动。
|
|
41
|
-
|
|
42
|
-
**React / Vue。** 官方薄封装 `@icen.ai/ui/react`、`@icen.ai/ui/vue`:只做生命周期接线和事件桥,底下是同一个命令式内核。详见 <https://ui.icen.ai/frameworks/>
|
|
43
|
-
|
|
44
|
-
还内建:RTL 全面支持(逻辑属性 + 方向图标自动镜像,`<html dir="rtl">` 即生效)、i18n 文案注册表(683 个 key,中英双语,`setIcenLocale` 一句切换)、统一事件体系(`onIcen` 一个入口:类型化、选择器委托、策略层)、表单校验(原生约束即 schema,零新词汇)。
|
|
41
|
+
- **组件**:无框架组件库(表单 / 浮层 / 导航 / 数据展示 / 反馈),纯 CSS 契约 + 按需行为 JS。类名 `icen-*`、钩子 `data-icen-*`、变量 `--icen-*`,产物全部进 cascade layers,与任何第三方 CSS 共存零冲突。
|
|
42
|
+
- **图表**:一份 JSON <!-- meta:charts.total -->14<!-- /meta:charts.total --> 种图,悬停/点击/图例开关内建,不依赖图表库。
|
|
43
|
+
- **AI 界面**:对话 / 推理 / 工具调用 / 审批 / 输入台 / 用量审计;兼容 OpenAI / Anthropic / MCP / AI SDK 协议;UI 能力可注册成模型直接调用的工具。
|
|
44
|
+
- **知识库**:覆盖 RAG 全链路的组件族(摄取 / 检索 / 问数 / 治理 / 权限),权限域 fail-closed。
|
|
45
|
+
- **主题**:色彩预设 × 设计原语 × 明暗,全部挂在 `<html>` data 属性上;自建品牌只需在自家 CSS 写一块 token 面。
|
|
46
|
+
- **React / Vue 官方薄封装**:`@icen.ai/ui/react`、`/vue`,只做生命周期接线与事件桥;兼容契约见 [docs/frameworks.md](docs/frameworks.md)。
|
|
47
|
+
- **RTL 全面支持**(逻辑属性 + 方向图标自动镜像)、**i18n 文案注册表**(中英双语,`setIcenLocale` 切换)、统一事件体系(`onIcen` 一个入口)。
|
|
45
48
|
|
|
46
49
|
## 工程信任
|
|
47
50
|
|
|
48
51
|
生产环境依赖一个 0.x 库之前该问的问题,直接给证据:
|
|
49
52
|
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
53
|
+
- **全部测试与纪律守卫挂在每个 PR 的 CI 上**,本地 `bun run verify` 同口径;覆盖率地板与体积预算以棘轮文件锁在 [docs/contracts/](docs/contracts/)。
|
|
54
|
+
- **稳定性分层**:Core 走 1.0 冻结轨道,图表 / AI / KB 标 Beta 随 minor 演进;事实源 `docs/contracts/tiers.json`,组件页徽标同源。
|
|
55
|
+
- **公共契约机器快照**:`docs/contracts/snapshot.json` 六个面(类名 / 属性 / 变量 / 事件 / 导出 / 类型签名),`bun run contracts:diff` 做 tag 间对比;破坏性变更只进 minor 且 CHANGELOG 逐条公告(CI 强制联动)。
|
|
56
|
+
- [SECURITY.md](SECURITY.md):支持矩阵与漏洞响应承诺;textContent-only 渲染、权限域 fail-closed、URL 协议守卫,逐条有测试或守卫背书。
|
|
57
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md):加组件的仪式与 PR 规范。
|
|
58
|
+
- [docs/versioning.md](docs/versioning.md):版本与稳定性政策(分层 / 0.x 纪律 / 弃用 / 主题预算 / 1.0 门槛 / 支持周期)。
|
|
59
|
+
- [docs/roadmap.md](docs/roadmap.md):0.11 → 0.13 → 1.0 收敛线与外部验收门槛;[docs/acceptance.md](docs/acceptance.md):AI/KB 验收矩阵;[docs/dogfood.md](docs/dogfood.md):陌生人验收记录。
|
|
60
|
+
- **发布链**:`git tag v<x.y.z>` → CI 全门 → OIDC trusted publishing 发 npm(provenance,无长期 token)→ 自动建 GitHub Release。
|
|
55
61
|
|
|
56
|
-
##
|
|
62
|
+
## 消费方接入
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
- **包引入(推荐)**:`bun add @icen.ai/ui`。CSS 全量一行 `./ui.css`,或按需 `./tokens.css` + `./components/<name>.css`;行为走 `./kit/<slug>` 一行入口。
|
|
65
|
+
- **vendor 复制**:锁定版本或离线构建场景,把 `dist/` 复制进自家仓库。
|
|
66
|
+
- **给 AI 代理**:包内 `AGENTS.md` + `dist/**/*.d.mts`;站点 <https://ui.icen.ai/llms.txt>(索引)与 `/llms-full.txt`(全文 + 类型面)。
|
|
59
67
|
|
|
60
|
-
##
|
|
68
|
+
## 构建与验证
|
|
61
69
|
|
|
62
70
|
```bash
|
|
63
71
|
bun install
|
|
64
|
-
bun run
|
|
65
|
-
bun run verify # tsc + 守卫 + 800+ 测试 + 构建,四道门
|
|
72
|
+
bun run verify # tsc + 守卫 + 测试 + 构建 + 体积预算(CI 另跑覆盖率、包面检查与 e2e)
|
|
66
73
|
```
|
|
67
74
|
|
|
68
|
-
产物:`dist
|
|
69
|
-
|
|
70
|
-
## 设计 token 导出
|
|
71
|
-
|
|
72
|
-
构建链末尾自动导出设计工具格式(`scripts/dtcg.mjs` + `scripts/design-tokens.mjs`);设计侧改 token 可走插件回灌,闭环到 `src/tokens/`:
|
|
73
|
-
|
|
74
|
-
- `dist/tokens/dtcg.json`:W3C DTCG 经典版,每个预设的明暗两态各一份有效级联结果;`var()` 引用转为规范别名,文件内自洽、无环。
|
|
75
|
-
- `dist/tokens/tokens-studio.json`:Tokens Studio 的 `$themes` 覆盖全部预设的明暗矩阵,导入后按 theme 切换即对齐 CSS。
|
|
76
|
-
- `dist/tokens/figma.variables.json`:Figma Variables,每预设一个 collection,light / dark 双 mode;rem 按 ×16 记 px。
|
|
77
|
-
- **诚实口径**:color-mix、box-shadow 等规范无法表示的值一律跳过并打印清单,不猜、不静默。
|
|
75
|
+
产物:`dist/`(ui.css / tokens.css / base.css / components/ / behaviors/ + .d.mts / registry.json / CLI)。
|
|
76
|
+
设计 token 另导出 DTCG / Tokens Studio / Figma Variables 三格式(`dist/tokens/`);规范无法表示的值明示跳过,不猜不静默。
|
|
78
77
|
|
|
79
78
|
## 目录
|
|
80
79
|
|
|
81
80
|
```
|
|
82
|
-
src/tokens/ #
|
|
83
|
-
src/base.css # 元素基线 +
|
|
84
|
-
src/components/ #
|
|
85
|
-
src/behaviors/ #
|
|
86
|
-
docs/
|
|
81
|
+
src/tokens/ # 设计令牌(单一事实源 themes.json,改动后 bun run colors)
|
|
82
|
+
src/base.css # 元素基线 + 交互基元 + a11y 工具类
|
|
83
|
+
src/components/ # 组件 CSS(每文件头注释 = DOM 契约)
|
|
84
|
+
src/behaviors/ # 行为模块(SSR 守卫 + 幂等 init + 返回销毁函数)
|
|
85
|
+
docs/ # 治理文档(versioning / contracts / testing / roadmap…)与 spec
|
|
87
86
|
site/ # 文档站(独立 Astro 应用)
|
|
88
|
-
scripts/ #
|
|
87
|
+
scripts/ # 构建、守卫与契约工具
|
|
89
88
|
```
|
|
90
|
-
|
|
91
|
-
## 消费方接入
|
|
92
|
-
|
|
93
|
-
- **包引入(推荐)**:`bun add @icen.ai/ui`,按 `./tokens.css` / `./ui.css` / `./kit/<slug>` 引入。
|
|
94
|
-
- **vendor 复制**:锁定版本或离线构建场景,把 `dist/` 复制进自家仓库(accounts 走这条路,`bun run sync:ui`)。
|
|
95
|
-
- **给 AI 代理**:包内 `AGENTS.md` + `dist/**/*.d.mts`;站点 <https://ui.icen.ai/llms.txt>(索引)与 `/llms-full.txt`(全文 + 类型面);场景配方 skill `icen-ui`(skill.icen.ai)。
|
|
96
|
-
|
|
97
|
-
## 现代原语
|
|
98
|
-
|
|
99
|
-
默认原语「现代」:瓷白内容面、轻盈磨砂操作层、系统排版、统一的几何节奏。想调成自己的品牌,改 `--icen-design-*` 种子即可,`createDesign` 会编译出带明暗与对比度验证的完整配方——本地文档站 `/modern/` 有实时工作台。规范与接入见 [设计原语](docs/spec/design-primitives.md)。
|
package/dist/base.css
CHANGED
|
@@ -173,30 +173,60 @@ p { margin: 0 0 1em; }
|
|
|
173
173
|
background: color-mix(in srgb, var(--icen-token-accent) 35%, transparent);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
/*
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
176
|
+
/* Native scrollbar paint; policy slots live in tokens/scrollbars.css. */
|
|
177
|
+
* {
|
|
178
|
+
scrollbar-width: var(--icen-scrollbar-width, thin);
|
|
179
|
+
scrollbar-color: color-mix(in srgb, var(--icen-token-scrollbar-thumb) var(--icen-scrollbar-idle, 100%), transparent) var(--icen-token-scrollbar-track);
|
|
180
|
+
scrollbar-gutter: var(--icen-token-scrollbar-gutter);
|
|
181
|
+
}
|
|
182
|
+
:where(*:not(html):hover) {
|
|
183
|
+
scrollbar-color: color-mix(in srgb, var(--icen-token-scrollbar-thumb-hover) var(--icen-scrollbar-hover, 100%), transparent) var(--icen-token-scrollbar-track);
|
|
184
|
+
}
|
|
185
|
+
:where(*:focus-within, [data-icen-scroll-active]) {
|
|
186
|
+
scrollbar-color: color-mix(in srgb, var(--icen-token-scrollbar-thumb) var(--icen-scrollbar-active, 100%), transparent) var(--icen-token-scrollbar-track);
|
|
187
|
+
}
|
|
188
|
+
/* Keep native arrows disabled when standard properties would override the skin. */
|
|
189
|
+
@supports selector(::-webkit-scrollbar) {
|
|
190
|
+
*, :where(*:hover, *:focus-within, [data-icen-scroll-active]) {
|
|
191
|
+
scrollbar-width: var(--icen-scrollbar-webkit-width, auto);
|
|
192
|
+
scrollbar-color: auto;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
::-webkit-scrollbar {
|
|
196
|
+
width: var(--icen-token-scrollbar-size);
|
|
197
|
+
height: var(--icen-token-scrollbar-size);
|
|
198
|
+
display: var(--icen-scrollbar-display, block);
|
|
199
|
+
}
|
|
183
200
|
::-webkit-scrollbar-track { background: var(--icen-token-scrollbar-track); }
|
|
184
201
|
::-webkit-scrollbar-thumb {
|
|
185
|
-
background: var(--icen-token-scrollbar-thumb);
|
|
202
|
+
background-color: color-mix(in srgb, var(--icen-token-scrollbar-thumb) var(--icen-scrollbar-idle, 100%), transparent);
|
|
186
203
|
border-radius: var(--icen-radius-full);
|
|
187
|
-
border:
|
|
204
|
+
border: var(--icen-token-scrollbar-inset) solid transparent;
|
|
188
205
|
background-clip: padding-box;
|
|
206
|
+
transition: background-color var(--icen-token-scrollbar-fade-duration) var(--icen-ease-out);
|
|
189
207
|
}
|
|
190
|
-
::-webkit-scrollbar-thumb
|
|
191
|
-
background: var(--icen-token-scrollbar-thumb-hover);
|
|
192
|
-
|
|
208
|
+
:where(*:not(html):hover)::-webkit-scrollbar-thumb {
|
|
209
|
+
background-color: color-mix(in srgb, var(--icen-token-scrollbar-thumb-hover) var(--icen-scrollbar-hover, 100%), transparent);
|
|
210
|
+
}
|
|
211
|
+
:is(*:focus-within, [data-icen-scroll-active])::-webkit-scrollbar-thumb {
|
|
212
|
+
background-color: color-mix(in srgb, var(--icen-token-scrollbar-thumb) var(--icen-scrollbar-active, 100%), transparent);
|
|
213
|
+
}
|
|
214
|
+
::-webkit-scrollbar-thumb:active {
|
|
215
|
+
background-color: color-mix(in srgb, var(--icen-token-scrollbar-thumb) var(--icen-scrollbar-active, 100%), transparent);
|
|
193
216
|
}
|
|
194
217
|
::-webkit-scrollbar-button,
|
|
195
218
|
::-webkit-scrollbar-button:single-button { display: none; width: 0; height: 0; }
|
|
196
219
|
::-webkit-scrollbar-corner { background: transparent; }
|
|
197
|
-
|
|
198
|
-
scrollbar-
|
|
199
|
-
|
|
220
|
+
@media (prefers-reduced-motion: reduce) {
|
|
221
|
+
::-webkit-scrollbar-thumb { transition: none; }
|
|
222
|
+
}
|
|
223
|
+
@media (forced-colors: active) {
|
|
224
|
+
*, :where(*:hover, *:focus-within, [data-icen-scroll-active]) {
|
|
225
|
+
scrollbar-width: auto;
|
|
226
|
+
scrollbar-color: auto;
|
|
227
|
+
}
|
|
228
|
+
::-webkit-scrollbar { display: block; }
|
|
229
|
+
::-webkit-scrollbar-thumb { background-color: CanvasText; }
|
|
200
230
|
}
|
|
201
231
|
|
|
202
232
|
/* ══════════════════════════════════════════════════════════════
|
|
@@ -266,7 +296,7 @@ html {
|
|
|
266
296
|
--icen-z-raised: 10;
|
|
267
297
|
--icen-z-sticky: 50;
|
|
268
298
|
--icen-z-chrome: 200;
|
|
269
|
-
--icen-z-toast:
|
|
299
|
+
--icen-z-toast: 560; /* 反馈层必须压过 dialog(500):模态里触发的 toast 不能被遮罩压住;仍低于 popover/tooltip(瞬态浮层) */
|
|
270
300
|
--icen-z-banner: 450;
|
|
271
301
|
--icen-z-dialog: 500;
|
|
272
302
|
--icen-z-popover: 620;
|
|
@@ -289,6 +319,10 @@ html {
|
|
|
289
319
|
@keyframes icen-spin {
|
|
290
320
|
to { transform: rotate(360deg); }
|
|
291
321
|
}
|
|
322
|
+
@keyframes icen-blink {
|
|
323
|
+
0%, 100% { opacity: 1; }
|
|
324
|
+
50% { opacity: 0; }
|
|
325
|
+
}
|
|
292
326
|
@keyframes icen-skeleton-pulse {
|
|
293
327
|
0%, 100% { opacity: 1; }
|
|
294
328
|
50% { opacity: 0.5; }
|
|
@@ -459,15 +493,20 @@ html {
|
|
|
459
493
|
动效与无障碍兜底
|
|
460
494
|
══════════════════════════════════════════════════════════════ */
|
|
461
495
|
|
|
462
|
-
/* reduced-motion
|
|
496
|
+
/* reduced-motion 与手动覆盖的行为压制(变量开关在 design.css 自持):
|
|
497
|
+
循环/环境动画单次瞬时 + 平滑滚动关闭;过渡时长经 --icen-design-motion 归零瞬时完成(状态反馈保留)。 */
|
|
463
498
|
@media (prefers-reduced-motion: reduce) {
|
|
464
|
-
*, *::before, *::after {
|
|
465
|
-
animation-duration: 0.01ms !important; /* guard:allow-important
|
|
466
|
-
animation-iteration-count: 1 !important; /* guard:allow-important
|
|
467
|
-
|
|
468
|
-
scroll-behavior: auto !important; /* guard:allow-important 动效守卫:降级媒体查询需压制组件动画 */
|
|
499
|
+
:root:not([data-icen-motion="full"]) *, :root:not([data-icen-motion="full"]) *::before, :root:not([data-icen-motion="full"]) *::after {
|
|
500
|
+
animation-duration: 0.01ms !important; /* guard:allow-important 动效守卫:降级需压制组件循环动画 */
|
|
501
|
+
animation-iteration-count: 1 !important; /* guard:allow-important 动效守卫:降级需压制组件循环动画 */
|
|
502
|
+
scroll-behavior: auto !important; /* guard:allow-important 动效守卫:降级需关闭平滑滚动 */
|
|
469
503
|
}
|
|
470
504
|
}
|
|
505
|
+
:root[data-icen-motion="reduce"] *, :root[data-icen-motion="reduce"] *::before, :root[data-icen-motion="reduce"] *::after {
|
|
506
|
+
animation-duration: 0.01ms !important; /* guard:allow-important 手动降级:与系统降级同口径 */
|
|
507
|
+
animation-iteration-count: 1 !important; /* guard:allow-important 手动降级:与系统降级同口径 */
|
|
508
|
+
scroll-behavior: auto !important; /* guard:allow-important 手动降级:与系统降级同口径 */
|
|
509
|
+
}
|
|
471
510
|
|
|
472
511
|
/* 触屏设备(无精细指针):压低 pressable 按下形变(hover 抬升已由 hover 守卫关闭) */
|
|
473
512
|
@media (pointer: coarse) {
|
|
@@ -484,6 +523,13 @@ html {
|
|
|
484
523
|
outline: 2px solid Highlight;
|
|
485
524
|
outline-offset: 2px;
|
|
486
525
|
}
|
|
526
|
+
/* 强制颜色会丢弃 box-shadow——本库多数焦点环正是 box-shadow(var(--icen-ring))。
|
|
527
|
+
组件普遍以 outline:none 关闭了默认焦点,所以这里必须提权兜底:
|
|
528
|
+
所有原生可聚焦元素在强制颜色下都拿到系统焦点框。 */
|
|
529
|
+
:where(a[href], button, input, select, textarea, summary, [tabindex]:not([tabindex='-1'])):focus-visible {
|
|
530
|
+
outline: 2px solid Highlight !important; /* guard:allow-important 高对比模式焦点兜底:组件 outline:none 会吞掉默认焦点 */
|
|
531
|
+
outline-offset: 2px !important; /* guard:allow-important 高对比模式焦点兜底:组件 outline:none 会吞掉默认焦点 */
|
|
532
|
+
}
|
|
487
533
|
}
|
|
488
534
|
|
|
489
535
|
/* 打印:去掉阴影/背景/动效,节约墨水并避免打印截断 */
|
|
@@ -21,7 +21,7 @@ function setup(container) {
|
|
|
21
21
|
if (item.classList.contains("is-disabled")) return;
|
|
22
22
|
const open = item.classList.toggle("is-open");
|
|
23
23
|
trigger.setAttribute("aria-expanded", String(open));
|
|
24
|
-
const index = Array.from(container.querySelectorAll(".icen-accordion-item")).indexOf(item);
|
|
24
|
+
const index = Array.from(container.querySelectorAll(":scope > .icen-accordion-item")).indexOf(item);
|
|
25
25
|
emitIcen(container, "icen:accordion-toggle", { item, index, open });
|
|
26
26
|
if (single && open) {
|
|
27
27
|
container.querySelectorAll(".icen-accordion-item.is-open").forEach((other) => {
|
|
@@ -122,6 +122,7 @@ var ICEN_LOCALE_ZH_CN = {
|
|
|
122
122
|
"aiChat.roleTool": "\u5DE5",
|
|
123
123
|
"aiChat.roleUser": "\u6211",
|
|
124
124
|
"aiChat.thinking": "\u601D\u8003 {p0}",
|
|
125
|
+
"aiChat.thinkingNow": "\u6B63\u5728\u601D\u8003\u2026",
|
|
125
126
|
/* ── aiComposer ── */
|
|
126
127
|
"aiComposer.attachAgent": "\u4EE3\u7406",
|
|
127
128
|
"aiComposer.library": "\u6DFB\u52A0\u4E0A\u4E0B\u6587\u4E0E\u547D\u4EE4",
|
|
@@ -304,6 +305,19 @@ var ICEN_LOCALE_ZH_CN = {
|
|
|
304
305
|
"charts.noData": "\u6682\u65E0\u6570\u636E",
|
|
305
306
|
"charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
|
|
306
307
|
"charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
|
|
308
|
+
"charts.a11y.keyboardHint": "\u65B9\u5411\u952E\u9010\u70B9\u6D4F\u89C8\uFF0CHome/End \u8DF3\u9996\u5C3E\uFF0CEsc \u91CD\u7F6E\u7F29\u653E",
|
|
309
|
+
"charts.a11y.max": "\u6700\u5927 {p0}\uFF08{p1}\uFF09",
|
|
310
|
+
"charts.a11y.min": "\u6700\u5C0F {p0}\uFF08{p1}\uFF09",
|
|
311
|
+
"charts.a11y.point": "{p0}\uFF1A{p1}\uFF0C\u7B2C {p2}/{p3} \u70B9",
|
|
312
|
+
"charts.a11y.pointSeries": "{p0} \xB7 {p1}\uFF1A{p2}\uFF0C\u7B2C {p3}/{p4} \u70B9",
|
|
313
|
+
"charts.a11y.range": "\u7C7B\u76EE {p0} \u81F3 {p1}",
|
|
314
|
+
"charts.a11y.scatterRange": "x \u8303\u56F4 {p0}\u2013{p1}\uFF0Cy \u8303\u56F4 {p2}\u2013{p3}",
|
|
315
|
+
"charts.a11y.seriesCount": "{p0} \u4E2A\u7CFB\u5217",
|
|
316
|
+
"charts.a11y.tableCaption": "{p0} \u6570\u636E\u8868",
|
|
317
|
+
"charts.a11y.trend": "\u9996\u7CFB\u5217\u8D8B\u52BF{p0}",
|
|
318
|
+
"charts.a11y.trendDown": "\u4E0B\u964D",
|
|
319
|
+
"charts.a11y.trendFlat": "\u6301\u5E73",
|
|
320
|
+
"charts.a11y.trendUp": "\u4E0A\u5347",
|
|
307
321
|
"charts.type.area": "\u9762\u79EF\u56FE",
|
|
308
322
|
"charts.type.donut": "\u73AF\u5F62\u56FE",
|
|
309
323
|
"charts.type.gauge": "\u4EEA\u8868\u76D8",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AiContent, AiContentPart } from './ai-core.
|
|
1
|
+
import { AiContent, AiContentPart } from './ai-core.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 初始化 root 下的全部 .icen-ai-chat(root 自身匹配时包含自身)。
|
|
@@ -46,7 +46,7 @@ interface AiMessageHandle {
|
|
|
46
46
|
/** 在正文末开一个流式文本节点,返回 createAiStream 句柄 */
|
|
47
47
|
stream(): AiStreamHandle;
|
|
48
48
|
}
|
|
49
|
-
/** 单个内容部件 → DOM 节点(文本一律 textContent;媒体 src 走 aiContentUrl
|
|
49
|
+
/** 单个内容部件 → DOM 节点(文本一律 textContent;媒体 src 走 aiContentUrl 归一 + safeUrl 守卫)。
|
|
50
50
|
* 导出供 ai-tool 的 IO 区复用(MCP 多模态回执;类名 .ai-msg-* 语义即「消息部件」,全局生效)。
|
|
51
51
|
* 前置条件:浏览器环境——本函数无 SSR 守卫,SSR 由调用方守卫覆盖(renderAiMessage /
|
|
52
52
|
* renderAiToolCall 头部守卫后才会触达此处)。 */
|
|
@@ -21,6 +21,7 @@ var ICEN_LOCALE_ZH_CN = {
|
|
|
21
21
|
"aiChat.roleTool": "\u5DE5",
|
|
22
22
|
"aiChat.roleUser": "\u6211",
|
|
23
23
|
"aiChat.thinking": "\u601D\u8003 {p0}",
|
|
24
|
+
"aiChat.thinkingNow": "\u6B63\u5728\u601D\u8003\u2026",
|
|
24
25
|
/* ── aiComposer ── */
|
|
25
26
|
"aiComposer.attachAgent": "\u4EE3\u7406",
|
|
26
27
|
"aiComposer.library": "\u6DFB\u52A0\u4E0A\u4E0B\u6587\u4E0E\u547D\u4EE4",
|
|
@@ -203,6 +204,19 @@ var ICEN_LOCALE_ZH_CN = {
|
|
|
203
204
|
"charts.noData": "\u6682\u65E0\u6570\u636E",
|
|
204
205
|
"charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
|
|
205
206
|
"charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
|
|
207
|
+
"charts.a11y.keyboardHint": "\u65B9\u5411\u952E\u9010\u70B9\u6D4F\u89C8\uFF0CHome/End \u8DF3\u9996\u5C3E\uFF0CEsc \u91CD\u7F6E\u7F29\u653E",
|
|
208
|
+
"charts.a11y.max": "\u6700\u5927 {p0}\uFF08{p1}\uFF09",
|
|
209
|
+
"charts.a11y.min": "\u6700\u5C0F {p0}\uFF08{p1}\uFF09",
|
|
210
|
+
"charts.a11y.point": "{p0}\uFF1A{p1}\uFF0C\u7B2C {p2}/{p3} \u70B9",
|
|
211
|
+
"charts.a11y.pointSeries": "{p0} \xB7 {p1}\uFF1A{p2}\uFF0C\u7B2C {p3}/{p4} \u70B9",
|
|
212
|
+
"charts.a11y.range": "\u7C7B\u76EE {p0} \u81F3 {p1}",
|
|
213
|
+
"charts.a11y.scatterRange": "x \u8303\u56F4 {p0}\u2013{p1}\uFF0Cy \u8303\u56F4 {p2}\u2013{p3}",
|
|
214
|
+
"charts.a11y.seriesCount": "{p0} \u4E2A\u7CFB\u5217",
|
|
215
|
+
"charts.a11y.tableCaption": "{p0} \u6570\u636E\u8868",
|
|
216
|
+
"charts.a11y.trend": "\u9996\u7CFB\u5217\u8D8B\u52BF{p0}",
|
|
217
|
+
"charts.a11y.trendDown": "\u4E0B\u964D",
|
|
218
|
+
"charts.a11y.trendFlat": "\u6301\u5E73",
|
|
219
|
+
"charts.a11y.trendUp": "\u4E0A\u5347",
|
|
206
220
|
"charts.type.area": "\u9762\u79EF\u56FE",
|
|
207
221
|
"charts.type.donut": "\u73AF\u5F62\u56FE",
|
|
208
222
|
"charts.type.gauge": "\u4EEA\u8868\u76D8",
|
|
@@ -1102,6 +1116,15 @@ function svgIcon(svg) {
|
|
|
1102
1116
|
return parseSvg(svg);
|
|
1103
1117
|
}
|
|
1104
1118
|
|
|
1119
|
+
// src/behaviors/url-safety.ts
|
|
1120
|
+
function safeUrl(uri) {
|
|
1121
|
+
const s = uri.replace(/[\t\n\r\0-\x1f\x7f]/g, "").trim().toLowerCase();
|
|
1122
|
+
if (!s) return void 0;
|
|
1123
|
+
if (s.startsWith("javascript:") || s.startsWith("vbscript:")) return void 0;
|
|
1124
|
+
if (/^data:\s*text\/html/.test(s)) return void 0;
|
|
1125
|
+
return uri;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1105
1128
|
// src/behaviors/ai-chat.ts
|
|
1106
1129
|
var ICON_COPY = iconHTML("copy");
|
|
1107
1130
|
var ICON_RETRY = iconHTML("refresh");
|
|
@@ -1140,6 +1163,10 @@ function setupChat(chat) {
|
|
|
1140
1163
|
if (el.__icenAiChatInit) return void 0;
|
|
1141
1164
|
el.__icenAiChatInit = true;
|
|
1142
1165
|
const scrollEl = chat.querySelector(".icen-ai-chat-scroll");
|
|
1166
|
+
if (scrollEl && !scrollEl.hasAttribute("aria-live")) {
|
|
1167
|
+
scrollEl.setAttribute("role", "log");
|
|
1168
|
+
scrollEl.setAttribute("aria-live", "polite");
|
|
1169
|
+
}
|
|
1143
1170
|
let jump = chat.querySelector(".icen-ai-chat-jump") ?? null;
|
|
1144
1171
|
let pinned = true;
|
|
1145
1172
|
let unread = 0;
|
|
@@ -1196,6 +1223,26 @@ function setupChat(chat) {
|
|
|
1196
1223
|
updateJump();
|
|
1197
1224
|
}
|
|
1198
1225
|
};
|
|
1226
|
+
const onUserLeaveIntent = () => {
|
|
1227
|
+
if (!pinned) return;
|
|
1228
|
+
pinned = false;
|
|
1229
|
+
updateJump();
|
|
1230
|
+
};
|
|
1231
|
+
const onWheel = (e) => {
|
|
1232
|
+
if (e.deltaY < 0) onUserLeaveIntent();
|
|
1233
|
+
};
|
|
1234
|
+
let lastTouchY = 0;
|
|
1235
|
+
const onTouchStart = (e) => {
|
|
1236
|
+
lastTouchY = e.touches[0]?.clientY ?? 0;
|
|
1237
|
+
};
|
|
1238
|
+
const onTouchMove = (e) => {
|
|
1239
|
+
const y = e.touches[0]?.clientY ?? 0;
|
|
1240
|
+
if (y > lastTouchY + 4) onUserLeaveIntent();
|
|
1241
|
+
lastTouchY = y;
|
|
1242
|
+
};
|
|
1243
|
+
const onScrollerKeydown = (e) => {
|
|
1244
|
+
if (e.key === "PageUp" || e.key === "Home" || e.key === "ArrowUp") onUserLeaveIntent();
|
|
1245
|
+
};
|
|
1199
1246
|
const onJumpClick = () => {
|
|
1200
1247
|
pinned = true;
|
|
1201
1248
|
unread = 0;
|
|
@@ -1251,10 +1298,18 @@ function setupChat(chat) {
|
|
|
1251
1298
|
}
|
|
1252
1299
|
lastMsgCount = scrollEl.querySelectorAll(".icen-ai-msg").length;
|
|
1253
1300
|
scrollEl.addEventListener("scroll", onScroll, { passive: true });
|
|
1301
|
+
scrollEl.addEventListener("wheel", onWheel, { passive: true });
|
|
1302
|
+
scrollEl.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
1303
|
+
scrollEl.addEventListener("touchmove", onTouchMove, { passive: true });
|
|
1304
|
+
scrollEl.addEventListener("keydown", onScrollerKeydown);
|
|
1254
1305
|
jump.addEventListener("click", onJumpClick);
|
|
1255
1306
|
const jumpBtn = jump;
|
|
1256
1307
|
disposers.push(() => {
|
|
1257
1308
|
scrollEl.removeEventListener("scroll", onScroll);
|
|
1309
|
+
scrollEl.removeEventListener("wheel", onWheel);
|
|
1310
|
+
scrollEl.removeEventListener("touchstart", onTouchStart);
|
|
1311
|
+
scrollEl.removeEventListener("touchmove", onTouchMove);
|
|
1312
|
+
scrollEl.removeEventListener("keydown", onScrollerKeydown);
|
|
1258
1313
|
jumpBtn.removeEventListener("click", onJumpClick);
|
|
1259
1314
|
});
|
|
1260
1315
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -1326,6 +1381,8 @@ function createAiStream(el) {
|
|
|
1326
1381
|
el.setAttribute("aria-busy", "true");
|
|
1327
1382
|
if (reasoning) {
|
|
1328
1383
|
reasoning.classList.add("is-streaming");
|
|
1384
|
+
const timeEl = reasoning.querySelector(".icen-ai-reasoning-time");
|
|
1385
|
+
if (timeEl) timeEl.textContent = t("aiChat.thinkingNow");
|
|
1329
1386
|
setReasoningOpen(reasoning, true);
|
|
1330
1387
|
const head = reasoning.querySelector(".icen-ai-reasoning-head");
|
|
1331
1388
|
if (head && head.dataset.icenReasoningStart == null) head.dataset.icenReasoningStart = String(startedAt);
|
|
@@ -1413,10 +1470,9 @@ var ROLE_AVATAR = {
|
|
|
1413
1470
|
system: t("aiChat.roleSystem"),
|
|
1414
1471
|
tool: t("aiChat.roleTool")
|
|
1415
1472
|
};
|
|
1416
|
-
function
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
return uri;
|
|
1473
|
+
function safeContentUrl(part) {
|
|
1474
|
+
const src = aiContentUrl(part);
|
|
1475
|
+
return src ? safeUrl(src) : void 0;
|
|
1420
1476
|
}
|
|
1421
1477
|
function renderAiContentPart(part) {
|
|
1422
1478
|
if (part.type === "text") {
|
|
@@ -1426,7 +1482,7 @@ function renderAiContentPart(part) {
|
|
|
1426
1482
|
return span;
|
|
1427
1483
|
}
|
|
1428
1484
|
if (part.type === "image") {
|
|
1429
|
-
const src =
|
|
1485
|
+
const src = safeContentUrl(part);
|
|
1430
1486
|
if (!src) return void 0;
|
|
1431
1487
|
const img = document.createElement("img");
|
|
1432
1488
|
img.className = "icen-ai-msg-media icen-ai-msg-media--image";
|
|
@@ -1436,7 +1492,7 @@ function renderAiContentPart(part) {
|
|
|
1436
1492
|
return img;
|
|
1437
1493
|
}
|
|
1438
1494
|
if (part.type === "audio") {
|
|
1439
|
-
const src =
|
|
1495
|
+
const src = safeContentUrl(part);
|
|
1440
1496
|
if (!src) return void 0;
|
|
1441
1497
|
const audio = document.createElement("audio");
|
|
1442
1498
|
audio.className = "icen-ai-msg-media icen-ai-msg-media--audio";
|
|
@@ -1445,7 +1501,7 @@ function renderAiContentPart(part) {
|
|
|
1445
1501
|
return audio;
|
|
1446
1502
|
}
|
|
1447
1503
|
if (part.type === "video") {
|
|
1448
|
-
const src =
|
|
1504
|
+
const src = safeContentUrl(part);
|
|
1449
1505
|
if (!src) return void 0;
|
|
1450
1506
|
const video = document.createElement("video");
|
|
1451
1507
|
video.className = "icen-ai-msg-media icen-ai-msg-media--video";
|
|
@@ -1457,7 +1513,7 @@ function renderAiContentPart(part) {
|
|
|
1457
1513
|
if (part.type === "file") {
|
|
1458
1514
|
const chip = h("span", "icen-ai-msg-file");
|
|
1459
1515
|
chip.appendChild(h("span", "ai-msg-file-name", part.filename ?? t("aiChat.file")));
|
|
1460
|
-
const href2 = part.url ?
|
|
1516
|
+
const href2 = part.url ? safeUrl(part.url) : void 0;
|
|
1461
1517
|
if (href2 && /^(https?:|data:)/i.test(href2)) {
|
|
1462
1518
|
const a2 = document.createElement("a");
|
|
1463
1519
|
a2.className = "icen-ai-msg-file-link";
|
|
@@ -1470,7 +1526,7 @@ function renderAiContentPart(part) {
|
|
|
1470
1526
|
}
|
|
1471
1527
|
const a = document.createElement("a");
|
|
1472
1528
|
a.className = "icen-ai-msg-link";
|
|
1473
|
-
const href =
|
|
1529
|
+
const href = safeUrl(part.uri);
|
|
1474
1530
|
if (href) a.href = href;
|
|
1475
1531
|
a.textContent = part.name ?? part.uri;
|
|
1476
1532
|
a.title = part.uri;
|
|
@@ -1532,7 +1588,7 @@ function renderAiMessage(scrollEl, model) {
|
|
|
1532
1588
|
errEl.textContent = message;
|
|
1533
1589
|
},
|
|
1534
1590
|
stream() {
|
|
1535
|
-
const span = h("span", "ai-msg-stream-text");
|
|
1591
|
+
const span = h("span", "icen-ai-msg-text ai-msg-stream-text");
|
|
1536
1592
|
body.appendChild(span);
|
|
1537
1593
|
return createAiStream(span);
|
|
1538
1594
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { IconName } from './icons.
|
|
2
|
-
import { AiContent, AiUsage } from './ai-core.
|
|
3
|
-
import { PanelSizing } from './popover.
|
|
4
|
-
import { AiTodoItem, AiUsageRingOpts } from './ai-panel.
|
|
5
|
-
import { AiClient, AiAuditor } from './ai-provider.
|
|
1
|
+
import { IconName } from './icons.mjs';
|
|
2
|
+
import { AiContent, AiUsage } from './ai-core.mjs';
|
|
3
|
+
import { PanelSizing } from './popover.mjs';
|
|
4
|
+
import { AiTodoItem, AiUsageRingOpts } from './ai-panel.mjs';
|
|
5
|
+
import { AiClient, AiAuditor } from './ai-provider.mjs';
|
|
6
6
|
|
|
7
7
|
interface AiProviderModelOption {
|
|
8
8
|
id: string;
|
|
@@ -122,6 +122,7 @@ var ICEN_LOCALE_ZH_CN = {
|
|
|
122
122
|
"aiChat.roleTool": "\u5DE5",
|
|
123
123
|
"aiChat.roleUser": "\u6211",
|
|
124
124
|
"aiChat.thinking": "\u601D\u8003 {p0}",
|
|
125
|
+
"aiChat.thinkingNow": "\u6B63\u5728\u601D\u8003\u2026",
|
|
125
126
|
/* ── aiComposer ── */
|
|
126
127
|
"aiComposer.attachAgent": "\u4EE3\u7406",
|
|
127
128
|
"aiComposer.library": "\u6DFB\u52A0\u4E0A\u4E0B\u6587\u4E0E\u547D\u4EE4",
|
|
@@ -304,6 +305,19 @@ var ICEN_LOCALE_ZH_CN = {
|
|
|
304
305
|
"charts.noData": "\u6682\u65E0\u6570\u636E",
|
|
305
306
|
"charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
|
|
306
307
|
"charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
|
|
308
|
+
"charts.a11y.keyboardHint": "\u65B9\u5411\u952E\u9010\u70B9\u6D4F\u89C8\uFF0CHome/End \u8DF3\u9996\u5C3E\uFF0CEsc \u91CD\u7F6E\u7F29\u653E",
|
|
309
|
+
"charts.a11y.max": "\u6700\u5927 {p0}\uFF08{p1}\uFF09",
|
|
310
|
+
"charts.a11y.min": "\u6700\u5C0F {p0}\uFF08{p1}\uFF09",
|
|
311
|
+
"charts.a11y.point": "{p0}\uFF1A{p1}\uFF0C\u7B2C {p2}/{p3} \u70B9",
|
|
312
|
+
"charts.a11y.pointSeries": "{p0} \xB7 {p1}\uFF1A{p2}\uFF0C\u7B2C {p3}/{p4} \u70B9",
|
|
313
|
+
"charts.a11y.range": "\u7C7B\u76EE {p0} \u81F3 {p1}",
|
|
314
|
+
"charts.a11y.scatterRange": "x \u8303\u56F4 {p0}\u2013{p1}\uFF0Cy \u8303\u56F4 {p2}\u2013{p3}",
|
|
315
|
+
"charts.a11y.seriesCount": "{p0} \u4E2A\u7CFB\u5217",
|
|
316
|
+
"charts.a11y.tableCaption": "{p0} \u6570\u636E\u8868",
|
|
317
|
+
"charts.a11y.trend": "\u9996\u7CFB\u5217\u8D8B\u52BF{p0}",
|
|
318
|
+
"charts.a11y.trendDown": "\u4E0B\u964D",
|
|
319
|
+
"charts.a11y.trendFlat": "\u6301\u5E73",
|
|
320
|
+
"charts.a11y.trendUp": "\u4E0A\u5347",
|
|
307
321
|
"charts.type.area": "\u9762\u79EF\u56FE",
|
|
308
322
|
"charts.type.donut": "\u73AF\u5F62\u56FE",
|
|
309
323
|
"charts.type.gauge": "\u4EEA\u8868\u76D8",
|
|
@@ -1687,6 +1701,15 @@ var ICON_SPARKLES = svgWrap(
|
|
|
1687
1701
|
'<path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z"/>'
|
|
1688
1702
|
);
|
|
1689
1703
|
|
|
1704
|
+
// src/behaviors/url-safety.ts
|
|
1705
|
+
function safeUrl(uri) {
|
|
1706
|
+
const s = uri.replace(/[\t\n\r\0-\x1f\x7f]/g, "").trim().toLowerCase();
|
|
1707
|
+
if (!s) return void 0;
|
|
1708
|
+
if (s.startsWith("javascript:") || s.startsWith("vbscript:")) return void 0;
|
|
1709
|
+
if (/^data:\s*text\/html/.test(s)) return void 0;
|
|
1710
|
+
return uri;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1690
1713
|
// src/behaviors/ai-chat.ts
|
|
1691
1714
|
var ICON_COPY = iconHTML("copy");
|
|
1692
1715
|
var ICON_RETRY = iconHTML("refresh");
|
|
@@ -1714,6 +1737,8 @@ function createAiStream(el) {
|
|
|
1714
1737
|
el.setAttribute("aria-busy", "true");
|
|
1715
1738
|
if (reasoning) {
|
|
1716
1739
|
reasoning.classList.add("is-streaming");
|
|
1740
|
+
const timeEl = reasoning.querySelector(".icen-ai-reasoning-time");
|
|
1741
|
+
if (timeEl) timeEl.textContent = t("aiChat.thinkingNow");
|
|
1717
1742
|
setReasoningOpen(reasoning, true);
|
|
1718
1743
|
const head = reasoning.querySelector(".icen-ai-reasoning-head");
|
|
1719
1744
|
if (head && head.dataset.icenReasoningStart == null) head.dataset.icenReasoningStart = String(startedAt);
|
|
@@ -1801,10 +1826,9 @@ var ROLE_AVATAR = {
|
|
|
1801
1826
|
system: t("aiChat.roleSystem"),
|
|
1802
1827
|
tool: t("aiChat.roleTool")
|
|
1803
1828
|
};
|
|
1804
|
-
function
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
return uri;
|
|
1829
|
+
function safeContentUrl(part) {
|
|
1830
|
+
const src = aiContentUrl(part);
|
|
1831
|
+
return src ? safeUrl(src) : void 0;
|
|
1808
1832
|
}
|
|
1809
1833
|
function renderAiContentPart(part) {
|
|
1810
1834
|
if (part.type === "text") {
|
|
@@ -1814,7 +1838,7 @@ function renderAiContentPart(part) {
|
|
|
1814
1838
|
return span;
|
|
1815
1839
|
}
|
|
1816
1840
|
if (part.type === "image") {
|
|
1817
|
-
const src =
|
|
1841
|
+
const src = safeContentUrl(part);
|
|
1818
1842
|
if (!src) return void 0;
|
|
1819
1843
|
const img = document.createElement("img");
|
|
1820
1844
|
img.className = "icen-ai-msg-media icen-ai-msg-media--image";
|
|
@@ -1824,7 +1848,7 @@ function renderAiContentPart(part) {
|
|
|
1824
1848
|
return img;
|
|
1825
1849
|
}
|
|
1826
1850
|
if (part.type === "audio") {
|
|
1827
|
-
const src =
|
|
1851
|
+
const src = safeContentUrl(part);
|
|
1828
1852
|
if (!src) return void 0;
|
|
1829
1853
|
const audio = document.createElement("audio");
|
|
1830
1854
|
audio.className = "icen-ai-msg-media icen-ai-msg-media--audio";
|
|
@@ -1833,7 +1857,7 @@ function renderAiContentPart(part) {
|
|
|
1833
1857
|
return audio;
|
|
1834
1858
|
}
|
|
1835
1859
|
if (part.type === "video") {
|
|
1836
|
-
const src =
|
|
1860
|
+
const src = safeContentUrl(part);
|
|
1837
1861
|
if (!src) return void 0;
|
|
1838
1862
|
const video = document.createElement("video");
|
|
1839
1863
|
video.className = "icen-ai-msg-media icen-ai-msg-media--video";
|
|
@@ -1845,7 +1869,7 @@ function renderAiContentPart(part) {
|
|
|
1845
1869
|
if (part.type === "file") {
|
|
1846
1870
|
const chip = h("span", "icen-ai-msg-file");
|
|
1847
1871
|
chip.appendChild(h("span", "ai-msg-file-name", part.filename ?? t("aiChat.file")));
|
|
1848
|
-
const href2 = part.url ?
|
|
1872
|
+
const href2 = part.url ? safeUrl(part.url) : void 0;
|
|
1849
1873
|
if (href2 && /^(https?:|data:)/i.test(href2)) {
|
|
1850
1874
|
const a2 = document.createElement("a");
|
|
1851
1875
|
a2.className = "icen-ai-msg-file-link";
|
|
@@ -1858,7 +1882,7 @@ function renderAiContentPart(part) {
|
|
|
1858
1882
|
}
|
|
1859
1883
|
const a = document.createElement("a");
|
|
1860
1884
|
a.className = "icen-ai-msg-link";
|
|
1861
|
-
const href =
|
|
1885
|
+
const href = safeUrl(part.uri);
|
|
1862
1886
|
if (href) a.href = href;
|
|
1863
1887
|
a.textContent = part.name ?? part.uri;
|
|
1864
1888
|
a.title = part.uri;
|
|
@@ -1920,7 +1944,7 @@ function renderAiMessage(scrollEl, model) {
|
|
|
1920
1944
|
errEl.textContent = message;
|
|
1921
1945
|
},
|
|
1922
1946
|
stream() {
|
|
1923
|
-
const span = h("span", "ai-msg-stream-text");
|
|
1947
|
+
const span = h("span", "icen-ai-msg-text ai-msg-stream-text");
|
|
1924
1948
|
body.appendChild(span);
|
|
1925
1949
|
return createAiStream(span);
|
|
1926
1950
|
}
|