@icen.ai/ui 0.5.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/AGENTS.md +256 -0
  2. package/README.md +47 -13
  3. package/dist/base.css +31 -4
  4. package/dist/behaviors/accordion.d.mts +4 -0
  5. package/dist/behaviors/accordion.mjs +4 -3
  6. package/dist/behaviors/ai-chat.d.mts +57 -0
  7. package/dist/behaviors/ai-chat.mjs +557 -0
  8. package/dist/behaviors/ai-composer.d.mts +112 -0
  9. package/dist/behaviors/ai-composer.mjs +2032 -0
  10. package/dist/behaviors/ai-core.d.mts +151 -0
  11. package/dist/behaviors/ai-core.mjs +491 -0
  12. package/dist/behaviors/ai-diff.d.mts +53 -0
  13. package/dist/behaviors/ai-diff.mjs +282 -0
  14. package/dist/behaviors/ai-panel.d.mts +98 -0
  15. package/dist/behaviors/ai-panel.mjs +1012 -0
  16. package/dist/behaviors/ai-provider.d.mts +198 -0
  17. package/dist/behaviors/ai-provider.mjs +1055 -0
  18. package/dist/behaviors/ai-tool.d.mts +18 -0
  19. package/dist/behaviors/ai-tool.mjs +2125 -0
  20. package/dist/behaviors/ai-tools.d.mts +81 -0
  21. package/dist/behaviors/ai-tools.mjs +1658 -0
  22. package/dist/behaviors/back-top.d.mts +20 -0
  23. package/dist/behaviors/back-top.mjs +49 -32
  24. package/dist/behaviors/carousel.d.mts +4 -0
  25. package/dist/behaviors/carousel.mjs +34 -16
  26. package/dist/behaviors/charts.d.mts +250 -0
  27. package/dist/behaviors/charts.mjs +1301 -448
  28. package/dist/behaviors/command-palette.d.mts +8 -0
  29. package/dist/behaviors/command-palette.mjs +24 -12
  30. package/dist/behaviors/context-menu.d.mts +36 -0
  31. package/dist/behaviors/context-menu.mjs +6 -2
  32. package/dist/behaviors/copy.d.mts +4 -0
  33. package/dist/behaviors/copy.mjs +7 -6
  34. package/dist/behaviors/datatable.d.mts +78 -0
  35. package/dist/behaviors/datatable.mjs +54 -11
  36. package/dist/behaviors/date-picker.d.mts +4 -0
  37. package/dist/behaviors/date-picker.mjs +104 -36
  38. package/dist/behaviors/dropdown.d.mts +4 -0
  39. package/dist/behaviors/dropdown.mjs +7 -3
  40. package/dist/behaviors/input.d.mts +4 -0
  41. package/dist/behaviors/input.mjs +29 -22
  42. package/dist/behaviors/modal.d.mts +11 -0
  43. package/dist/behaviors/modal.mjs +11 -6
  44. package/dist/behaviors/nav.d.mts +7 -0
  45. package/dist/behaviors/nav.mjs +25 -9
  46. package/dist/behaviors/notification.d.mts +172 -0
  47. package/dist/behaviors/notification.mjs +243 -89
  48. package/dist/behaviors/popover.d.mts +46 -0
  49. package/dist/behaviors/popover.mjs +2 -1
  50. package/dist/behaviors/select.d.mts +4 -0
  51. package/dist/behaviors/select.mjs +131 -26
  52. package/dist/behaviors/sidebar.d.mts +4 -0
  53. package/dist/behaviors/sidebar.mjs +4 -3
  54. package/dist/behaviors/slider.d.mts +4 -0
  55. package/dist/behaviors/slider.mjs +4 -3
  56. package/dist/behaviors/split-pane.d.mts +4 -0
  57. package/dist/behaviors/split-pane.mjs +13 -13
  58. package/dist/behaviors/tabs.d.mts +8 -0
  59. package/dist/behaviors/tabs.mjs +44 -19
  60. package/dist/behaviors/tag-input.d.mts +4 -0
  61. package/dist/behaviors/tag-input.mjs +12 -3
  62. package/dist/behaviors/theme.d.mts +25 -0
  63. package/dist/behaviors/theme.mjs +3 -1
  64. package/dist/behaviors/toast.d.mts +26 -0
  65. package/dist/behaviors/toast.mjs +24 -18
  66. package/dist/behaviors/tree.d.mts +4 -0
  67. package/dist/behaviors/tree.mjs +4 -3
  68. package/dist/behaviors/upload.d.mts +4 -0
  69. package/dist/behaviors/upload.mjs +63 -26
  70. package/dist/components/ai-chat.css +1037 -0
  71. package/dist/components/ai-chat.mjs +3 -0
  72. package/dist/components/ai-composer.mjs +3 -0
  73. package/dist/components/ai-context.mjs +2 -0
  74. package/dist/components/ai-diff.css +311 -0
  75. package/dist/components/ai-diff.mjs +3 -0
  76. package/dist/components/ai-files.mjs +3 -0
  77. package/dist/components/ai-message.mjs +3 -0
  78. package/dist/components/ai-panel.css +682 -0
  79. package/dist/components/ai-reasoning.mjs +3 -0
  80. package/dist/components/ai-subagent.mjs +2 -0
  81. package/dist/components/ai-todo.mjs +2 -0
  82. package/dist/components/ai-tool-call.mjs +2 -0
  83. package/dist/components/ai-tool.css +532 -0
  84. package/dist/components/ai-usage.mjs +3 -0
  85. package/dist/components/badge.css +4 -8
  86. package/dist/components/breadcrumb.css +1 -5
  87. package/dist/components/btn.css +4 -7
  88. package/dist/components/card.css +3 -6
  89. package/dist/components/chart-bar.css +10 -0
  90. package/dist/components/chart-calendar.mjs +5 -0
  91. package/dist/components/chart-gauge.css +1 -1
  92. package/dist/components/chart-heatmap.css +40 -0
  93. package/dist/components/chart-scatter.css +24 -0
  94. package/dist/components/chart-scatter.mjs +4 -0
  95. package/dist/components/charts.css +86 -1
  96. package/dist/components/charts.mjs +1 -0
  97. package/dist/components/command-palette.css +2 -2
  98. package/dist/components/content.css +8 -9
  99. package/dist/components/copy.css +8 -8
  100. package/dist/components/datatable.css +4 -8
  101. package/dist/components/date-picker.css +3 -7
  102. package/dist/components/empty.css +0 -4
  103. package/dist/components/feedback.css +10 -17
  104. package/dist/components/form.css +13 -14
  105. package/dist/components/input.css +2 -2
  106. package/dist/components/layout.css +36 -36
  107. package/dist/components/media.css +12 -8
  108. package/dist/components/menu.css +2 -2
  109. package/dist/components/modal.css +1 -1
  110. package/dist/components/nav.css +11 -6
  111. package/dist/components/notification.css +28 -28
  112. package/dist/components/pagination.css +1 -1
  113. package/dist/components/panel.css +16 -16
  114. package/dist/components/pill.css +15 -15
  115. package/dist/components/segmented.css +5 -5
  116. package/dist/components/segmented.mjs +1 -0
  117. package/dist/components/select.css +12 -20
  118. package/dist/components/sidebar.css +1 -1
  119. package/dist/components/split-pane.css +0 -1
  120. package/dist/components/stat.css +14 -18
  121. package/dist/components/steps.css +2 -2
  122. package/dist/components/switch.css +8 -12
  123. package/dist/components/table.css +46 -47
  124. package/dist/components/tabs.css +35 -46
  125. package/dist/components/tag-input.css +4 -4
  126. package/dist/components/toast.css +16 -13
  127. package/dist/components/tooltip.css +2 -2
  128. package/dist/index.d.mts +37 -0
  129. package/dist/index.mjs +7890 -1199
  130. package/dist/registry.json +113 -4
  131. package/dist/retro-effects.css +21 -17
  132. package/dist/tokens/colors.css +4 -1
  133. package/dist/tokens/retro-effects.css +21 -17
  134. package/dist/tokens/style-profiles.css +21 -0
  135. package/dist/tokens.css +25 -1
  136. package/dist/ui.css +3168 -433
  137. package/docs/spec/ai-native.md +659 -0
  138. package/package.json +15 -5
