@icen.ai/ui 0.9.0 → 0.10.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 (281) hide show
  1. package/AGENTS.md +138 -80
  2. package/README.md +66 -91
  3. package/dist/base.css +208 -179
  4. package/dist/behaviors/accordion.mjs +8 -8
  5. package/dist/behaviors/ai-branch.d.mts +1 -1
  6. package/dist/behaviors/ai-branch.mjs +1010 -94
  7. package/dist/behaviors/ai-chat.d.mts +7 -7
  8. package/dist/behaviors/ai-chat.mjs +1071 -161
  9. package/dist/behaviors/ai-composer.d.mts +44 -12
  10. package/dist/behaviors/ai-composer.mjs +1997 -524
  11. package/dist/behaviors/ai-core.d.mts +1 -1
  12. package/dist/behaviors/ai-core.mjs +1016 -107
  13. package/dist/behaviors/ai-diff.mjs +1019 -127
  14. package/dist/behaviors/ai-feedback.d.mts +1 -1
  15. package/dist/behaviors/ai-feedback.mjs +1035 -126
  16. package/dist/behaviors/ai-panel.d.mts +8 -8
  17. package/dist/behaviors/ai-panel.mjs +1284 -266
  18. package/dist/behaviors/ai-provider.mjs +1003 -101
  19. package/dist/behaviors/ai-threads.d.mts +1 -1
  20. package/dist/behaviors/ai-threads.mjs +1068 -154
  21. package/dist/behaviors/ai-tool.d.mts +2 -2
  22. package/dist/behaviors/ai-tool.mjs +2037 -396
  23. package/dist/behaviors/ai-tools.d.mts +89 -16
  24. package/dist/behaviors/ai-tools.mjs +4448 -372
  25. package/dist/behaviors/autocomplete.d.mts +53 -0
  26. package/dist/behaviors/autocomplete.mjs +1600 -0
  27. package/dist/behaviors/back-top.d.mts +1 -1
  28. package/dist/behaviors/back-top.mjs +951 -14
  29. package/dist/behaviors/carousel.d.mts +1 -1
  30. package/dist/behaviors/carousel.mjs +906 -11
  31. package/dist/behaviors/cascader.d.mts +48 -0
  32. package/dist/behaviors/cascader.mjs +1853 -0
  33. package/dist/behaviors/charts.d.mts +72 -8
  34. package/dist/behaviors/charts.mjs +1901 -256
  35. package/dist/behaviors/code-block.d.mts +36 -0
  36. package/dist/behaviors/code-block.mjs +1611 -0
  37. package/dist/behaviors/command-palette.d.mts +9 -6
  38. package/dist/behaviors/command-palette.mjs +161 -59
  39. package/dist/behaviors/context-menu.d.mts +9 -8
  40. package/dist/behaviors/context-menu.mjs +1112 -44
  41. package/dist/behaviors/controls.d.mts +13 -13
  42. package/dist/behaviors/controls.mjs +989 -130
  43. package/dist/behaviors/copy.d.mts +21 -2
  44. package/dist/behaviors/copy.mjs +1206 -42
  45. package/dist/behaviors/datatable.d.mts +53 -3
  46. package/dist/behaviors/datatable.mjs +1963 -207
  47. package/dist/behaviors/date-picker.d.mts +0 -1
  48. package/dist/behaviors/date-picker.mjs +1340 -293
  49. package/dist/behaviors/design.d.mts +54 -0
  50. package/dist/behaviors/design.mjs +109 -0
  51. package/dist/behaviors/dropdown.d.mts +5 -6
  52. package/dist/behaviors/dropdown.mjs +262 -110
  53. package/dist/behaviors/events.d.mts +120 -3
  54. package/dist/behaviors/events.mjs +47 -2
  55. package/dist/behaviors/feedback.d.mts +30 -0
  56. package/dist/behaviors/feedback.mjs +296 -0
  57. package/dist/behaviors/focus.d.mts +8 -0
  58. package/dist/behaviors/focus.mjs +24 -0
  59. package/dist/behaviors/form-validate.d.mts +102 -0
  60. package/dist/behaviors/form-validate.mjs +6091 -0
  61. package/dist/behaviors/i18n.d.mts +789 -0
  62. package/dist/behaviors/i18n.mjs +1711 -0
  63. package/dist/behaviors/icons.d.mts +93 -0
  64. package/dist/behaviors/icons.mjs +113 -0
  65. package/dist/behaviors/input.d.mts +9 -1
  66. package/dist/behaviors/input.mjs +1056 -60
  67. package/dist/behaviors/kb-access.d.mts +1 -1
  68. package/dist/behaviors/kb-access.mjs +2129 -239
  69. package/dist/behaviors/kb-acl.d.mts +6 -6
  70. package/dist/behaviors/kb-acl.mjs +1119 -179
  71. package/dist/behaviors/kb-answer.mjs +2070 -418
  72. package/dist/behaviors/kb-audit.d.mts +1 -1
  73. package/dist/behaviors/kb-audit.mjs +1076 -154
  74. package/dist/behaviors/kb-canvas.mjs +1100 -177
  75. package/dist/behaviors/kb-chain.mjs +1022 -100
  76. package/dist/behaviors/kb-checkpoint.d.mts +1 -1
  77. package/dist/behaviors/kb-checkpoint.mjs +1319 -169
  78. package/dist/behaviors/kb-chunks.mjs +1152 -213
  79. package/dist/behaviors/kb-citation.d.mts +1 -1
  80. package/dist/behaviors/kb-citation.mjs +1201 -179
  81. package/dist/behaviors/kb-clarify.mjs +1058 -130
  82. package/dist/behaviors/kb-conflict.mjs +1041 -119
  83. package/dist/behaviors/kb-connector.mjs +1104 -176
  84. package/dist/behaviors/kb-core.d.mts +6 -1
  85. package/dist/behaviors/kb-core.mjs +1077 -162
  86. package/dist/behaviors/kb-eval.mjs +1971 -144
  87. package/dist/behaviors/kb-explain.d.mts +1 -1
  88. package/dist/behaviors/kb-explain.mjs +1024 -102
  89. package/dist/behaviors/kb-filter.mjs +2008 -178
  90. package/dist/behaviors/kb-gap.d.mts +1 -1
  91. package/dist/behaviors/kb-gap.mjs +1043 -121
  92. package/dist/behaviors/kb-hittest.d.mts +1 -1
  93. package/dist/behaviors/kb-hittest.mjs +1093 -150
  94. package/dist/behaviors/kb-metadata.mjs +1052 -130
  95. package/dist/behaviors/kb-passage.d.mts +2 -2
  96. package/dist/behaviors/kb-passage.mjs +1101 -161
  97. package/dist/behaviors/kb-pipeline.d.mts +2 -2
  98. package/dist/behaviors/kb-pipeline.mjs +1082 -162
  99. package/dist/behaviors/kb-qa.mjs +1088 -160
  100. package/dist/behaviors/kb-rerank.mjs +1047 -114
  101. package/dist/behaviors/kb-retrieval.mjs +2063 -224
  102. package/dist/behaviors/kb-review.mjs +1990 -159
  103. package/dist/behaviors/kb-sandbox.d.mts +1 -1
  104. package/dist/behaviors/kb-sandbox.mjs +1026 -104
  105. package/dist/behaviors/kb-segment.mjs +1111 -179
  106. package/dist/behaviors/kb-select.d.mts +24 -0
  107. package/dist/behaviors/kb-select.mjs +1989 -0
  108. package/dist/behaviors/kb-sources.d.mts +1 -1
  109. package/dist/behaviors/kb-sources.mjs +1084 -170
  110. package/dist/behaviors/kb-sql.d.mts +1 -1
  111. package/dist/behaviors/kb-sql.mjs +1143 -226
  112. package/dist/behaviors/kb-trace.d.mts +1 -1
  113. package/dist/behaviors/kb-trace.mjs +1042 -128
  114. package/dist/behaviors/kb-visibility.mjs +1086 -158
  115. package/dist/behaviors/kb-who-can.mjs +1974 -137
  116. package/dist/behaviors/mentions.d.mts +93 -0
  117. package/dist/behaviors/mentions.mjs +2152 -0
  118. package/dist/behaviors/modal.d.mts +4 -3
  119. package/dist/behaviors/modal.mjs +1055 -33
  120. package/dist/behaviors/nav.d.mts +1 -1
  121. package/dist/behaviors/nav.mjs +6 -6
  122. package/dist/behaviors/notification.d.mts +16 -8
  123. package/dist/behaviors/notification.mjs +1615 -265
  124. package/dist/behaviors/panel.d.mts +4 -0
  125. package/dist/behaviors/panel.mjs +994 -0
  126. package/dist/behaviors/popover.d.mts +28 -5
  127. package/dist/behaviors/popover.mjs +225 -24
  128. package/dist/behaviors/select.d.mts +5 -2
  129. package/dist/behaviors/select.mjs +1410 -117
  130. package/dist/behaviors/sidebar.d.mts +1 -1
  131. package/dist/behaviors/sidebar.mjs +13 -13
  132. package/dist/behaviors/slider.d.mts +1 -1
  133. package/dist/behaviors/slider.mjs +9 -9
  134. package/dist/behaviors/split-pane.d.mts +1 -1
  135. package/dist/behaviors/split-pane.mjs +7 -7
  136. package/dist/behaviors/suggestions.d.mts +27 -0
  137. package/dist/behaviors/suggestions.mjs +1089 -0
  138. package/dist/behaviors/svg-parse.d.mts +14 -0
  139. package/dist/behaviors/svg-parse.mjs +37 -0
  140. package/dist/behaviors/tabs.d.mts +2 -2
  141. package/dist/behaviors/tabs.mjs +25 -25
  142. package/dist/behaviors/tag-input.d.mts +23 -5
  143. package/dist/behaviors/tag-input.mjs +1359 -104
  144. package/dist/behaviors/theme-switcher.d.mts +24 -0
  145. package/dist/behaviors/theme-switcher.mjs +1981 -0
  146. package/dist/behaviors/theme.d.mts +15 -8
  147. package/dist/behaviors/theme.mjs +390 -28
  148. package/dist/behaviors/toast.d.mts +24 -25
  149. package/dist/behaviors/toast.mjs +1266 -88
  150. package/dist/behaviors/tooltip.d.mts +4 -0
  151. package/dist/behaviors/tooltip.mjs +378 -0
  152. package/dist/behaviors/tree-select.d.mts +69 -0
  153. package/dist/behaviors/tree-select.mjs +1907 -0
  154. package/dist/behaviors/tree.d.mts +1 -1
  155. package/dist/behaviors/tree.mjs +15 -15
  156. package/dist/behaviors/upload.d.mts +3 -2
  157. package/dist/behaviors/upload.mjs +1076 -66
  158. package/dist/cli.mjs +5 -6
  159. package/dist/components/ai-chat.css +666 -573
  160. package/dist/components/ai-diff.css +153 -132
  161. package/dist/components/ai-panel.css +351 -343
  162. package/dist/components/ai-tool.css +393 -238
  163. package/dist/components/autocomplete.css +61 -0
  164. package/dist/components/autocomplete.mjs +3 -0
  165. package/dist/components/back-top.css +34 -31
  166. package/dist/components/badge.css +44 -41
  167. package/dist/components/breadcrumb.css +67 -46
  168. package/dist/components/btn.css +80 -74
  169. package/dist/components/card.css +117 -106
  170. package/dist/components/cascader.css +311 -0
  171. package/dist/components/cascader.mjs +3 -0
  172. package/dist/components/chart-area.css +8 -2
  173. package/dist/components/chart-bar.css +95 -33
  174. package/dist/components/chart-gauge.css +14 -11
  175. package/dist/components/chart-graph.css +20 -17
  176. package/dist/components/chart-heatmap.css +96 -90
  177. package/dist/components/chart-line.css +8 -3
  178. package/dist/components/chart-pie.css +9 -6
  179. package/dist/components/chart-radar.css +8 -5
  180. package/dist/components/chart-scatter.css +9 -6
  181. package/dist/components/chart-sparkline.css +5 -2
  182. package/dist/components/chart-stack.css +14 -11
  183. package/dist/components/charts.css +164 -73
  184. package/dist/components/code-block.css +418 -0
  185. package/dist/components/code-block.mjs +4 -0
  186. package/dist/components/command-palette.css +138 -117
  187. package/dist/components/content.css +257 -251
  188. package/dist/components/copy.css +72 -44
  189. package/dist/components/datatable.css +423 -181
  190. package/dist/components/date-picker.css +135 -104
  191. package/dist/components/empty.css +68 -65
  192. package/dist/components/feedback.css +201 -183
  193. package/dist/components/form-validate.css +120 -0
  194. package/dist/components/form-validate.mjs +3 -0
  195. package/dist/components/form.css +150 -127
  196. package/dist/components/icon.css +102 -0
  197. package/dist/components/icon.mjs +3 -0
  198. package/dist/components/input.css +159 -104
  199. package/dist/components/kb-agent.css +188 -173
  200. package/dist/components/kb-data.css +257 -253
  201. package/dist/components/kb-ground.css +197 -185
  202. package/dist/components/kb-ingest.css +334 -350
  203. package/dist/components/kb-ops.css +265 -259
  204. package/dist/components/kb-perm.css +260 -271
  205. package/dist/components/kb-search.css +317 -360
  206. package/dist/components/kb.css +120 -109
  207. package/dist/components/layout.css +85 -82
  208. package/dist/components/material-chinese.css +73 -0
  209. package/dist/components/material-soviet.css +76 -0
  210. package/dist/components/material.css +275 -0
  211. package/dist/components/media.css +220 -166
  212. package/dist/components/mentions.css +189 -0
  213. package/dist/components/mentions.mjs +3 -0
  214. package/dist/components/menu.css +113 -105
  215. package/dist/components/modal.css +107 -112
  216. package/dist/components/nav.css +118 -115
  217. package/dist/components/notification.css +238 -174
  218. package/dist/components/pagination.css +56 -53
  219. package/dist/components/panel.css +66 -63
  220. package/dist/components/panel.mjs +1 -0
  221. package/dist/components/pill.css +81 -71
  222. package/dist/components/popover.css +67 -53
  223. package/dist/components/scroll-area.css +113 -110
  224. package/dist/components/segmented.css +146 -143
  225. package/dist/components/select.css +206 -186
  226. package/dist/components/sidebar.css +137 -127
  227. package/dist/components/slider.css +117 -114
  228. package/dist/components/split-pane.css +112 -109
  229. package/dist/components/stat.css +112 -106
  230. package/dist/components/steps.css +109 -103
  231. package/dist/components/switch.css +138 -134
  232. package/dist/components/table.css +131 -107
  233. package/dist/components/tabs.css +114 -108
  234. package/dist/components/tag-input.css +48 -133
  235. package/dist/components/tag.css +81 -71
  236. package/dist/components/theme-switcher.css +105 -0
  237. package/dist/components/theme-switcher.mjs +3 -0
  238. package/dist/components/toast.css +100 -86
  239. package/dist/components/toolbar.css +45 -42
  240. package/dist/components/tooltip.css +102 -43
  241. package/dist/components/tooltip.mjs +1 -0
  242. package/dist/components/tree-select.css +416 -0
  243. package/dist/components/tree-select.mjs +3 -0
  244. package/dist/components/upload.css +135 -92
  245. package/dist/fonts/LICENSES/ark-pixel/OFL.txt +96 -0
  246. package/dist/fonts/LICENSES/cubic-11/OFL.txt +108 -0
  247. package/dist/fonts/LICENSES/galmuri/LICENSE.txt +93 -0
  248. package/dist/fonts/OFL.txt +96 -0
  249. package/dist/fonts/README.md +14 -0
  250. package/dist/fonts/fusion-pixel-12px-zh-hans.woff2 +0 -0
  251. package/dist/index.d.mts +28 -14
  252. package/dist/index.mjs +20705 -9601
  253. package/dist/react.d.mts +91 -0
  254. package/dist/react.mjs +8208 -0
  255. package/dist/registry.json +130 -13
  256. package/dist/tokens/chinese.css +44 -0
  257. package/dist/tokens/colors.css +365 -1222
  258. package/dist/tokens/design.css +109 -0
  259. package/dist/tokens/dtcg.json +11198 -0
  260. package/dist/tokens/figma.variables.json +7017 -0
  261. package/dist/tokens/icon-masks.css +78 -0
  262. package/dist/tokens/pixel.css +50 -0
  263. package/dist/tokens/soviet.css +44 -0
  264. package/dist/tokens/style-profiles.css +36 -547
  265. package/dist/tokens/themes.json +308 -0
  266. package/dist/tokens/tokens-studio.json +6728 -0
  267. package/dist/tokens/typography.css +21 -18
  268. package/dist/tokens.css +1555 -1801
  269. package/dist/ui.css +11975 -9831
  270. package/dist/vue.d.mts +344 -0
  271. package/dist/vue.mjs +7781 -0
  272. package/docs/spec/ai-input-experience.md +75 -0
  273. package/docs/spec/ai-native.md +238 -147
  274. package/docs/spec/design-philosophy.md +189 -0
  275. package/docs/spec/design-primitives.md +196 -0
  276. package/docs/spec/kb-family.md +147 -115
  277. package/package.json +43 -8
  278. package/dist/retro-effects.css +0 -255
  279. package/dist/tokens/retro-effects.css +0 -255
  280. package/docs/research/2026-10-03-ai-kb-components.md +0 -392
  281. package/docs/research/2026-10-03-kb-permissions.md +0 -992
