@icen.ai/ui 0.5.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/AGENTS.md +262 -0
  2. package/README.md +52 -13
  3. package/dist/base.css +33 -5
  4. package/dist/behaviors/accordion.d.mts +7 -0
  5. package/dist/behaviors/accordion.mjs +37 -11
  6. package/dist/behaviors/ai-chat.d.mts +59 -0
  7. package/dist/behaviors/ai-chat.mjs +580 -0
  8. package/dist/behaviors/ai-composer.d.mts +137 -0
  9. package/dist/behaviors/ai-composer.mjs +2114 -0
  10. package/dist/behaviors/ai-core.d.mts +156 -0
  11. package/dist/behaviors/ai-core.mjs +504 -0
  12. package/dist/behaviors/ai-diff.d.mts +53 -0
  13. package/dist/behaviors/ai-diff.mjs +413 -0
  14. package/dist/behaviors/ai-panel.d.mts +101 -0
  15. package/dist/behaviors/ai-panel.mjs +1087 -0
  16. package/dist/behaviors/ai-provider.d.mts +198 -0
  17. package/dist/behaviors/ai-provider.mjs +1061 -0
  18. package/dist/behaviors/ai-tool.d.mts +26 -0
  19. package/dist/behaviors/ai-tool.mjs +2173 -0
  20. package/dist/behaviors/ai-tools.d.mts +84 -0
  21. package/dist/behaviors/ai-tools.mjs +1762 -0
  22. package/dist/behaviors/back-top.d.mts +20 -0
  23. package/dist/behaviors/back-top.mjs +64 -32
  24. package/dist/behaviors/carousel.d.mts +7 -0
  25. package/dist/behaviors/carousel.mjs +72 -27
  26. package/dist/behaviors/charts.d.mts +296 -0
  27. package/dist/behaviors/charts.mjs +1364 -440
  28. package/dist/behaviors/command-palette.d.mts +42 -0
  29. package/dist/behaviors/command-palette.mjs +167 -27
  30. package/dist/behaviors/context-menu.d.mts +53 -0
  31. package/dist/behaviors/context-menu.mjs +127 -39
  32. package/dist/behaviors/controls.d.mts +59 -0
  33. package/dist/behaviors/controls.mjs +601 -0
  34. package/dist/behaviors/copy.d.mts +7 -0
  35. package/dist/behaviors/copy.mjs +41 -15
  36. package/dist/behaviors/datatable.d.mts +90 -0
  37. package/dist/behaviors/datatable.mjs +279 -53
  38. package/dist/behaviors/date-picker.d.mts +4 -0
  39. package/dist/behaviors/date-picker.mjs +276 -69
  40. package/dist/behaviors/dropdown.d.mts +8 -0
  41. package/dist/behaviors/dropdown.mjs +171 -28
  42. package/dist/behaviors/events.d.mts +330 -0
  43. package/dist/behaviors/events.mjs +208 -0
  44. package/dist/behaviors/input.d.mts +4 -0
  45. package/dist/behaviors/input.mjs +52 -32
  46. package/dist/behaviors/modal.d.mts +30 -0
  47. package/dist/behaviors/modal.mjs +87 -16
  48. package/dist/behaviors/nav.d.mts +7 -0
  49. package/dist/behaviors/nav.mjs +57 -15
  50. package/dist/behaviors/notification.d.mts +188 -0
  51. package/dist/behaviors/notification.mjs +274 -140
  52. package/dist/behaviors/popover.d.mts +82 -0
  53. package/dist/behaviors/popover.mjs +92 -5
  54. package/dist/behaviors/select.d.mts +6 -0
  55. package/dist/behaviors/select.mjs +287 -45
  56. package/dist/behaviors/sidebar.d.mts +7 -0
  57. package/dist/behaviors/sidebar.mjs +44 -13
  58. package/dist/behaviors/slider.d.mts +7 -0
  59. package/dist/behaviors/slider.mjs +55 -43
  60. package/dist/behaviors/split-pane.d.mts +8 -0
  61. package/dist/behaviors/split-pane.mjs +70 -29
  62. package/dist/behaviors/tabs.d.mts +9 -0
  63. package/dist/behaviors/tabs.mjs +102 -26
  64. package/dist/behaviors/tag-input.d.mts +7 -0
  65. package/dist/behaviors/tag-input.mjs +66 -17
  66. package/dist/behaviors/theme.d.mts +36 -0
  67. package/dist/behaviors/theme.mjs +51 -5
  68. package/dist/behaviors/toast.d.mts +48 -0
  69. package/dist/behaviors/toast.mjs +98 -25
  70. package/dist/behaviors/tree.d.mts +7 -0
  71. package/dist/behaviors/tree.mjs +39 -15
  72. package/dist/behaviors/upload.d.mts +4 -0
  73. package/dist/behaviors/upload.mjs +79 -44
  74. package/dist/cli.mjs +1 -1
  75. package/dist/components/ai-chat.css +1037 -0
  76. package/dist/components/ai-chat.mjs +3 -0
  77. package/dist/components/ai-composer.mjs +3 -0
  78. package/dist/components/ai-context.mjs +2 -0
  79. package/dist/components/ai-diff.css +311 -0
  80. package/dist/components/ai-diff.mjs +3 -0
  81. package/dist/components/ai-files.mjs +3 -0
  82. package/dist/components/ai-message.mjs +3 -0
  83. package/dist/components/ai-panel.css +682 -0
  84. package/dist/components/ai-reasoning.mjs +3 -0
  85. package/dist/components/ai-subagent.mjs +2 -0
  86. package/dist/components/ai-todo.mjs +2 -0
  87. package/dist/components/ai-tool-call.mjs +2 -0
  88. package/dist/components/ai-tool.css +532 -0
  89. package/dist/components/ai-usage.mjs +3 -0
  90. package/dist/components/badge.css +5 -9
  91. package/dist/components/breadcrumb.css +0 -36
  92. package/dist/components/btn.css +4 -7
  93. package/dist/components/card.css +3 -6
  94. package/dist/components/chart-bar.css +10 -0
  95. package/dist/components/chart-calendar.mjs +4 -0
  96. package/dist/components/chart-gauge.css +1 -1
  97. package/dist/components/chart-heatmap.css +40 -0
  98. package/dist/components/chart-pie.css +0 -6
  99. package/dist/components/chart-scatter.css +24 -0
  100. package/dist/components/chart-scatter.mjs +4 -0
  101. package/dist/components/charts.css +87 -2
  102. package/dist/components/charts.mjs +1 -0
  103. package/dist/components/command-palette.css +2 -2
  104. package/dist/components/content.css +11 -25
  105. package/dist/components/copy.css +8 -8
  106. package/dist/components/datatable.css +5 -9
  107. package/dist/components/date-picker.css +4 -8
  108. package/dist/components/empty.css +0 -4
  109. package/dist/components/feedback.css +10 -17
  110. package/dist/components/form.css +13 -14
  111. package/dist/components/input.css +2 -2
  112. package/dist/components/layout.css +36 -36
  113. package/dist/components/media.css +12 -8
  114. package/dist/components/menu.css +2 -2
  115. package/dist/components/modal.css +1 -1
  116. package/dist/components/nav.css +11 -6
  117. package/dist/components/notification.css +27 -28
  118. package/dist/components/pagination.css +4 -2
  119. package/dist/components/pagination.mjs +1 -0
  120. package/dist/components/panel.css +16 -16
  121. package/dist/components/pill.css +15 -15
  122. package/dist/components/rating.mjs +1 -0
  123. package/dist/components/segmented.css +14 -6
  124. package/dist/components/segmented.mjs +1 -0
  125. package/dist/components/select.css +79 -24
  126. package/dist/components/sidebar.css +1 -1
  127. package/dist/components/split-pane.css +0 -1
  128. package/dist/components/stat.css +14 -18
  129. package/dist/components/steps.css +15 -5
  130. package/dist/components/steps.mjs +1 -0
  131. package/dist/components/switch.css +11 -13
  132. package/dist/components/switch.mjs +1 -0
  133. package/dist/components/table.css +46 -47
  134. package/dist/components/table.mjs +1 -0
  135. package/dist/components/tabs.css +57 -45
  136. package/dist/components/tag-input.css +5 -10
  137. package/dist/components/toast.css +16 -13
  138. package/dist/components/tooltip.css +2 -2
  139. package/dist/components/upload.css +0 -15
  140. package/dist/index.d.mts +39 -0
  141. package/dist/index.mjs +11243 -3312
  142. package/dist/registry.json +147 -9
  143. package/dist/retro-effects.css +23 -32
  144. package/dist/tokens/colors.css +4 -1
  145. package/dist/tokens/retro-effects.css +23 -32
  146. package/dist/tokens/style-profiles.css +24 -2
  147. package/dist/tokens/typography.css +2 -0
  148. package/dist/tokens.css +30 -3
  149. package/dist/ui.css +3420 -645
  150. package/docs/spec/ai-native.md +662 -0
  151. package/package.json +15 -5