@@ -18,6 +18,19 @@ function isAccepted(file, accept) {
18
18
  }
19
19
  var fileIconSvg = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
20
20
  var xIconSvg = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>';
21
+ function parseSvg(source) {
22
+ try {
23
+ const doc = new DOMParser().parseFromString(source, "image/svg+xml");
24
+ const svg = doc.documentElement;
25
+ if (!svg || svg.tagName.toLowerCase() !== "svg") return null;
26
+ return document.importNode(svg, true);
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ function sameFile(a, b) {
32
+ return a.name === b.name && a.size === b.size && a.lastModified === b.lastModified;
33
+ }
21
34
  function setup(zone) {
22
35
  const el = zone;
23
36
  if (el.__icenUploadInit) return;
@@ -30,6 +43,7 @@ function setup(zone) {
30
43
  const accept = input.getAttribute("accept") ?? "";
31
44
  let currentFiles = [];
32
45
  const urlMap = /* @__PURE__ */ new Map();
46
+ let syncing = false;
33
47
  function getListEl() {
34
48
  if (!hasList) return null;
35
49
  let list = zone.querySelector(".upload-list");
@@ -43,8 +57,12 @@ function setup(zone) {
43
57
  function renderList() {
44
58
  const list = getListEl();
45
59
  if (!list) return;
46
- urlMap.forEach((url) => URL.revokeObjectURL(url));
47
- urlMap.clear();
60
+ urlMap.forEach((url, f) => {
61
+ if (!currentFiles.includes(f)) {
62
+ URL.revokeObjectURL(url);
63
+ urlMap.delete(f);
64
+ }
65
+ });
48
66
  list.textContent = "";
49
67
  if (currentFiles.length === 0) {
50
68
  list.hidden = true;
@@ -55,25 +73,29 @@ function setup(zone) {
55
73
  const li = document.createElement("li");
56
74
  li.className = "upload-list-item";
57
75
  const isImage = file.type.startsWith("image/");
76
+ let thumbUrl;
58
77
  if (isImage) {
59
- try {
60
- const url = URL.createObjectURL(file);
61
- urlMap.set(file, url);
62
- const img = document.createElement("img");
63
- img.className = "upload-list-item-thumb";
64
- img.src = url;
65
- img.alt = file.name;
66
- li.appendChild(img);
67
- } catch {
68
- const icon = document.createElement("span");
69
- icon.className = "upload-list-item-icon";
70
- icon.innerHTML = fileIconSvg;
71
- li.appendChild(icon);
78
+ thumbUrl = urlMap.get(file);
79
+ if (!thumbUrl) {
80
+ try {
81
+ thumbUrl = URL.createObjectURL(file);
82
+ urlMap.set(file, thumbUrl);
83
+ } catch {
84
+ thumbUrl = void 0;
85
+ }
72
86
  }
87
+ }
88
+ if (thumbUrl) {
89
+ const img = document.createElement("img");
90
+ img.className = "upload-list-item-thumb";
91
+ img.src = thumbUrl;
92
+ img.alt = file.name;
93
+ li.appendChild(img);
73
94
  } else {
74
95
  const icon = document.createElement("span");
75
96
  icon.className = "upload-list-item-icon";
76
- icon.innerHTML = fileIconSvg;
97
+ const svg = parseSvg(fileIconSvg);
98
+ if (svg) icon.appendChild(svg);
77
99
  li.appendChild(icon);
78
100
  }
79
101
  const info = document.createElement("div");
@@ -91,7 +113,8 @@ function setup(zone) {
91
113
  removeBtn.className = "upload-list-item-x";
92
114
  removeBtn.type = "button";
93
115
  removeBtn.setAttribute("aria-label", `\u79FB\u9664 ${file.name}`);
94
- removeBtn.innerHTML = xIconSvg;
116
+ const xSvg = parseSvg(xIconSvg);
117
+ if (xSvg) removeBtn.appendChild(xSvg);
95
118
  removeBtn.addEventListener("click", (ev) => {
96
119
  ev.stopPropagation();
97
120
  currentFiles = currentFiles.filter((f) => f !== file);
@@ -107,9 +130,12 @@ function setup(zone) {
107
130
  try {
108
131
  const dt = new DataTransfer();
109
132
  for (const f of currentFiles) dt.items.add(f);
133
+ syncing = true;
110
134
  input.files = dt.files;
111
135
  input.dispatchEvent(new Event("change", { bubbles: true }));
112
136
  } catch {
137
+ } finally {
138
+ syncing = false;
113
139
  }
114
140
  }
115
141
  function validate(file) {
@@ -129,20 +155,28 @@ function setup(zone) {
129
155
  }
130
156
  if (valid.length === 0) return;
131
157
  if (hasList) {
158
+ const fresh = [];
159
+ for (const f of valid) {
160
+ if (currentFiles.some((c) => sameFile(c, f)) || fresh.some((c) => sameFile(c, f))) continue;
161
+ fresh.push(f);
162
+ }
163
+ if (fresh.length === 0) return;
132
164
  if (maxFiles > 0) {
133
165
  const remaining = maxFiles - currentFiles.length;
134
166
  if (remaining <= 0) {
135
167
  zone.dispatchEvent(new CustomEvent("icen:upload-error", {
136
168
  bubbles: true,
137
- detail: { reason: `\u6700\u591A ${maxFiles} \u4E2A\u6587\u4EF6` }
169
+ detail: { file: null, reason: `\u6700\u591A ${maxFiles} \u4E2A\u6587\u4EF6` }
138
170
  }));
139
171
  return;
140
172
  }
141
- valid.splice(remaining);
173
+ if (fresh.length > remaining) fresh.length = remaining;
142
174
  }
143
- currentFiles.push(...valid);
175
+ currentFiles.push(...fresh);
144
176
  renderList();
145
177
  syncInputFiles();
178
+ zone.dispatchEvent(new CustomEvent("icen:upload", { bubbles: true, detail: { files: fresh } }));
179
+ return;
146
180
  }
147
181
  zone.dispatchEvent(new CustomEvent("icen:upload", { bubbles: true, detail: { files: valid } }));
148
182
  }
@@ -160,14 +194,13 @@ function setup(zone) {
160
194
  }
161
195
  });
162
196
  input.addEventListener("change", () => {
197
+ if (syncing) return;
163
198
  if (!input.files) return;
164
199
  const files = Array.from(input.files);
165
200
  if (hasList) {
166
- handleFiles(files);
167
201
  input.value = "";
168
- } else {
169
- handleFiles(files);
170
202
  }
203
+ handleFiles(files);
171
204
  });
172
205
  zone.addEventListener("dragover", (ev) => {
173
206
  ev.preventDefault();
@@ -186,18 +219,22 @@ function setup(zone) {
186
219
  try {
187
220
  const dt = new DataTransfer();
188
221
  for (const f of Array.from(files)) dt.items.add(f);
222
+ syncing = true;
189
223
  input.files = dt.files;
190
224
  input.dispatchEvent(new Event("change", { bubbles: true }));
191
225
  } catch {
226
+ } finally {
227
+ syncing = false;
192
228
  }
193
229
  }
194
230
  });
195
231
  }
196
- function initUpload(root = document) {
232
+ function initUpload(root) {
197
233
  if (typeof document === "undefined") return;
234
+ const scope = root ?? document;
198
235
  const zones = [];
199
- if (root instanceof HTMLElement && root.matches(".upload")) zones.push(root);
200
- zones.push(...Array.from(root.querySelectorAll(".upload")));
236
+ if (scope instanceof HTMLElement && scope.matches(".upload")) zones.push(scope);
237
+ zones.push(...Array.from(scope.querySelectorAll(".upload")));
201
238
  for (const z of zones) setup(z);
202
239
  }
203
240
  export {