@icen.ai/ui 0.8.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.
- package/AGENTS.md +15 -9
- package/README.md +8 -3
- package/dist/base.css +4 -3
- package/dist/behaviors/accordion.d.mts +5 -2
- package/dist/behaviors/accordion.mjs +36 -11
- package/dist/behaviors/ai-chat.d.mts +3 -1
- package/dist/behaviors/ai-chat.mjs +66 -43
- package/dist/behaviors/ai-composer.d.mts +28 -3
- package/dist/behaviors/ai-composer.mjs +291 -209
- package/dist/behaviors/ai-core.d.mts +9 -4
- package/dist/behaviors/ai-core.mjs +21 -8
- package/dist/behaviors/ai-diff.d.mts +2 -2
- package/dist/behaviors/ai-diff.mjs +172 -41
- package/dist/behaviors/ai-panel.d.mts +11 -8
- package/dist/behaviors/ai-panel.mjs +185 -110
- package/dist/behaviors/ai-provider.mjs +54 -48
- package/dist/behaviors/ai-tool.d.mts +12 -4
- package/dist/behaviors/ai-tool.mjs +311 -263
- package/dist/behaviors/ai-tools.d.mts +9 -6
- package/dist/behaviors/ai-tools.mjs +259 -155
- package/dist/behaviors/back-top.mjs +16 -1
- package/dist/behaviors/carousel.d.mts +5 -2
- package/dist/behaviors/carousel.mjs +53 -26
- package/dist/behaviors/charts.d.mts +68 -22
- package/dist/behaviors/charts.mjs +221 -150
- package/dist/behaviors/command-palette.d.mts +40 -6
- package/dist/behaviors/command-palette.mjs +143 -15
- package/dist/behaviors/context-menu.d.mts +21 -4
- package/dist/behaviors/context-menu.mjs +122 -38
- package/dist/behaviors/controls.d.mts +59 -0
- package/dist/behaviors/controls.mjs +601 -0
- package/dist/behaviors/copy.d.mts +5 -2
- package/dist/behaviors/copy.mjs +37 -12
- package/dist/behaviors/datatable.d.mts +12 -0
- package/dist/behaviors/datatable.mjs +226 -43
- package/dist/behaviors/date-picker.d.mts +2 -2
- package/dist/behaviors/date-picker.mjs +171 -32
- package/dist/behaviors/dropdown.d.mts +6 -2
- package/dist/behaviors/dropdown.mjs +164 -25
- package/dist/behaviors/events.d.mts +330 -0
- package/dist/behaviors/events.mjs +208 -0
- package/dist/behaviors/input.mjs +25 -12
- package/dist/behaviors/modal.d.mts +24 -5
- package/dist/behaviors/modal.mjs +76 -10
- package/dist/behaviors/nav.d.mts +1 -1
- package/dist/behaviors/nav.mjs +33 -7
- package/dist/behaviors/notification.d.mts +20 -4
- package/dist/behaviors/notification.mjs +53 -73
- package/dist/behaviors/popover.d.mts +39 -3
- package/dist/behaviors/popover.mjs +90 -4
- package/dist/behaviors/select.d.mts +4 -2
- package/dist/behaviors/select.mjs +161 -24
- package/dist/behaviors/sidebar.d.mts +5 -2
- package/dist/behaviors/sidebar.mjs +43 -13
- package/dist/behaviors/slider.d.mts +5 -2
- package/dist/behaviors/slider.mjs +54 -43
- package/dist/behaviors/split-pane.d.mts +6 -2
- package/dist/behaviors/split-pane.mjs +62 -21
- package/dist/behaviors/tabs.d.mts +3 -2
- package/dist/behaviors/tabs.mjs +65 -14
- package/dist/behaviors/tag-input.d.mts +5 -2
- package/dist/behaviors/tag-input.mjs +61 -21
- package/dist/behaviors/theme.d.mts +13 -2
- package/dist/behaviors/theme.mjs +48 -4
- package/dist/behaviors/toast.d.mts +30 -8
- package/dist/behaviors/toast.mjs +74 -7
- package/dist/behaviors/tree.d.mts +5 -2
- package/dist/behaviors/tree.mjs +38 -15
- package/dist/behaviors/upload.mjs +21 -23
- package/dist/cli.mjs +1 -1
- package/dist/components/ai-panel.css +1 -1
- package/dist/components/ai-tool-call.mjs +1 -1
- package/dist/components/badge.css +1 -1
- package/dist/components/breadcrumb.css +0 -32
- package/dist/components/chart-calendar.mjs +1 -2
- package/dist/components/chart-pie.css +0 -6
- package/dist/components/charts.css +1 -1
- package/dist/components/content.css +3 -16
- package/dist/components/datatable.css +1 -1
- package/dist/components/date-picker.css +1 -1
- package/dist/components/notification.css +2 -3
- package/dist/components/pagination.css +3 -1
- package/dist/components/pagination.mjs +1 -0
- package/dist/components/rating.mjs +1 -0
- package/dist/components/segmented.css +9 -1
- package/dist/components/segmented.mjs +1 -1
- package/dist/components/select.css +67 -4
- package/dist/components/steps.css +13 -3
- package/dist/components/steps.mjs +1 -0
- package/dist/components/switch.css +3 -1
- package/dist/components/switch.mjs +1 -0
- package/dist/components/table.mjs +1 -0
- package/dist/components/tabs.css +23 -0
- package/dist/components/tag-input.css +1 -6
- package/dist/components/upload.css +0 -15
- package/dist/index.d.mts +12 -10
- package/dist/index.mjs +2700 -1460
- package/dist/registry.json +42 -13
- package/dist/retro-effects.css +3 -16
- package/dist/tokens/retro-effects.css +3 -16
- package/dist/tokens/style-profiles.css +3 -2
- package/dist/tokens/typography.css +2 -0
- package/dist/tokens.css +5 -2
- package/dist/ui.css +138 -98
- package/docs/spec/ai-native.md +7 -4
- package/package.json +1 -1
package/dist/behaviors/toast.mjs
CHANGED
|
@@ -18,10 +18,40 @@ function ensureContainer() {
|
|
|
18
18
|
}
|
|
19
19
|
var KIND_CLASS = {
|
|
20
20
|
ok: "toast--success",
|
|
21
|
+
success: "toast--success",
|
|
21
22
|
err: "toast--error",
|
|
23
|
+
error: "toast--error",
|
|
22
24
|
warn: "toast--warning",
|
|
23
25
|
info: "toast--info"
|
|
24
26
|
};
|
|
27
|
+
var CLOSE_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
|
|
28
|
+
function stripUnsafeSvg(el) {
|
|
29
|
+
for (const attr of Array.from(el.attributes)) {
|
|
30
|
+
if (attr.name.toLowerCase().startsWith("on")) el.removeAttribute(attr.name);
|
|
31
|
+
}
|
|
32
|
+
for (const child of Array.from(el.children)) {
|
|
33
|
+
const tag = child.tagName.toLowerCase();
|
|
34
|
+
if (tag === "script" || tag === "foreignobject") child.remove();
|
|
35
|
+
else stripUnsafeSvg(child);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function sanitizeSvgIcon(source) {
|
|
39
|
+
if (!source.trimStart().startsWith("<svg")) return null;
|
|
40
|
+
try {
|
|
41
|
+
const doc = new DOMParser().parseFromString(source, "image/svg+xml");
|
|
42
|
+
const svg = doc.documentElement;
|
|
43
|
+
if (!svg || svg.tagName.toLowerCase() !== "svg") return null;
|
|
44
|
+
stripUnsafeSvg(svg);
|
|
45
|
+
return document.importNode(svg, true);
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
var closeSvgCache = null;
|
|
51
|
+
function closeSvg() {
|
|
52
|
+
closeSvgCache ??= sanitizeSvgIcon(CLOSE_SVG);
|
|
53
|
+
return closeSvgCache ? closeSvgCache.cloneNode(true) : null;
|
|
54
|
+
}
|
|
25
55
|
function scheduleRemove(el) {
|
|
26
56
|
el.classList.add("is-leaving");
|
|
27
57
|
let done = false;
|
|
@@ -48,12 +78,23 @@ function trimStack(container) {
|
|
|
48
78
|
}
|
|
49
79
|
}
|
|
50
80
|
function show(message, kind = "info", opts = {}) {
|
|
51
|
-
if (!isBrowser()) return
|
|
52
|
-
|
|
81
|
+
if (!isBrowser()) return { dismiss() {
|
|
82
|
+
} };
|
|
83
|
+
const { ms, actionLabel, onAction, icon, closable, progress } = opts;
|
|
53
84
|
const container = ensureContainer();
|
|
54
85
|
const el = document.createElement("div");
|
|
55
86
|
el.className = `toast ${KIND_CLASS[kind]}`;
|
|
56
|
-
el.setAttribute("role", kind === "err" ? "alert" : "status");
|
|
87
|
+
el.setAttribute("role", kind === "err" || kind === "error" ? "alert" : "status");
|
|
88
|
+
if (icon) {
|
|
89
|
+
const svg = sanitizeSvgIcon(icon);
|
|
90
|
+
if (svg) {
|
|
91
|
+
const iconWrap = document.createElement("span");
|
|
92
|
+
iconWrap.className = "toast-icon";
|
|
93
|
+
iconWrap.setAttribute("aria-hidden", "true");
|
|
94
|
+
iconWrap.appendChild(svg);
|
|
95
|
+
el.appendChild(iconWrap);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
57
98
|
const msgNode = document.createElement("span");
|
|
58
99
|
msgNode.className = "toast-msg";
|
|
59
100
|
msgNode.textContent = message;
|
|
@@ -66,16 +107,39 @@ function show(message, kind = "info", opts = {}) {
|
|
|
66
107
|
actionBtn.textContent = actionLabel;
|
|
67
108
|
el.appendChild(actionBtn);
|
|
68
109
|
}
|
|
69
|
-
|
|
70
|
-
|
|
110
|
+
const duration = ms ?? (actionLabel ? ACTION_MS : DEFAULT_MS);
|
|
111
|
+
let timer = 0;
|
|
71
112
|
let removed = false;
|
|
72
113
|
const remove = () => {
|
|
73
114
|
if (removed) return;
|
|
74
115
|
removed = true;
|
|
116
|
+
if (timer) window.clearTimeout(timer);
|
|
75
117
|
scheduleRemove(el);
|
|
76
118
|
};
|
|
77
|
-
|
|
78
|
-
|
|
119
|
+
if (closable) {
|
|
120
|
+
const closeBtn = document.createElement("button");
|
|
121
|
+
closeBtn.type = "button";
|
|
122
|
+
closeBtn.className = "toast-close";
|
|
123
|
+
closeBtn.setAttribute("aria-label", "\u5173\u95ED");
|
|
124
|
+
const svg = closeSvg();
|
|
125
|
+
if (svg) closeBtn.appendChild(svg);
|
|
126
|
+
else closeBtn.textContent = "\xD7";
|
|
127
|
+
closeBtn.addEventListener("click", remove);
|
|
128
|
+
el.appendChild(closeBtn);
|
|
129
|
+
}
|
|
130
|
+
if (progress) {
|
|
131
|
+
const bar = document.createElement("div");
|
|
132
|
+
bar.className = "toast-progress";
|
|
133
|
+
bar.setAttribute("aria-hidden", "true");
|
|
134
|
+
const fill = document.createElement("div");
|
|
135
|
+
fill.className = "toast-progress-fill";
|
|
136
|
+
fill.style.animationDuration = `${duration}ms`;
|
|
137
|
+
bar.appendChild(fill);
|
|
138
|
+
el.appendChild(bar);
|
|
139
|
+
}
|
|
140
|
+
container.appendChild(el);
|
|
141
|
+
trimStack(container);
|
|
142
|
+
timer = window.setTimeout(remove, duration);
|
|
79
143
|
if (actionBtn && onAction) {
|
|
80
144
|
actionBtn.addEventListener("click", () => {
|
|
81
145
|
window.clearTimeout(timer);
|
|
@@ -84,11 +148,14 @@ function show(message, kind = "info", opts = {}) {
|
|
|
84
148
|
remove();
|
|
85
149
|
});
|
|
86
150
|
}
|
|
151
|
+
return { dismiss: remove };
|
|
87
152
|
}
|
|
88
153
|
var toast = Object.assign(
|
|
89
154
|
(message, kind = "info", ms) => show(message, kind, { ms }),
|
|
90
155
|
{
|
|
156
|
+
success: (message, ms) => show(message, "success", { ms }),
|
|
91
157
|
ok: (message, ms) => show(message, "ok", { ms }),
|
|
158
|
+
error: (message, ms) => show(message, "error", { ms }),
|
|
92
159
|
err: (message, ms) => show(message, "err", { ms }),
|
|
93
160
|
warn: (message, ms) => show(message, "warn", { ms }),
|
|
94
161
|
info: (message, ms) => show(message, "info", { ms }),
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 为 root 下每棵 .tree 初始化(root 自身是 .tree 也算)。
|
|
3
|
+
* 返回销毁函数:移除本次挂上的容器监听并复位幂等标记(销毁后可重新 init)。SSR 下返回 no-op。
|
|
4
|
+
*/
|
|
5
|
+
declare function initTree(root?: ParentNode): () => void;
|
|
3
6
|
|
|
4
7
|
export { initTree };
|
package/dist/behaviors/tree.mjs
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
// src/behaviors/events.ts
|
|
2
|
+
function emitIcen(source, type, detail) {
|
|
3
|
+
return source.dispatchEvent(
|
|
4
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
5
|
+
);
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
// src/behaviors/tree.ts
|
|
2
9
|
function setup(tree) {
|
|
3
10
|
const el = tree;
|
|
4
|
-
if (el.__icenTreeInit) return;
|
|
11
|
+
if (el.__icenTreeInit) return void 0;
|
|
5
12
|
el.__icenTreeInit = true;
|
|
6
13
|
tree.querySelectorAll(".tree-node").forEach((node) => {
|
|
7
14
|
if (!node.hasAttribute("tabindex")) node.tabIndex = 0;
|
|
8
15
|
});
|
|
9
|
-
|
|
16
|
+
const onClick = (ev) => {
|
|
10
17
|
const target = ev.target;
|
|
11
18
|
if (!(target instanceof Element)) return;
|
|
12
19
|
const node = target.closest(".tree-node");
|
|
@@ -28,6 +35,7 @@ function setup(tree) {
|
|
|
28
35
|
toggle?.classList.add("is-collapsed");
|
|
29
36
|
toggle?.setAttribute("aria-expanded", "false");
|
|
30
37
|
}
|
|
38
|
+
emitIcen(tree, "icen:tree-toggle", { node: li, open: !collapsed });
|
|
31
39
|
return;
|
|
32
40
|
}
|
|
33
41
|
if (childUl) return;
|
|
@@ -37,12 +45,9 @@ function setup(tree) {
|
|
|
37
45
|
});
|
|
38
46
|
node.classList.add("is-selected");
|
|
39
47
|
node.setAttribute("aria-selected", "true");
|
|
40
|
-
tree
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}));
|
|
44
|
-
});
|
|
45
|
-
tree.addEventListener("keydown", (ev) => {
|
|
48
|
+
emitIcen(tree, "icen:tree-select", { node });
|
|
49
|
+
};
|
|
50
|
+
const onKeyDown = (ev) => {
|
|
46
51
|
const kev = ev;
|
|
47
52
|
const target = kev.target;
|
|
48
53
|
if (!(target instanceof Element)) return;
|
|
@@ -71,6 +76,7 @@ function setup(tree) {
|
|
|
71
76
|
childUl.removeAttribute("hidden");
|
|
72
77
|
node.querySelector(".tree-toggle")?.setAttribute("aria-expanded", "true");
|
|
73
78
|
node.querySelector(".tree-toggle")?.classList.remove("is-collapsed");
|
|
79
|
+
if (li) emitIcen(tree, "icen:tree-toggle", { node: li, open: true });
|
|
74
80
|
}
|
|
75
81
|
break;
|
|
76
82
|
}
|
|
@@ -82,19 +88,36 @@ function setup(tree) {
|
|
|
82
88
|
childUl.setAttribute("hidden", "");
|
|
83
89
|
node.querySelector(".tree-toggle")?.setAttribute("aria-expanded", "false");
|
|
84
90
|
node.querySelector(".tree-toggle")?.classList.add("is-collapsed");
|
|
91
|
+
if (li) emitIcen(tree, "icen:tree-toggle", { node: li, open: false });
|
|
85
92
|
}
|
|
86
93
|
break;
|
|
87
94
|
}
|
|
88
95
|
}
|
|
89
|
-
}
|
|
96
|
+
};
|
|
97
|
+
tree.addEventListener("click", onClick);
|
|
98
|
+
tree.addEventListener("keydown", onKeyDown);
|
|
99
|
+
return () => {
|
|
100
|
+
tree.removeEventListener("click", onClick);
|
|
101
|
+
tree.removeEventListener("keydown", onKeyDown);
|
|
102
|
+
el.__icenTreeInit = false;
|
|
103
|
+
};
|
|
90
104
|
}
|
|
91
105
|
function initTree(root) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
const cleanups = [];
|
|
107
|
+
if (typeof document !== "undefined") {
|
|
108
|
+
const scope = root ?? document;
|
|
109
|
+
const trees = [];
|
|
110
|
+
if (scope instanceof Element && scope.matches(".tree")) trees.push(scope);
|
|
111
|
+
trees.push(...Array.from(scope.querySelectorAll(".tree")));
|
|
112
|
+
for (const t of trees) {
|
|
113
|
+
const cleanup = setup(t);
|
|
114
|
+
if (cleanup) cleanups.push(cleanup);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return () => {
|
|
118
|
+
for (const fn of cleanups) fn();
|
|
119
|
+
cleanups.length = 0;
|
|
120
|
+
};
|
|
98
121
|
}
|
|
99
122
|
export {
|
|
100
123
|
initTree
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/behaviors/events.ts
|
|
2
|
+
function emitIcen(source, type, detail) {
|
|
3
|
+
return source.dispatchEvent(
|
|
4
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
5
|
+
);
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
// src/behaviors/upload.ts
|
|
2
9
|
function formatSize(bytes) {
|
|
3
10
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -34,9 +41,9 @@ function sameFile(a, b) {
|
|
|
34
41
|
function setup(zone) {
|
|
35
42
|
const el = zone;
|
|
36
43
|
if (el.__icenUploadInit) return;
|
|
37
|
-
el.__icenUploadInit = true;
|
|
38
44
|
const input = zone.querySelector('input[type="file"]');
|
|
39
45
|
if (!input) return;
|
|
46
|
+
el.__icenUploadInit = true;
|
|
40
47
|
const hasList = zone.hasAttribute("data-upload-list");
|
|
41
48
|
const maxSize = Number(zone.getAttribute("data-max-size")) || 0;
|
|
42
49
|
const maxFiles = Number(zone.getAttribute("data-max-files")) || 0;
|
|
@@ -118,7 +125,7 @@ function setup(zone) {
|
|
|
118
125
|
removeBtn.addEventListener("click", (ev) => {
|
|
119
126
|
ev.stopPropagation();
|
|
120
127
|
currentFiles = currentFiles.filter((f) => f !== file);
|
|
121
|
-
zone
|
|
128
|
+
emitIcen(zone, "icen:upload-remove", { file });
|
|
122
129
|
renderList();
|
|
123
130
|
syncInputFiles();
|
|
124
131
|
});
|
|
@@ -126,10 +133,10 @@ function setup(zone) {
|
|
|
126
133
|
list.appendChild(li);
|
|
127
134
|
}
|
|
128
135
|
}
|
|
129
|
-
function
|
|
136
|
+
function setInputFiles(files) {
|
|
130
137
|
try {
|
|
131
138
|
const dt = new DataTransfer();
|
|
132
|
-
for (const f of
|
|
139
|
+
for (const f of files) dt.items.add(f);
|
|
133
140
|
syncing = true;
|
|
134
141
|
input.files = dt.files;
|
|
135
142
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
@@ -138,6 +145,9 @@ function setup(zone) {
|
|
|
138
145
|
syncing = false;
|
|
139
146
|
}
|
|
140
147
|
}
|
|
148
|
+
function syncInputFiles() {
|
|
149
|
+
setInputFiles(currentFiles);
|
|
150
|
+
}
|
|
141
151
|
function validate(file) {
|
|
142
152
|
if (accept && !isAccepted(file, accept)) return "\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301";
|
|
143
153
|
if (maxSize > 0 && file.size > maxSize) return `\u6587\u4EF6\u8D85\u8FC7 ${formatSize(maxSize)}`;
|
|
@@ -148,7 +158,7 @@ function setup(zone) {
|
|
|
148
158
|
for (const file of files) {
|
|
149
159
|
const reason = validate(file);
|
|
150
160
|
if (reason) {
|
|
151
|
-
zone
|
|
161
|
+
emitIcen(zone, "icen:upload-error", { file, reason });
|
|
152
162
|
continue;
|
|
153
163
|
}
|
|
154
164
|
valid.push(file);
|
|
@@ -164,10 +174,7 @@ function setup(zone) {
|
|
|
164
174
|
if (maxFiles > 0) {
|
|
165
175
|
const remaining = maxFiles - currentFiles.length;
|
|
166
176
|
if (remaining <= 0) {
|
|
167
|
-
zone
|
|
168
|
-
bubbles: true,
|
|
169
|
-
detail: { file: null, reason: `\u6700\u591A ${maxFiles} \u4E2A\u6587\u4EF6` }
|
|
170
|
-
}));
|
|
177
|
+
emitIcen(zone, "icen:upload-error", { file: null, reason: `\u6700\u591A ${maxFiles} \u4E2A\u6587\u4EF6` });
|
|
171
178
|
return;
|
|
172
179
|
}
|
|
173
180
|
if (fresh.length > remaining) fresh.length = remaining;
|
|
@@ -175,10 +182,10 @@ function setup(zone) {
|
|
|
175
182
|
currentFiles.push(...fresh);
|
|
176
183
|
renderList();
|
|
177
184
|
syncInputFiles();
|
|
178
|
-
zone
|
|
185
|
+
emitIcen(zone, "icen:upload", { files: fresh });
|
|
179
186
|
return;
|
|
180
187
|
}
|
|
181
|
-
zone
|
|
188
|
+
emitIcen(zone, "icen:upload", { files: valid });
|
|
182
189
|
}
|
|
183
190
|
const openPicker = () => input.click();
|
|
184
191
|
zone.addEventListener("click", (ev) => {
|
|
@@ -189,6 +196,8 @@ function setup(zone) {
|
|
|
189
196
|
});
|
|
190
197
|
zone.addEventListener("keydown", (ev) => {
|
|
191
198
|
if (ev.key === "Enter" || ev.key === " ") {
|
|
199
|
+
const tgt = ev.target;
|
|
200
|
+
if (tgt instanceof Element && tgt.closest(".upload-list")) return;
|
|
192
201
|
ev.preventDefault();
|
|
193
202
|
openPicker();
|
|
194
203
|
}
|
|
@@ -215,18 +224,7 @@ function setup(zone) {
|
|
|
215
224
|
const files = ev.dataTransfer?.files;
|
|
216
225
|
if (!files || files.length === 0) return;
|
|
217
226
|
handleFiles(Array.from(files));
|
|
218
|
-
if (!hasList)
|
|
219
|
-
try {
|
|
220
|
-
const dt = new DataTransfer();
|
|
221
|
-
for (const f of Array.from(files)) dt.items.add(f);
|
|
222
|
-
syncing = true;
|
|
223
|
-
input.files = dt.files;
|
|
224
|
-
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
225
|
-
} catch {
|
|
226
|
-
} finally {
|
|
227
|
-
syncing = false;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
227
|
+
if (!hasList) setInputFiles(Array.from(files));
|
|
230
228
|
});
|
|
231
229
|
}
|
|
232
230
|
function initUpload(root) {
|
package/dist/cli.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 用法:
|
|
8
8
|
* bunx --bun @icen.ai/ui add <slug...> 打印引入行(多个 slug 空格分隔)
|
|
9
9
|
* bunx --bun @icen.ai/ui list 列出全部组件 slug
|
|
10
|
-
* 数据源:同目录 registry.json(构建期由 scripts/slugs.mjs
|
|
10
|
+
* 数据源:同目录 registry.json(构建期由 scripts/build-css.ts 写入,映射数据源为 scripts/slugs.mjs)。
|
|
11
11
|
*/
|
|
12
12
|
import { readFileSync } from 'node:fs';
|
|
13
13
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @icen.ai/ui — kit: ai-tool-call(CSS + behavior 一行入口;构建产物,勿手改)
|
|
2
|
-
import './ai-tool
|
|
2
|
+
import './ai-tool.css';
|
|
@@ -105,38 +105,6 @@
|
|
|
105
105
|
background: var(--token-fill-1);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
/* ── 溢出下拉(behavior 可选:点击 … 展开被折叠的中间路径) ── */
|
|
109
|
-
.breadcrumb-dropdown {
|
|
110
|
-
position: relative;
|
|
111
|
-
}
|
|
112
|
-
.breadcrumb-dropdown-menu {
|
|
113
|
-
position: absolute;
|
|
114
|
-
top: calc(100% + 4px);
|
|
115
|
-
left: 0;
|
|
116
|
-
min-width: 160px;
|
|
117
|
-
padding: 4px;
|
|
118
|
-
background: var(--token-card-strong);
|
|
119
|
-
border: 1px solid var(--token-outline);
|
|
120
|
-
border-radius: var(--radius-md);
|
|
121
|
-
box-shadow: var(--token-shadow-overlay);
|
|
122
|
-
z-index: var(--z-popover);
|
|
123
|
-
animation: icen-pop-in var(--icen-tx-fast);
|
|
124
|
-
}
|
|
125
|
-
.breadcrumb-dropdown-menu[hidden] { display: none; }
|
|
126
|
-
.breadcrumb-dropdown-menu a {
|
|
127
|
-
display: block;
|
|
128
|
-
padding: 4px var(--density-space-sm);
|
|
129
|
-
font-size: var(--density-font-size-sm);
|
|
130
|
-
color: var(--token-text-muted);
|
|
131
|
-
text-decoration: none;
|
|
132
|
-
border-radius: var(--radius-sm);
|
|
133
|
-
transition: background-color var(--icen-tx-fast), color var(--icen-tx-fast);
|
|
134
|
-
}
|
|
135
|
-
.breadcrumb-dropdown-menu a:hover {
|
|
136
|
-
background: var(--token-fill-1);
|
|
137
|
-
color: var(--token-text);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
108
|
/* ── 面包屑分隔符变体 ── */
|
|
141
109
|
.breadcrumb--slash .breadcrumb-sep::after { content: '/'; }
|
|
142
110
|
.breadcrumb--arrow .breadcrumb-sep::after { content: '→'; }
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
font-variant-numeric: tabular-nums;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
/* ──
|
|
81
|
+
/* ── 调色盘:手写 markup 调色盘;behavior 走 charts.ts PALETTE_VARS 内联同值(两处值需人工同步) ── */
|
|
82
82
|
.chart-tone-1 { --chart-tone: var(--token-accent); }
|
|
83
83
|
.chart-tone-2 { --chart-tone: var(--token-accent-cold); }
|
|
84
84
|
.chart-tone-3 { --chart-tone: var(--token-success); }
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 包含:kbd(按键)/ code-inline(行内代码)/ list(分隔列表)/ accordion(手风琴,behaviors/accordion 配套)/
|
|
5
5
|
* timeline(时间线)/ desc(描述列表)/ tree(树形,behaviors/tree 配套)
|
|
6
|
+
* markup API 变体(正文"增强"段,behavior 不产出、由手写 markup 消费):
|
|
7
|
+
* .accordion--cards / .code-inline--success|--warning|--error /
|
|
8
|
+
* .timeline--right|--alternate|--sm / .timeline-extra / .timeline-dot--sm 及 dot 语义色(--error 等)
|
|
6
9
|
* 规格源自 opengal src/presentation/ui/data/exhibit.tsx 与 data/display.tsx。
|
|
7
10
|
*/
|
|
8
11
|
|
|
@@ -531,22 +534,6 @@
|
|
|
531
534
|
background: var(--token-outline-soft);
|
|
532
535
|
}
|
|
533
536
|
|
|
534
|
-
/* 树节点复选框(checkbox tree) */
|
|
535
|
-
.tree-checkbox {
|
|
536
|
-
width: 14px;
|
|
537
|
-
height: 14px;
|
|
538
|
-
flex-shrink: 0;
|
|
539
|
-
margin-right: 2px;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/* 树拖拽视觉态 */
|
|
543
|
-
.tree-node.is-dragging {
|
|
544
|
-
opacity: 0.5;
|
|
545
|
-
}
|
|
546
|
-
.tree-node.is-drop-target {
|
|
547
|
-
box-shadow: inset 0 -2px var(--token-accent);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
537
|
/* 树搜索高亮 */
|
|
551
538
|
.tree-label mark {
|
|
552
539
|
background: color-mix(in srgb, var(--token-warning) 30%, transparent);
|
|
@@ -418,7 +418,7 @@
|
|
|
418
418
|
background: color-mix(in srgb, var(--token-accent-subtle) 82%, var(--token-fill-1));
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
-
/* ── 行状态类(rowClassName
|
|
421
|
+
/* ── 行状态类(rowClassName 产出;--danger/--warning/--success 同族语义行底色) ── */
|
|
422
422
|
.dt-row--danger { background: color-mix(in srgb, var(--token-error) 6%, transparent) !important; }
|
|
423
423
|
.dt-row--danger:hover { background: color-mix(in srgb, var(--token-error) 10%, transparent) !important; }
|
|
424
424
|
.dt-row--warning { background: color-mix(in srgb, var(--token-warning) 6%, transparent) !important; }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 契约:
|
|
5
5
|
* <div class="date-picker" data-date-picker data-date-picker-input="id">
|
|
6
|
-
* <button class="date-picker-trigger" type="button" aria-haspopup="dialog">
|
|
6
|
+
* <button class="date-picker-trigger [--sm|--lg]" type="button" aria-haspopup="dialog">
|
|
7
7
|
* <span class="date-picker-value">2026-07-29</span>
|
|
8
8
|
* <svg class="date-picker-icon">…日历 svg…</svg>
|
|
9
9
|
* </button>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* 完整契约(由 behavior 渲染):
|
|
11
11
|
* <div class="notification-container" role="region" aria-label="通知" data-position="top-right">
|
|
12
|
-
* <div class="notification [notification--success|warning|error
|
|
12
|
+
* <div class="notification [notification--success|warning|error]
|
|
13
13
|
* [.is-priority] [.is-unread] [.is-progress] [.is-clickable] [.is-paused]
|
|
14
14
|
* [.has-avatar]" role="alert|button">
|
|
15
15
|
* <span class="notification-icon">…svg 或 img.notification-avatar…</span>
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
.notification.notification--success { border-left-color: var(--token-success); }
|
|
82
82
|
.notification.notification--warning { border-left-color: var(--token-warning); }
|
|
83
83
|
.notification.notification--error { border-left-color: var(--token-error); }
|
|
84
|
-
|
|
84
|
+
/* info 为基类默认配色(基类左侧边即 --token-info),无修饰类 */
|
|
85
85
|
|
|
86
86
|
/* ── 优先级:高优先级强调(左侧加粗 + 更强阴影)── */
|
|
87
87
|
.notification.is-priority {
|
|
@@ -138,7 +138,6 @@
|
|
|
138
138
|
.notification--success .notification-icon { color: var(--token-success); }
|
|
139
139
|
.notification--warning .notification-icon { color: var(--token-warning); }
|
|
140
140
|
.notification--error .notification-icon { color: var(--token-error); }
|
|
141
|
-
.notification--info .notification-icon { color: var(--token-info); }
|
|
142
141
|
|
|
143
142
|
/* 头像(圆形图) */
|
|
144
143
|
.notification-avatar {
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* <button class="page-btn motion" aria-label="下一页">›</button>
|
|
11
11
|
* </nav>
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* 点击接线由 behaviors/controls.ts 的 initPagination() 提供(.page-btn 激活位移 +
|
|
14
|
+
* icen:page-change,上下页按钮按 aria-label / data-dir 推断方向);总页数与省略号
|
|
15
|
+
* 折叠策略由消费方维护,这里只提供样式契约。
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
.pagination {
|
|
@@ -28,7 +28,15 @@
|
|
|
28
28
|
* …
|
|
29
29
|
* </div>
|
|
30
30
|
*
|
|
31
|
-
* segmented/
|
|
31
|
+
* segmented / stepper 由 behaviors/controls.ts 接线(initSegmented / initStepper,
|
|
32
|
+
* 派 icen:segmented-change / icen:stepper-change);otp 由 behaviors/input.ts 的
|
|
33
|
+
* initInput 接线;toggle-group 为纯 CSS 状态组件(.is-active,使用方自行驱动)。
|
|
34
|
+
*
|
|
35
|
+
* 增强段(正文"增强"段):
|
|
36
|
+
* 变体:segmented--block / segmented--ghost / toggle-group--block / toggle-group--ghost
|
|
37
|
+
* 槽位:stepper-unit(步进器单位后缀)/ otp-separator(OTP 分组分隔符)
|
|
38
|
+
* 状态类(消费方施加):.otp.is-complete(behavior 接线中)/ .otp.is-error /
|
|
39
|
+
* .segmented.is-error / .segmented.is-disabled / .toggle-group.is-error / .toggle-group.is-disabled
|
|
32
40
|
*/
|
|
33
41
|
|
|
34
42
|
/* ── 分段控件(排他单选) ── */
|