@@ -1,12 +1,142 @@
1
+ // src/behaviors/popover.ts
2
+ var PANEL_SIZING_ATTRS = {
3
+ width: "data-panel-width",
4
+ minWidth: "data-panel-min",
5
+ maxWidth: "data-panel-max",
6
+ minHeight: "data-panel-min-height",
7
+ maxHeight: "data-panel-max-height"
8
+ };
9
+ function readPanelSizing(el) {
10
+ const out = {};
11
+ if (!el) return out;
12
+ for (const key of Object.keys(PANEL_SIZING_ATTRS)) {
13
+ const raw = el.getAttribute(PANEL_SIZING_ATTRS[key]);
14
+ if (raw == null) continue;
15
+ const n = Number(raw);
16
+ if (Number.isFinite(n) && n > 0) out[key] = n;
17
+ }
18
+ return out;
19
+ }
20
+ function applyPanelSizing(panel, sizing) {
21
+ if (sizing == null) {
22
+ panel.style.width = "";
23
+ panel.style.minWidth = "";
24
+ panel.style.maxWidth = "";
25
+ panel.style.minHeight = "";
26
+ panel.style.maxHeight = "";
27
+ return;
28
+ }
29
+ const vw = typeof document === "undefined" ? Number.POSITIVE_INFINITY : document.documentElement.clientWidth;
30
+ const fitW = (n) => Math.min(n, Math.max(80, vw - 24));
31
+ if (sizing.width != null) {
32
+ const w = fitW(sizing.width);
33
+ panel.style.width = `${w}px`;
34
+ panel.style.maxWidth = `${w}px`;
35
+ }
36
+ if (sizing.minWidth != null) panel.style.minWidth = `${fitW(sizing.minWidth)}px`;
37
+ if (sizing.maxWidth != null && sizing.width == null) panel.style.maxWidth = `${fitW(sizing.maxWidth)}px`;
38
+ if (sizing.minHeight != null) panel.style.minHeight = `${sizing.minHeight}px`;
39
+ if (sizing.maxHeight != null) panel.style.maxHeight = `${sizing.maxHeight}px`;
40
+ }
41
+ var DEFAULT_MIN_WIDTH = 220;
42
+ var DEFAULT_MAX_WIDTH = 420;
43
+ var DEFAULT_MIN_HEIGHT = 120;
44
+ var DEFAULT_MAX_HEIGHT = 420;
45
+ function clamp(value, min, max) {
46
+ if (max < min) return min;
47
+ return Math.min(max, Math.max(min, value));
48
+ }
49
+ function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
50
+ const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
51
+ const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
52
+ if (preferredSpace >= desiredHeight) return preferredSide;
53
+ if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
54
+ if (preferredSpace >= oppositeSpace) return preferredSide;
55
+ return preferredSide === "top" ? "bottom" : "top";
56
+ }
57
+ function horizontalAnchor(anchor, width, align) {
58
+ if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
59
+ if (align === "end") return anchor.right - width;
60
+ return anchor.left;
61
+ }
62
+ function computePopoverLayout(anchor, viewport, opts = {}) {
63
+ const margin = opts.margin ?? 12;
64
+ const offset = opts.offset ?? 8;
65
+ const preferredSide = opts.side ?? "top";
66
+ const align = opts.align ?? "start";
67
+ if (preferredSide === "left" || preferredSide === "right") {
68
+ const availableLeft = Math.max(0, anchor.left - margin - offset);
69
+ const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
70
+ const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
71
+ const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
72
+ const maxWidth2 = Math.min(
73
+ Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
74
+ side2 === "left" ? availableLeft : availableRight
75
+ );
76
+ const width2 = minWidth2;
77
+ const left2 = clamp(
78
+ side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
79
+ margin,
80
+ viewport.width - margin - width2
81
+ );
82
+ const availableHeight2 = Math.max(80, viewport.height - margin * 2);
83
+ const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
84
+ const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
85
+ const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
86
+ const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
87
+ return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
88
+ }
89
+ const availableWidth = Math.max(80, viewport.width - margin * 2);
90
+ const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
91
+ const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
92
+ const maxWidth = Math.min(requestedMaxWidth, availableWidth);
93
+ const minWidth = Math.min(requestedMinWidth, maxWidth);
94
+ const availableTop = Math.max(0, anchor.top - margin - offset);
95
+ const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
96
+ const availableHeight = Math.max(80, viewport.height - margin * 2);
97
+ const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
98
+ const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
99
+ const fallbackHeight = Math.min(260, requestedMaxHeight);
100
+ const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
101
+ const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
102
+ const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
103
+ const sideSpace = side === "top" ? availableTop : availableBottom;
104
+ const maxHeight = Math.min(
105
+ requestedMaxHeight,
106
+ Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
107
+ availableHeight
108
+ );
109
+ const positionedHeight = clamp(desiredHeight, 40, maxHeight);
110
+ const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
111
+ const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
112
+ const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
113
+ const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
114
+ return { side, left, top, width, maxHeight };
115
+ }
116
+
117
+ // src/behaviors/events.ts
118
+ function emitIcen(source, type, detail) {
119
+ return source.dispatchEvent(
120
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
121
+ );
122
+ }
123
+
124
+ // src/behaviors/context-menu.ts
125
+ function emitMenuSelect(source, item) {
126
+ emitIcen(item, "icen:menu-select", {
127
+ source,
128
+ item,
129
+ value: item.dataset.value ?? "",
130
+ /* 优先专用 label 子元素(避开快捷键/图标文本混入),无则退整项文本 */
131
+ label: (item.querySelector(".menu-item-label, .command-item-label")?.textContent ?? item.textContent ?? "").trim().slice(0, 80)
132
+ });
133
+ }
134
+
1
135
  // src/behaviors/dropdown.ts