@@ -0,0 +1,4 @@
1
+ /** 事件委托接入一个作用域(缺省 document,动态插入同样生效)。 */
2
+ declare function initTooltip(root?: ParentNode): () => void;
3
+
4
+ export { initTooltip };
@@ -0,0 +1,378 @@
1
+ // src/behaviors/popover.ts
2
+ var DEFAULT_MIN_WIDTH = 220;
3
+ var DEFAULT_MAX_WIDTH = 420;
4
+ var DEFAULT_MIN_HEIGHT = 120;
5
+ var DEFAULT_MAX_HEIGHT = 420;
6
+ function clamp(value, min, max) {
7
+ if (max < min) return min;
8
+ return Math.min(max, Math.max(min, value));
9
+ }
10
+ function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
11
+ const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
12
+ const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
13
+ if (preferredSpace >= desiredHeight) return preferredSide;
14
+ if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
15
+ if (preferredSpace >= oppositeSpace) return preferredSide;
16
+ return preferredSide === "top" ? "bottom" : "top";
17
+ }
18
+ function horizontalAnchor(anchor, width, align) {
19
+ if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
20
+ if (align === "end") return anchor.right - width;
21
+ return anchor.left;
22
+ }
23
+ function computePopoverLayout(anchor, viewport, opts = {}) {
24
+ const margin = opts.margin ?? 12;
25
+ const offset = opts.offset ?? 8;
26
+ const preferredSide = opts.side ?? "top";
27
+ const align = opts.align ?? "start";
28
+ if (preferredSide === "left" || preferredSide === "right") {
29
+ const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
30
+ const availableLeft = Math.max(0, anchor.left - margin - offset);
31
+ const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
32
+ const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= minWidth2 ? preferredSide : preferredSide === "left" ? "right" : "left";
33
+ const maxWidth2 = Math.min(
34
+ Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
35
+ side2 === "left" ? availableLeft : availableRight
36
+ );
37
+ const width2 = minWidth2;
38
+ const left2 = clamp(
39
+ side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
40
+ margin,
41
+ viewport.width - margin - width2
42
+ );
43
+ const availableHeight2 = Math.max(80, viewport.height - margin * 2);
44
+ const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
45
+ const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
46
+ const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
47
+ const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
48
+ return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
49
+ }
50
+ const availableWidth = Math.max(80, viewport.width - margin * 2);
51
+ const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
52
+ const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
53
+ const maxWidth = Math.min(requestedMaxWidth, availableWidth);
54
+ const minWidth = Math.min(requestedMinWidth, maxWidth);
55
+ const availableTop = Math.max(0, anchor.top - margin - offset);
56
+ const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
57
+ const availableHeight = Math.max(80, viewport.height - margin * 2);
58
+ const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
59
+ const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
60
+ const fallbackHeight = Math.min(260, requestedMaxHeight);
61
+ const contentHeight = opts.contentHeightHint != null && opts.contentHeightHint > 0 ? opts.contentHeightHint : fallbackHeight;
62
+ const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
63
+ const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
64
+ const sideSpace = side === "top" ? availableTop : availableBottom;
65
+ const maxHeight = Math.min(
66
+ requestedMaxHeight,
67
+ Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
68
+ availableHeight
69
+ );
70
+ const positionedHeight = clamp(desiredHeight, 40, maxHeight);
71
+ const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
72
+ const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
73
+ const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
74
+ const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
75
+ return { side, left, top, width, maxHeight };
76
+ }
77
+
78
+ // src/behaviors/tooltip.ts
79
+ var HOVER_DELAY_MS = 400;
80
+ var LEAVE_TIMEOUT_MS = 240;
81
+ var POINTER_LEAVE_DELAY_MS = 80;
82
+ var OFFSET_PX = 6;
83
+ var VIEWPORT_MARGIN_PX = 8;
84
+ var WATCHED_ATTRS = ["data-icen-tooltip", "data-icen-tooltip-side", "data-icen-tooltip-align", "data-icen-tooltip-multiline"];
85
+ var scopes = /* @__PURE__ */ new WeakMap();
86
+ var handled = /* @__PURE__ */ new WeakSet();
87
+ var bubble = null;
88
+ var current = null;
89
+ var dismissed = /* @__PURE__ */ new Set();
90
+ var triggerOwners = /* @__PURE__ */ new WeakMap();
91
+ var closeTimer = null;
92
+ var pending = null;
93
+ var leaving = null;
94
+ var observer = null;
95
+ var sequence = 0;
96
+ function textOf(trigger) {
97
+ return trigger.getAttribute("data-icen-tooltip")?.trim() ?? "";
98
+ }
99
+ function sideOf(trigger) {
100
+ const side = trigger.getAttribute("data-icen-tooltip-side");
101
+ return side === "bottom" || side === "left" || side === "right" ? side : "top";
102
+ }
103
+ function alignOf(trigger) {
104
+ const align = trigger.getAttribute("data-icen-tooltip-align");
105
+ return align === "start" || align === "end" ? align : "center";
106
+ }
107
+ function focusVisible(el) {
108
+ try {
109
+ return el.matches(":focus-visible");
110
+ } catch {
111
+ return true;
112
+ }
113
+ }
114
+ function triggerFrom(target) {
115
+ return target instanceof Element ? target.closest("[data-icen-tooltip]") : null;
116
+ }
117
+ function cancelPending() {
118
+ if (!pending) return;
119
+ window.clearTimeout(pending.timer);
120
+ pending.trigger.removeAttribute("data-icen-tooltip-live");
121
+ pending = null;
122
+ }
123
+ function cancelLeaving() {
124
+ if (!leaving) return;
125
+ window.clearTimeout(leaving.timer);
126
+ leaving.node.classList.add("is-open");
127
+ leaving = null;
128
+ }
129
+ function release(trigger) {
130
+ trigger.removeAttribute("data-icen-tooltip-live");
131
+ const tokens = (trigger.getAttribute("aria-describedby") ?? "").split(/\s+/).filter((id) => id && id !== bubble?.id);
132
+ if (tokens.length) trigger.setAttribute("aria-describedby", tokens.join(" "));
133
+ else trigger.removeAttribute("aria-describedby");
134
+ }
135
+ function cancelClose() {
136
+ if (closeTimer !== null) window.clearTimeout(closeTimer);
137
+ closeTimer = null;
138
+ }
139
+ function clearDismissed(trigger) {
140
+ if (!dismissed.delete(trigger)) return;
141
+ if (current !== trigger && pending?.trigger !== trigger) trigger.removeAttribute("data-icen-tooltip-live");
142
+ }
143
+ function scheduleClose() {
144
+ cancelClose();
145
+ closeTimer = window.setTimeout(() => {
146
+ closeTimer = null;
147
+ if (current && document.activeElement !== current) hide(current);
148
+ }, POINTER_LEAVE_DELAY_MS);
149
+ }
150
+ function unwatch() {
151
+ window.removeEventListener("scroll", onViewportScroll, true);
152
+ window.removeEventListener("resize", onViewportResize);
153
+ observer?.disconnect();
154
+ observer = null;
155
+ }
156
+ function removeBubble(node) {
157
+ node.remove();
158
+ if (bubble === node) bubble = null;
159
+ }
160
+ function hide(trigger, opts) {
161
+ cancelClose();
162
+ const waiting = pending?.trigger;
163
+ cancelPending();
164
+ if (waiting && opts?.suppressFallback) {
165
+ dismissed.add(waiting);
166
+ waiting.setAttribute("data-icen-tooltip-live", "");
167
+ }
168
+ const active = current;
169
+ if (!active || trigger && active !== trigger) return;
170
+ current = null;
171
+ release(active);
172
+ if (opts?.suppressFallback) {
173
+ dismissed.add(active);
174
+ active.setAttribute("data-icen-tooltip-live", "");
175
+ }
176
+ unwatch();
177
+ if (!bubble) return;
178
+ const node = bubble;
179
+ if (opts?.instant) {
180
+ cancelLeaving();
181
+ removeBubble(node);
182
+ return;
183
+ }
184
+ node.classList.remove("is-open");
185
+ leaving = {
186
+ node,
187
+ timer: window.setTimeout(() => {
188
+ if (leaving?.node === node) leaving = null;
189
+ removeBubble(node);
190
+ }, LEAVE_TIMEOUT_MS)
191
+ };
192
+ }
193
+ function layout() {
194
+ if (!current || !bubble) return;
195
+ const anchor = current.getBoundingClientRect();
196
+ const width = bubble.offsetWidth;
197
+ const height = bubble.offsetHeight;
198
+ const out = computePopoverLayout(anchor, { width: window.innerWidth, height: window.innerHeight }, {
199
+ side: sideOf(current),
200
+ align: alignOf(current),
201
+ offset: OFFSET_PX,
202
+ margin: VIEWPORT_MARGIN_PX,
203
+ minWidth: width,
204
+ maxWidth: width,
205
+ minHeight: height,
206
+ maxHeight: height,
207
+ contentHeightHint: height
208
+ });
209
+ bubble.style.left = `${out.left}px`;
210
+ bubble.style.top = `${out.top}px`;
211
+ bubble.dataset.icenSide = out.side;
212
+ bubble.style.visibility = "";
213
+ const node = bubble, trigger = current;
214
+ requestAnimationFrame(() => {
215
+ if (bubble === node && current === trigger) node.classList.add("is-open");
216
+ });
217
+ }
218
+ function watch() {
219
+ unwatch();
220
+ window.addEventListener("scroll", onViewportScroll, { capture: true, passive: true });
221
+ window.addEventListener("resize", onViewportResize, { passive: true });
222
+ observer = new MutationObserver(() => {
223
+ if (!current) return;
224
+ const text = textOf(current);
225
+ if (!current.isConnected || !text) {
226
+ hide(null, { instant: true });
227
+ return;
228
+ }
229
+ if (bubble) {
230
+ if (bubble.textContent !== text) bubble.textContent = text;
231
+ bubble.classList.toggle("is-multiline", current.getAttribute("data-icen-tooltip-multiline") === "true");
232
+ }
233
+ layout();
234
+ });
235
+ observer.observe(document.documentElement, {
236
+ subtree: true,
237
+ childList: true,
238
+ attributes: true,
239
+ attributeFilter: WATCHED_ATTRS
240
+ });
241
+ }
242
+ function onViewportScroll() {
243
+ hide(null, { instant: true, suppressFallback: true });
244
+ }
245
+ function onViewportResize() {
246
+ hide(null, { instant: true, suppressFallback: true });
247
+ }
248
+ function show(trigger) {
249
+ if (!trigger.isConnected) return;
250
+ const text = textOf(trigger);
251
+ if (!text) return;
252
+ cancelPending();
253
+ cancelClose();
254
+ cancelLeaving();
255
+ if (current && current !== trigger) release(current);
256
+ current = trigger;
257
+ trigger.setAttribute("data-icen-tooltip-live", "");
258
+ if (!bubble) {
259
+ bubble = document.createElement("div");
260
+ bubble.className = "icen-tooltip";
261
+ bubble.setAttribute("role", "tooltip");
262
+ bubble.id = `icen-tooltip-${++sequence}`;
263
+ bubble.addEventListener("pointerenter", cancelClose);
264
+ bubble.addEventListener("pointerleave", (event) => {
265
+ if (current && event.relatedTarget instanceof Node && current.contains(event.relatedTarget)) return;
266
+ scheduleClose();
267
+ });
268
+ }
269
+ bubble.textContent = text;
270
+ bubble.classList.toggle("is-multiline", trigger.getAttribute("data-icen-tooltip-multiline") === "true");
271
+ if (!bubble.isConnected) {
272
+ bubble.style.visibility = "hidden";
273
+ document.body.appendChild(bubble);
274
+ }
275
+ const tokens = new Set((trigger.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean));
276
+ tokens.add(bubble.id);
277
+ trigger.setAttribute("aria-describedby", [...tokens].join(" "));
278
+ layout();
279
+ watch();
280
+ }
281
+ function markPending(trigger) {
282
+ for (const stale of dismissed) if (!stale.isConnected) clearDismissed(stale);
283
+ cancelClose();
284
+ cancelPending();
285
+ if (current === trigger) return;
286
+ trigger.setAttribute("data-icen-tooltip-live", "");
287
+ pending = {
288
+ trigger,
289
+ timer: window.setTimeout(() => {
290
+ pending = null;
291
+ show(trigger);
292
+ }, HOVER_DELAY_MS)
293
+ };
294
+ }
295
+ function owns(trigger, scope) {
296
+ if (!trigger) return false;
297
+ return scope instanceof Node ? scope.contains(trigger) || scope === trigger : false;
298
+ }
299
+ function initTooltip(root) {
300
+ if (typeof document === "undefined") return () => {
301
+ };
302
+ const scope = root ?? document;
303
+ const existing = scopes.get(scope);
304
+ if (existing) return existing;
305
+ const onPointerOver = (event) => {
306
+ if (handled.has(event)) return;
307
+ handled.add(event);
308
+ const pointer = event;
309
+ if (pointer.pointerType === "touch") return;
310
+ const trigger = triggerFrom(pointer.target);
311
+ if (!trigger) return;
312
+ if (pointer.relatedTarget instanceof Node && trigger.contains(pointer.relatedTarget)) return;
313
+ if (dismissed.has(trigger)) return;
314
+ if (current === trigger) {
315
+ cancelClose();
316
+ return;
317
+ }
318
+ if (pending?.trigger === trigger) return;
319
+ triggerOwners.set(trigger, scope);
320
+ markPending(trigger);
321
+ };
322
+ const onPointerOut = (event) => {
323
+ const pointer = event;
324
+ const trigger = triggerFrom(pointer.target);
325
+ if (!trigger) return;
326
+ if (pointer.relatedTarget instanceof Node && trigger.contains(pointer.relatedTarget)) return;
327
+ if (document.activeElement !== trigger) clearDismissed(trigger);
328
+ if (pending?.trigger === trigger) cancelPending();
329
+ if (current === trigger && !(pointer.relatedTarget instanceof Node && bubble?.contains(pointer.relatedTarget))) scheduleClose();
330
+ };
331
+ const onFocusIn = (event) => {
332
+ if (handled.has(event)) return;
333
+ handled.add(event);
334
+ const trigger = triggerFrom(event.target);
335
+ if (!trigger || !focusVisible(trigger) || dismissed.has(trigger)) return;
336
+ triggerOwners.set(trigger, scope);
337
+ show(trigger);
338
+ };
339
+ const onFocusOut = (event) => {
340
+ const trigger = triggerFrom(event.target);
341
+ if (!trigger) return;
342
+ if (!trigger.matches(":hover")) clearDismissed(trigger);
343
+ if (pending?.trigger === trigger) cancelPending();
344
+ if (current === trigger && !trigger.matches(":hover")) hide(trigger);
345
+ };
346
+ const onPointerDown = () => {
347
+ hide(null, { suppressFallback: true });
348
+ };
349
+ const onKeyDown = (event) => {
350
+ if (event.key !== "Escape") return;
351
+ hide(null, { suppressFallback: true });
352
+ };
353
+ scope.addEventListener("pointerover", onPointerOver);
354
+ scope.addEventListener("pointerout", onPointerOut);
355
+ scope.addEventListener("focusin", onFocusIn);
356
+ scope.addEventListener("focusout", onFocusOut);
357
+ scope.addEventListener("pointerdown", onPointerDown, true);
358
+ scope.addEventListener("keydown", onKeyDown, true);
359
+ const dispose = () => {
360
+ scope.removeEventListener("pointerover", onPointerOver);
361
+ scope.removeEventListener("pointerout", onPointerOut);
362
+ scope.removeEventListener("focusin", onFocusIn);
363
+ scope.removeEventListener("focusout", onFocusOut);
364
+ scope.removeEventListener("pointerdown", onPointerDown, true);
365
+ scope.removeEventListener("keydown", onKeyDown, true);
366
+ if (owns(pending?.trigger ?? null, scope)) cancelPending();
367
+ if (owns(current, scope)) hide(null, { instant: true });
368
+ for (const trigger of dismissed) {
369
+ if (scope === document || triggerOwners.get(trigger) === scope || owns(trigger, scope)) clearDismissed(trigger);
370
+ }
371
+ scopes.delete(scope);
372
+ };
373
+ scopes.set(scope, dispose);
374
+ return dispose;
375
+ }
376
+ export {
377
+ initTooltip
378
+ };
@@ -0,0 +1,69 @@
1
+ interface TreeNode {
2
+ value: string;
3
+ label: string;
4
+ children?: TreeNode[];
5
+ disabled?: boolean;
6
+ }
7
+ interface TreeSelectOptions {
8
+ /** Accessible field name; defaults to placeholder. */
9
+ label?: string;
10
+ size?: 'sm' | 'md' | 'lg';
11
+ nodes: TreeNode[];
12
+ /** 初值(多选数组;单选取首个有效值) */
13
+ value?: string[];
14
+ multiple?: boolean;
15
+ /** 面板内搜索:过滤命中节点自动展开祖先,命中词 <mark> 高亮 */
16
+ searchable?: boolean;
17
+ /** 多选的三态勾选(父 = 子集并集,混合态);隐含多值语义 */
18
+ checkable?: boolean;
19
+ /** 单选时仅叶子可定值(默认任意级可选) */
20
+ leafOnly?: boolean;
21
+ /** 默认「请选择」 */
22
+ placeholder?: string;
23
+ clearable?: boolean;
24
+ disabled?: boolean;
25
+ /** 搜索无命中文案,默认「无匹配」 */
26
+ emptyLabel?: string;
27
+ /** 搜索框占位,默认「搜索」 */
28
+ searchPlaceholder?: string;
29
+ onChange?: (value: string[], labels: string[]) => void;
30
+ }
31
+ interface TreeSelectHandle {
32
+ getValue(): string[];
33
+ getLabels(): string[];
34
+ setValue(v: string[]): void;
35
+ clear(): void;
36
+ open(): void;
37
+ close(): void;
38
+ expandAll(): void;
39
+ collapseAll(): void;
40
+ destroy(): void;
41
+ }
42
+ interface FlatTreeEntry {
43
+ node: TreeNode;
44
+ depth: number;
45
+ parents: TreeNode[];
46
+ }
47
+ /** 深度优先拍平(文档序):节点 + 深度 + 父链(键盘导航 / chips 序 / 值序共用)。 */
48
+ declare function flattenTreeNodes(nodes: TreeNode[]): FlatTreeEntry[];
49
+ interface TreeFilterResult {
50
+ /** label 命中 query 的节点值集合(大小写不敏感) */
51
+ matched: Set<string>;
52
+ /** 需保留可见的节点值集合 = 命中 ∪ 祖先 ∪ 命中节点的子孙(上下文可见,不高亮) */
53
+ keep: Set<string>;
54
+ /** 命中节点祖先(搜索态自动展开的目标) */
55
+ expand: Set<string>;
56
+ }
57
+ /** 搜索过滤:命中节点 + 其全部祖先 + 其子孙保留可见(子孙是命中上下文,不高亮);
58
+ * 祖先进 expand(搜索态自动展开的目标)。空 query 返回空集。 */
59
+ declare function filterTreeNodes(nodes: TreeNode[], query: string): TreeFilterResult;
60
+ type TreeCheckState = 'checked' | 'unchecked' | 'mixed';
61
+ /**
62
+ * 三态求值:checked 集合(全部选中节点值)下某节点的勾选态。
63
+ * 自身 + 全部子孙都在集合 → checked;全不在 → unchecked;部分在 → mixed
64
+ * (父不在集合但全部子孙在也呈 mixed——诚实呈现部分选择,由 toggle 逻辑保证一致)。
65
+ */
66
+ declare function treeNodeCheckState(node: TreeNode, checked: ReadonlySet<string>): TreeCheckState;
67
+ declare function createTreeSelect(el: HTMLElement, opts: TreeSelectOptions): TreeSelectHandle;
68
+
69
+ export { type FlatTreeEntry, type TreeCheckState, type TreeFilterResult, type TreeNode, type TreeSelectHandle, type TreeSelectOptions, createTreeSelect, filterTreeNodes, flattenTreeNodes, treeNodeCheckState };