2
136
  var VIEWPORT_MARGIN = 12;
3
137
  var SIDE_OFFSET = 4;
4
138
  var active = null;
5
139
  var initialized = false;
6
- function clamp(value, min, max) {
7
- if (max < min) return min;
8
- return Math.min(max, Math.max(min, value));
9
- }
10
140
  function menuItems(panel) {
11
141
  return Array.from(panel.querySelectorAll(".menu-item:not(.is-disabled):not(:disabled)"));
12
142
  }
@@ -19,21 +149,14 @@ function place() {
19
149
  if (!active) return;
20
150
  const { trigger, panel } = active;
21
151
  const rect = trigger.getBoundingClientRect();
22
- const vw = window.innerWidth;
23
- const vh = window.innerHeight;
24
- const pw = panel.offsetWidth;
25
- const ph = panel.offsetHeight;
26
- const left = clamp(rect.left, VIEWPORT_MARGIN, vw - VIEWPORT_MARGIN - pw);
27
- let top = rect.bottom + SIDE_OFFSET;
28
- let side = "bottom";
29
- if (top + ph > vh - VIEWPORT_MARGIN && rect.top - SIDE_OFFSET - ph >= VIEWPORT_MARGIN) {
30
- top = rect.top - SIDE_OFFSET - ph;
31
- side = "top";
32
- }
33
- top = clamp(top, VIEWPORT_MARGIN, vh - VIEWPORT_MARGIN - ph);
34
- panel.style.left = `${left}px`;
35
- panel.style.top = `${top}px`;
36
- panel.dataset.side = side;
152
+ const layout = computePopoverLayout(
153
+ rect,
154
+ { width: window.innerWidth, height: window.innerHeight },
155
+ { side: "bottom", offset: SIDE_OFFSET, margin: VIEWPORT_MARGIN }
156
+ );
157
+ panel.style.left = `${layout.left}px`;
158
+ panel.style.top = `${layout.top}px`;
159
+ panel.dataset.side = layout.side;
37
160
  }
38
161
  function openDropdown(trigger, highlightFirst = false) {
39
162
  closeDropdown();
@@ -44,6 +167,7 @@ function openDropdown(trigger, highlightFirst = false) {
44
167
  panel.className = "menu";
45
168
  panel.setAttribute("role", "menu");
46
169
  panel.style.visibility = "hidden";
170
+ applyPanelSizing(panel, readPanelSizing(root));
47
171
  while (template.firstChild) panel.appendChild(template.firstChild);
48
172
  panel.querySelectorAll(".menu-item").forEach((el) => el.setAttribute("role", "menuitem"));
49
173
  document.body.appendChild(panel);
@@ -75,7 +199,10 @@ function onOutsidePointerDown(e) {
75
199
  if (active.panel.contains(target) || active.trigger.contains(target)) return;
76
200
  closeDropdown();
77
201
  }
78
- function onScroll() {
202
+ function onScroll(e) {
203
+ if (!active) return;
204
+ const target = e.target;
205
+ if (target instanceof Node && active.panel.contains(target)) return;
79
206
  closeDropdown();
80
207
  }
81
208
  function onKeyDown(e) {
@@ -97,21 +224,28 @@ function onKeyDown(e) {
97
224
  if (e.key === "Enter" && active.highlight >= 0) {
98
225
  e.preventDefault();
99
226
  const item = items[active.highlight];
227
+ emitMenuSelect(active.panel, item);
100
228
  closeDropdown(true);
101
229
  item.click();
102
230
  }
103
231
  }
104
- function initDropdown(root = document) {
105
- if (typeof document === "undefined") return;
106
- root.querySelectorAll("[data-dropdown-trigger]").forEach((el) => el.setAttribute("aria-expanded", "false"));
107
- if (initialized) return;
232
+ function initDropdown(root) {
233
+ if (typeof document === "undefined") return () => {
234
+ };
235
+ const scope = root ?? document;
236
+ scope.querySelectorAll("[data-dropdown-trigger]").forEach((el) => el.setAttribute("aria-expanded", "false"));
237
+ if (initialized) return () => {
238
+ };
108
239
  initialized = true;
109
- document.addEventListener("click", (e) => {
240
+ const onClick = (e) => {
110
241
  const target = e.target instanceof Element ? e.target : null;
111
242
  if (!target) return;
112
243
  if (active && target.closest(".menu-item") && active.panel.contains(target)) {
113
244
  const item = target.closest(".menu-item");
114
245
  if (item) {
246
+ if (!item.matches(".is-disabled") && !item.matches(":disabled")) {
247
+ emitMenuSelect(active.panel, item);
248
+ }
115
249
  closeDropdown();
116
250
  return;
117
251
  }
@@ -120,8 +254,9 @@ function initDropdown(root = document) {
120
254
  if (!trigger) return;
121
255
  if (active && active.trigger === trigger) closeDropdown();
122
256
  else openDropdown(trigger);
123
- });
124
- document.addEventListener("keydown", (e) => {
257
+ };
258
+ document.addEventListener("click", onClick);
259
+ const onKeyDown2 = (e) => {
125
260
  if (e.key !== "ArrowDown") return;
126
261
  if (active) return;
127
262
  const target = e.target instanceof Element ? e.target : null;
@@ -129,7 +264,15 @@ function initDropdown(root = document) {
129
264
  if (!trigger) return;
130
265
  e.preventDefault();
131
266
  openDropdown(trigger, true);
132
- });
267
+ };
268
+ document.addEventListener("keydown", onKeyDown2);
269
+ const destroy = () => {
270
+ document.removeEventListener("click", onClick);
271
+ document.removeEventListener("keydown", onKeyDown2);
272
+ closeDropdown();
273
+ initialized = false;
274
+ };
275
+ return destroy;
133
276
  }
134
277
  export {
135
278
  initDropdown
@@ -0,0 +1,330 @@
1
+ import { ChartEventDetail } from './charts.js';
2
+ import { AiUsage } from './ai-core.js';
3
+ import { AiDoneEventDetail } from './ai-provider.js';
4
+ import { AiComposerRefSource } from './ai-composer.js';
5
+ import './popover.js';
6
+ import './ai-panel.js';
7
+
8
+ interface IcenGestureDetail {
9
+ /** 手势命中源:最近的 [data-gestures] 宿主 */
10
+ source: HTMLElement;
11
+ /** 原始 DOM 事件(click / contextmenu / pointerdown / mouseup…) */
12
+ originalEvent: Event;
13
+ /** 视口坐标(指针手势) */
14
+ x: number;
15
+ y: number;
16
+ /** 划词文本(仅 icen:text-select) */
17
+ text?: string;
18
+ }
19
+ type IcenPolicy = (e: CustomEvent<IcenGestureDetail>) => void;
20
+ /** 手势层事件名(策略层只认这五种,编译期收死)。 */
21
+ type IcenGestureEvent = 'icen:click' | 'icen:dblclick' | 'icen:contextmenu' | 'icen:longpress' | 'icen:text-select';
22
+ /**
23
+ * 全库事件注册表:事件名 → detail 契约。onIcen 推断、emitIcen 校验,双侧同表。
24
+ * 新增事件必须在此登记(模板签名兜底只留给宿主自定义事件)。
25
+ */
26
+ interface IcenEventMap {
27
+ 'icen:click': IcenGestureDetail;
28
+ 'icen:dblclick': IcenGestureDetail;
29
+ 'icen:contextmenu': IcenGestureDetail;
30
+ 'icen:longpress': IcenGestureDetail;
31
+ 'icen:text-select': IcenGestureDetail;
32
+ 'icen:chart-hover': ChartEventDetail;
33
+ 'icen:chart-click': ChartEventDetail;
34
+ 'icen:chart-dblclick': ChartEventDetail;
35
+ 'icen:chart-contextmenu': ChartEventDetail;
36
+ 'icen:chart-legend-toggle': {
37
+ key: string;
38
+ seriesIndex: number;
39
+ hidden: boolean;
40
+ };
41
+ 'icen:chart-legend-visibility': {
42
+ visible: boolean;
43
+ };
44
+ 'icen:tab-change': {
45
+ tab: HTMLElement | null;
46
+ panel: HTMLElement | null;
47
+ index: number;
48
+ };
49
+ 'icen:tags-change': {
50
+ tags: string[];
51
+ };
52
+ 'icen:otp-complete': {
53
+ code: string;
54
+ };
55
+ 'icen:slider-input': {
56
+ value?: number;
57
+ lo?: number;
58
+ hi?: number;
59
+ };
60
+ 'icen:slider-change': {
61
+ value?: number;
62
+ lo?: number;
63
+ hi?: number;
64
+ };
65
+ 'icen:switch-change': {
66
+ el: HTMLElement;
67
+ checked: boolean;
68
+ };
69
+ 'icen:check-change': {
70
+ el: HTMLElement;
71
+ checked: boolean | 'mixed';
72
+ };
73
+ 'icen:stepper-change': {
74
+ el: HTMLElement;
75
+ value: number;
76
+ delta: number;
77
+ };
78
+ 'icen:segmented-change': {
79
+ el: HTMLElement;
80
+ value: string;
81
+ index: number;
82
+ };
83
+ 'icen:step-change': {
84
+ step: HTMLElement;
85
+ index: number;
86
+ };
87
+ 'icen:rating-change': {
88
+ el: HTMLElement;
89
+ value: number;
90
+ };
91
+ 'icen:page-change': {
92
+ page: number;
93
+ };
94
+ 'icen:sort-change': {
95
+ th: HTMLElement;
96
+ key: string;
97
+ dir: 'asc' | 'desc' | null;
98
+ };
99
+ 'icen:nav-select': {
100
+ item: HTMLElement;
101
+ index: number;
102
+ };
103
+ 'icen:sidebar-toggle': {
104
+ group: HTMLElement;
105
+ open: boolean;
106
+ };
107
+ 'icen:sidebar-collapse': {
108
+ collapsed: boolean;
109
+ };
110
+ 'icen:split-change': {
111
+ value: number;
112
+ };
113
+ 'icen:copy-success': {
114
+ text: string;
115
+ };
116
+ 'icen:copy-error': {
117
+ text: string;
118
+ reason: string;
119
+ };
120
+ 'icen:back-top': Record<string, never>;
121
+ 'icen:modal-open': {
122
+ id: string;
123
+ };
124
+ 'icen:modal-close': {
125
+ id: string;
126
+ };
127
+ 'icen:command-palette-open': Record<string, never>;
128
+ 'icen:command-palette-close': Record<string, never>;
129
+ 'icen:menu-select': {
130
+ source: HTMLElement;
131
+ item: HTMLElement;
132
+ value: string;
133
+ label: string;
134
+ };
135
+ 'icen:select-change': {
136
+ value: string;
137
+ values: string[];
138
+ label: string;
139
+ };
140
+ 'icen:date-change': {
141
+ value: string;
142
+ date: Date | null;
143
+ };
144
+ 'icen:accordion-toggle': {
145
+ item: HTMLElement;
146
+ index: number;
147
+ open: boolean;
148
+ };
149
+ 'icen:tree-select': {
150
+ node: HTMLElement;
151
+ };
152
+ 'icen:tree-toggle': {
153
+ node: HTMLElement;
154
+ open: boolean;
155
+ };
156
+ 'icen:carousel-change': {
157
+ index: number;
158
+ count: number;
159
+ };
160
+ 'icen:theme-change': {
161
+ preset: string;
162
+ dark: boolean;
163
+ style: string;
164
+ };
165
+ 'icen:table-sort-change': {
166
+ key: string;
167
+ dir: 'asc' | 'desc';
168
+ };
169
+ 'icen:table-page-change': {
170
+ page: number;
171
+ pageSize: number;
172
+ };
173
+ 'icen:table-page-size-change': {
174
+ pageSize: number;
175
+ };
176
+ 'icen:table-filter-change': {
177
+ key: string;
178
+ values: string[];
179
+ };
180
+ 'icen:table-search-change': {
181
+ query: string;
182
+ };
183
+ 'icen:table-selection-change': {
184
+ selected: unknown[];
185
+ };
186
+ 'icen:table-row-click': {
187
+ row: unknown;
188
+ index: number;
189
+ };
190
+ 'icen:table-row-toggle': {
191
+ row: unknown;
192
+ index: number;
193
+ expanded: boolean;
194
+ };
195
+ 'icen:table-columns-change': {
196
+ hidden: string[];
197
+ };
198
+ 'icen:table-export': {
199
+ count: number;
200
+ };
201
+ 'icen:upload': {
202
+ files: File[];
203
+ };
204
+ 'icen:upload-error': {
205
+ file: File | null;
206
+ reason: string;
207
+ };
208
+ 'icen:upload-remove': {
209
+ file: File;
210
+ };
211
+ 'icen:ai-toggle': {
212
+ el: HTMLElement;
213
+ open: boolean;
214
+ };
215
+ 'icen:ai-copy': {
216
+ el: HTMLElement;
217
+ };
218
+ 'icen:ai-retry': {
219
+ el: HTMLElement;
220
+ };
221
+ 'icen:ai-approve': {
222
+ id: string;
223
+ kind: string;
224
+ };
225
+ 'icen:ai-reject': {
226
+ id: string;
227
+ kind: string;
228
+ };
229
+ 'icen:ai-diff-accept': {
230
+ path: string;
231
+ };
232
+ 'icen:ai-diff-reject': {
233
+ path: string;
234
+ };
235
+ 'icen:ai-send': {
236
+ text: string;
237
+ };
238
+ 'icen:ai-stop': Record<string, never>;
239
+ 'icen:ai-queue': {
240
+ text: string;
241
+ };
242
+ 'icen:ai-dequeue': {
243
+ index: number;
244
+ };
245
+ 'icen:ai-attach': {
246
+ files: File[];
247
+ };
248
+ 'icen:ai-model-change': {
249
+ provider: string;
250
+ model: string;
251
+ label: string;
252
+ context?: number;
253
+ };
254
+ 'icen:ai-command': {
255
+ name: string;
256
+ args: string;
257
+ };
258
+ 'icen:ai-ref': {
259
+ action: 'add' | 'remove';
260
+ ref: AiComposerRefSource;
261
+ };
262
+ 'icen:ai-todo-toggle': {
263
+ index: number;
264
+ status: string;
265
+ };
266
+ 'icen:ai-audit-clear': Record<string, never>;
267
+ 'icen:ai-context-open': Record<string, never>;
268
+ 'icen:ai-context-close': Record<string, never>;
269
+ 'icen:ai-tool-call': {
270
+ name: string;
271
+ input: unknown;
272
+ el: HTMLElement;
273
+ };
274
+ 'icen:ai-tool-result': {
275
+ name: string;
276
+ ok: boolean;
277
+ el: HTMLElement;
278
+ error?: string;
279
+ count: number;
280
+ };
281
+ 'icen:ai-tool-evict': {
282
+ name: string;
283
+ el: HTMLElement;
284
+ };
285
+ 'icen:ai-usage': AiUsage;
286
+ 'icen:ai-done': AiDoneEventDetail;
287
+ [key: `icen:${string}`]: unknown;
288
+ }
289
+ interface IcenListenOptions {
290
+ /** 只监听该子树内冒泡上来的事件(组件实例级绑定)。
291
+ * 广播事件(自 document 派发)不经过元素子树:within 为 Element 时收不到,要收广播请用全局形态 */
292
+ within?: Element | Document;
293
+ /** AbortSignal:abort 即解绑(React useEffect 清理 / 批量卸载场景) */
294
+ signal?: AbortSignal;
295
+ /** 只触发一次(触发后自动解绑) */
296
+ once?: boolean;
297
+ }
298
+ /**
299
+ * 统一绑定器(全库事件一个入口):
300
+ * onIcen('icen:tab-change', (e) => e.detail.index) 全局
301
+ * onIcen('icen:chart-click', '.chart', (e) => …) 选择器委托
302
+ * onIcen('icen:upload', (e) => …, { within: formEl }) 子树限定
303
+ * onIcen('icen:ai-done', (e) => …, { signal: ctrl.signal }) 广播 + 信号解绑
304
+ * 返回解绑函数 —— React useEffect / Vue onUnmounted 直接 return。
305
+ */
306
+ declare function onIcen<K extends keyof IcenEventMap>(type: K, handler: (e: CustomEvent<IcenEventMap[K]>) => void, opts?: IcenListenOptions): () => void;
307
+ declare function onIcen<K extends keyof IcenEventMap>(type: K, selector: string, handler: (e: CustomEvent<IcenEventMap[K]>) => void, opts?: IcenListenOptions): () => void;
308
+ /**
309
+ * 全库唯一派生口:bubbles + composed + cancelable。返回 false = 被消费方 preventDefault。
310
+ * 组件作者派发领域事件一律走这里(不要裸写 new CustomEvent);detail 契约见 IcenEventMap。
311
+ * 从 document 派发 = 全局广播(传播只经 [document, window],仅 onIcen 全局形态可收;
312
+ * within 为 Element 收不到,要收广播请用全局形态)。
313
+ */
314
+ declare function emitIcen<K extends keyof IcenEventMap>(source: Element | Document, type: K, detail: IcenEventMap[K]): boolean;
315
+ /** 注册/覆盖某手势事件的全站默认行为;传 null 移除(= 禁用该默认)。
316
+ * 只接受手势层五种事件——领域事件的默认行为归组件交互契约,不会被静默忽略。 */
317
+ declare function setEventPolicy(type: IcenGestureEvent, policy: IcenPolicy | null): void;
318
+ /** 读取某手势事件当前注册的策略(与 setEventPolicy 对称的调试/测试读取器;未注册返回 undefined)。 */
319
+ declare function getEventPolicy(type: IcenGestureEvent): IcenPolicy | undefined;
320
+ /** 清空全部策略(测试/热重载用)。 */
321
+ declare function resetEventPolicies(): void;
322
+ /**
323
+ * 启动手势层(全局委托一次,幂等;重复调用安全)。
324
+ * 返回销毁函数:摘除全部 document 级委托并复位幂等标记,销毁后可重新 init。
325
+ */
326
+ declare function initGestures(): () => void;
327
+ /** 摘除手势层委托(等同 initGestures 返回的销毁函数;未启动时为 no-op)。 */
328
+ declare function destroyGestures(): void;
329
+
330
+ export { type IcenEventMap, type IcenGestureDetail, type IcenGestureEvent, type IcenListenOptions, type IcenPolicy, destroyGestures, emitIcen, getEventPolicy, initGestures, onIcen, resetEventPolicies, setEventPolicy };