@yewdiel21/motion-kit 0.14.0 → 0.15.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 (154) hide show
  1. package/README.md +174 -155
  2. package/bin/motion-kit.mjs +3 -3
  3. package/dist/auto/index.d.ts +6 -3
  4. package/dist/auto/index.js +8 -2
  5. package/dist/auto/index.js.map +1 -1
  6. package/dist/{chunk-EY4BJKC4.js → chunk-6IRJYXJU.js} +2 -2
  7. package/dist/{chunk-CU6SXW3E.js → chunk-A6QQCF6V.js} +1309 -310
  8. package/dist/chunk-A6QQCF6V.js.map +1 -0
  9. package/dist/chunk-JXRS3OEA.js +3638 -0
  10. package/dist/chunk-JXRS3OEA.js.map +1 -0
  11. package/dist/css/components/add-to-cart.css +72 -0
  12. package/dist/css/components/bidirectional-slider.css +168 -0
  13. package/dist/css/components/data-table.css +121 -26
  14. package/dist/css/components/filter-builder.css +10 -15
  15. package/dist/css/components/heatmap-chart.css +391 -0
  16. package/dist/css/components/hover-card.css +8 -2
  17. package/dist/css/components/image-preview.css +90 -2
  18. package/dist/css/components/json-viewer.css +33 -1
  19. package/dist/css/components/nav.css +102 -102
  20. package/dist/css/components/orbit-menu.css +130 -0
  21. package/dist/css/components/pagination.css +10 -5
  22. package/dist/css/components/pie-chart.css +390 -0
  23. package/dist/css/components/pill-button.css +135 -0
  24. package/dist/css/components/radar-chart.css +370 -0
  25. package/dist/css/components/radial-chart.css +398 -0
  26. package/dist/css/components/sankey-chart.css +358 -0
  27. package/dist/css/components/scroll-island.css +111 -0
  28. package/dist/css/components/select.css +136 -0
  29. package/dist/css/components/toolbar.css +122 -0
  30. package/dist/css/components/tree-view.css +79 -0
  31. package/dist/css/components/voice-dots.css +70 -0
  32. package/dist/elements/index.d.ts +180 -7
  33. package/dist/elements/index.js +287 -6
  34. package/dist/elements/index.js.map +1 -1
  35. package/dist/{engine-Cn6IhE3z.d.ts → engine-5zRIssTO.d.ts} +11 -1
  36. package/dist/index.css +1832 -168
  37. package/dist/index.d.ts +4 -4
  38. package/dist/index.js +33 -3
  39. package/dist/index.js.map +1 -1
  40. package/dist/react/index.d.ts +117 -11
  41. package/dist/react/index.js +314 -13
  42. package/dist/react/index.js.map +1 -1
  43. package/dist/{reduced-BBUusld6.d.ts → reduced-6X_bns60.d.ts} +1 -1
  44. package/dist/{reveal-DEvp6U6T.d.ts → reveal-BfyKKsw4.d.ts} +97 -4
  45. package/dist/store-CA2Qcun_.d.ts +453 -0
  46. package/package.json +9 -16
  47. package/registry.json +1096 -948
  48. package/src/auto/auto-init.ts +6 -0
  49. package/src/components/add-to-cart/README.md +34 -0
  50. package/src/components/add-to-cart/add-to-cart.css +71 -0
  51. package/src/components/add-to-cart/element.ts +20 -0
  52. package/src/components/add-to-cart/engine.ts +206 -0
  53. package/src/components/add-to-cart/react.tsx +29 -0
  54. package/src/components/bidirectional-slider/README.md +46 -0
  55. package/src/components/bidirectional-slider/bidirectional-slider.css +167 -0
  56. package/src/components/bidirectional-slider/element.ts +42 -0
  57. package/src/components/bidirectional-slider/engine.ts +289 -0
  58. package/src/components/bidirectional-slider/react.tsx +45 -0
  59. package/src/components/chart/README.md +10 -0
  60. package/src/components/chart/element.ts +2 -0
  61. package/src/components/chart/engine.ts +50 -23
  62. package/src/components/chart/parts.ts +237 -0
  63. package/src/components/chart/react.tsx +2 -2
  64. package/src/components/chart/use-chart.ts +55 -0
  65. package/src/components/data-table/README.md +3 -3
  66. package/src/components/data-table/data-table.css +121 -26
  67. package/src/components/data-table/engine.ts +81 -29
  68. package/src/components/editable-table/engine.ts +67 -11
  69. package/src/components/filter-builder/engine.ts +33 -2
  70. package/src/components/filter-builder/filter-builder.css +10 -15
  71. package/src/components/heatmap-chart/README.md +48 -0
  72. package/src/components/heatmap-chart/element.ts +21 -0
  73. package/src/components/heatmap-chart/engine.ts +281 -0
  74. package/src/components/heatmap-chart/heatmap-chart.css +93 -0
  75. package/src/components/heatmap-chart/react.tsx +14 -0
  76. package/src/components/hover-card/engine.ts +30 -1
  77. package/src/components/hover-card/hover-card.css +8 -2
  78. package/src/components/image-preview/element.ts +1 -1
  79. package/src/components/image-preview/engine.ts +95 -15
  80. package/src/components/image-preview/image-preview.css +90 -2
  81. package/src/components/image-preview/react.tsx +3 -3
  82. package/src/components/json-viewer/engine.ts +11 -2
  83. package/src/components/json-viewer/json-viewer.css +33 -1
  84. package/src/components/nav/README.md +12 -10
  85. package/src/components/nav/element.ts +5 -5
  86. package/src/components/nav/engine.ts +21 -14
  87. package/src/components/nav/nav.css +102 -102
  88. package/src/components/nav/react.tsx +4 -4
  89. package/src/components/orbit-menu/README.md +46 -0
  90. package/src/components/orbit-menu/element.ts +27 -0
  91. package/src/components/orbit-menu/engine.ts +179 -0
  92. package/src/components/orbit-menu/orbit-menu.css +129 -0
  93. package/src/components/orbit-menu/react.tsx +44 -0
  94. package/src/components/pagination/README.md +1 -1
  95. package/src/components/pagination/engine.ts +50 -4
  96. package/src/components/pagination/pagination.css +10 -5
  97. package/src/components/pie-chart/README.md +48 -0
  98. package/src/components/pie-chart/element.ts +30 -0
  99. package/src/components/pie-chart/engine.ts +283 -0
  100. package/src/components/pie-chart/pie-chart.css +92 -0
  101. package/src/components/pie-chart/react.tsx +14 -0
  102. package/src/components/pill-button/README.md +46 -0
  103. package/src/components/pill-button/element.ts +16 -0
  104. package/src/components/pill-button/engine.ts +77 -0
  105. package/src/components/pill-button/pill-button.css +134 -0
  106. package/src/components/pill-button/react.tsx +32 -0
  107. package/src/components/radar-chart/README.md +48 -0
  108. package/src/components/radar-chart/element.ts +30 -0
  109. package/src/components/radar-chart/engine.ts +327 -0
  110. package/src/components/radar-chart/radar-chart.css +72 -0
  111. package/src/components/radar-chart/react.tsx +14 -0
  112. package/src/components/radial-chart/README.md +48 -0
  113. package/src/components/radial-chart/element.ts +30 -0
  114. package/src/components/radial-chart/engine.ts +290 -0
  115. package/src/components/radial-chart/radial-chart.css +100 -0
  116. package/src/components/radial-chart/react.tsx +14 -0
  117. package/src/components/sankey-chart/README.md +48 -0
  118. package/src/components/sankey-chart/element.ts +31 -0
  119. package/src/components/sankey-chart/engine.ts +401 -0
  120. package/src/components/sankey-chart/react.tsx +14 -0
  121. package/src/components/sankey-chart/sankey-chart.css +60 -0
  122. package/src/components/scroll-island/README.md +34 -0
  123. package/src/components/scroll-island/element.ts +16 -0
  124. package/src/components/scroll-island/engine.ts +252 -0
  125. package/src/components/scroll-island/react.tsx +26 -0
  126. package/src/components/scroll-island/scroll-island.css +110 -0
  127. package/src/components/select/README.md +34 -0
  128. package/src/components/select/element.ts +26 -0
  129. package/src/components/select/engine.ts +267 -0
  130. package/src/components/select/react.tsx +33 -0
  131. package/src/components/select/select.css +135 -0
  132. package/src/components/toolbar/README.md +46 -0
  133. package/src/components/toolbar/element.ts +30 -0
  134. package/src/components/toolbar/engine.ts +230 -0
  135. package/src/components/toolbar/react.tsx +34 -0
  136. package/src/components/toolbar/toolbar.css +121 -0
  137. package/src/components/tree-view/element.ts +1 -1
  138. package/src/components/tree-view/engine.ts +174 -0
  139. package/src/components/tree-view/react.tsx +7 -3
  140. package/src/components/tree-view/tree-view.css +79 -0
  141. package/src/components/voice-dots/README.md +48 -0
  142. package/src/components/voice-dots/element.ts +45 -0
  143. package/src/components/voice-dots/engine.ts +243 -0
  144. package/src/components/voice-dots/react.tsx +52 -0
  145. package/src/components/voice-dots/voice-dots.css +69 -0
  146. package/src/elements/index.ts +39 -0
  147. package/src/index.ts +16 -0
  148. package/src/motion/liquid.ts +14 -4
  149. package/src/react/index.ts +14 -1
  150. package/dist/chunk-6P4P7NU6.js +0 -1401
  151. package/dist/chunk-6P4P7NU6.js.map +0 -1
  152. package/dist/chunk-CU6SXW3E.js.map +0 -1
  153. package/dist/store-DjkTBX7f.d.ts +0 -195
  154. /package/dist/{chunk-EY4BJKC4.js.map → chunk-6IRJYXJU.js.map} +0 -0
@@ -1,1401 +0,0 @@
1
- import {
2
- EASE,
3
- Flip,
4
- bezier,
5
- channels,
6
- easeInCubic,
7
- easeOut,
8
- easeOutCubic,
9
- eased,
10
- flowIn,
11
- flowInPalette,
12
- gsap,
13
- motionSpring,
14
- registerMotion,
15
- revealFrom,
16
- splitGraphemes,
17
- splitWords,
18
- spring,
19
- springEase
20
- } from "./chunk-CU6SXW3E.js";
21
- import {
22
- THEMES,
23
- THEME_EVENT,
24
- getTheme,
25
- setTheme
26
- } from "./chunk-RVSKEBIC.js";
27
- import {
28
- TIMING,
29
- createScope,
30
- entrances,
31
- reduced,
32
- subscribe
33
- } from "./chunk-DOOMULVV.js";
34
- import {
35
- inert,
36
- warnOnce
37
- } from "./chunk-TGV27ZWM.js";
38
-
39
- // src/motion/chat.ts
40
- var isUser = (bubble) => bubble.dataset.mkRole === "user" || bubble.classList.contains("user");
41
- function enterMessages(bubbles, { textSelector = "[data-mk-text], .message-text" } = {}) {
42
- if (!bubbles.length || reduced()) return null;
43
- registerMotion();
44
- const reverts = [];
45
- const cleanup = () => {
46
- for (const revert of reverts.splice(0)) revert();
47
- };
48
- const timeline = gsap.timeline({ onComplete: cleanup, onInterrupt: cleanup });
49
- bubbles.forEach((bubble, index) => {
50
- gsap.set(bubble, { transformOrigin: isUser(bubble) ? "100% 50%" : "0% 50%" });
51
- timeline.fromTo(
52
- bubble,
53
- { scale: 0.5, autoAlpha: 0, filter: "blur(24px)" },
54
- { scale: 1, autoAlpha: 1, filter: "blur(0px)", duration: TIMING.bubble, ease: EASE.signature, clearProps: "transform,opacity,visibility,filter" },
55
- index * TIMING.bubbleStagger
56
- );
57
- const text = bubble.querySelector(textSelector);
58
- if (!text) return;
59
- const before = text.offsetHeight;
60
- const split = splitWords(text);
61
- const lineHeight = parseFloat(getComputedStyle(text).lineHeight) || 0;
62
- let targets = split.parts;
63
- if (!split.parts.length || text.offsetHeight - before > lineHeight / 2) {
64
- split.revert();
65
- targets = [text];
66
- } else reverts.push(() => split.revert());
67
- timeline.fromTo(
68
- targets,
69
- { y: 16, autoAlpha: 0, filter: "blur(6px)" },
70
- {
71
- y: 0,
72
- autoAlpha: 1,
73
- filter: "blur(0px)",
74
- duration: TIMING.word,
75
- ease: "back.out(1)",
76
- stagger: TIMING.wordStagger,
77
- clearProps: "transform,opacity,visibility,filter"
78
- },
79
- index * TIMING.bubbleStagger
80
- );
81
- });
82
- return timeline;
83
- }
84
- function fitComposer(field, min = 64) {
85
- const from = field.offsetHeight;
86
- field.style.height = "auto";
87
- const to = Math.max(min, field.scrollHeight);
88
- field.style.height = `${from}px`;
89
- if (from === to || reduced()) {
90
- field.style.height = `${to}px`;
91
- return null;
92
- }
93
- registerMotion();
94
- return gsap.to(field, { height: to, duration: TIMING.composer, ease: "power2.out", overwrite: "auto" });
95
- }
96
- function scrollToLatest(scroller) {
97
- const top = scroller.scrollHeight - scroller.clientHeight;
98
- if (reduced()) {
99
- scroller.scrollTop = top;
100
- return null;
101
- }
102
- registerMotion();
103
- const state = { top: scroller.scrollTop };
104
- return gsap.to(state, {
105
- top,
106
- duration: TIMING.chatScroll,
107
- ease: EASE.chatScroll,
108
- overwrite: "auto",
109
- onUpdate: () => {
110
- scroller.scrollTop = state.top;
111
- }
112
- });
113
- }
114
-
115
- // src/components/theme-picker/engine.ts
116
- function createThemePicker(root, options = {}) {
117
- if (!root) {
118
- warnOnce("theme-picker: missing root");
119
- return inert({ select() {
120
- } });
121
- }
122
- registerMotion();
123
- const scope = createScope();
124
- const ctx = gsap.context(() => {
125
- });
126
- const generated = [];
127
- let swatches = [...root.querySelectorAll("[data-mk-swatch]")];
128
- if (!swatches.length) {
129
- const byId = new Map(THEMES.map((theme) => [theme.id, theme]));
130
- const ids = options.themes ?? THEMES.filter((theme) => options.showHidden || !theme.hidden).map((theme) => theme.id);
131
- for (const id of ids) {
132
- const theme = byId.get(id);
133
- if (!theme) {
134
- warnOnce(`theme-picker: unknown theme "${id}"`);
135
- continue;
136
- }
137
- const button = document.createElement("button");
138
- button.type = "button";
139
- button.className = "mk-swatch";
140
- button.dataset.mkSwatch = theme.id;
141
- button.title = theme.name;
142
- button.setAttribute("aria-label", theme.name);
143
- button.style.setProperty("--mk-swatch", theme.seed);
144
- root.append(button);
145
- generated.push(button);
146
- }
147
- swatches = generated;
148
- }
149
- scope.attr(root, "role", "group");
150
- if (!root.hasAttribute("aria-label")) scope.attr(root, "aria-label", options.labels?.group ?? "Tema");
151
- const sync = () => {
152
- const current = getTheme().id;
153
- for (const swatch of swatches) scope.attr(swatch, "aria-pressed", String(swatch.dataset.mkSwatch === current));
154
- };
155
- const select = (id) => {
156
- setTheme(id);
157
- options.onChange?.(id);
158
- };
159
- scope.on(root, "click", (event) => {
160
- const swatch = event.target?.closest("[data-mk-swatch]");
161
- if (swatch && root.contains(swatch) && swatch.dataset.mkSwatch) select(swatch.dataset.mkSwatch);
162
- });
163
- scope.on(document, THEME_EVENT, sync);
164
- sync();
165
- if (entrances()) ctx.add(() => flowInPalette(swatches));
166
- root.dataset.mkReady = "";
167
- return {
168
- select,
169
- destroy() {
170
- scope.dispose();
171
- ctx.revert();
172
- for (const button of generated.splice(0)) button.remove();
173
- delete root.dataset.mkReady;
174
- }
175
- };
176
- }
177
-
178
- // src/components/toast/store.ts
179
- var TOAST_LIMIT = 5;
180
- var items = [];
181
- var nextId = 1;
182
- var listeners = /* @__PURE__ */ new Set();
183
- var emit = () => {
184
- for (const listener of listeners) listener();
185
- };
186
- var getToasts = () => items;
187
- function subscribeToasts(listener) {
188
- listeners.add(listener);
189
- return () => {
190
- listeners.delete(listener);
191
- };
192
- }
193
- function show(message, { origin = null, duration = 4e3, tone = "success", action, icon = true } = {}) {
194
- const id = nextId++;
195
- const live = items.filter((item) => !item.closing);
196
- const extra = new Set(live.slice(0, Math.max(0, live.length - TOAST_LIMIT + 1)).map((item) => item.id));
197
- items = [
198
- ...items.map((item) => extra.has(item.id) ? { ...item, closing: true } : item),
199
- { id, message, origin, duration, tone, action: action ?? null, icon, closing: false }
200
- ];
201
- emit();
202
- return id;
203
- }
204
- function dismissToast(id) {
205
- const item = items.find((entry) => entry.id === id);
206
- if (!item || item.closing) return;
207
- items = items.map((entry) => entry.id === id ? { ...entry, closing: true } : entry);
208
- emit();
209
- }
210
- function removeToast(id) {
211
- toastTimers.clear(id);
212
- items = items.filter((entry) => entry.id !== id);
213
- emit();
214
- }
215
- function clearToasts() {
216
- for (const item of items) toastTimers.clear(item.id);
217
- items = [];
218
- emit();
219
- }
220
- var toast = Object.assign(show, { dismiss: dismissToast, clear: clearToasts });
221
- var running = /* @__PURE__ */ new Map();
222
- var toastTimers = {
223
- start(id, duration) {
224
- if (duration <= 0 || running.has(id)) return;
225
- running.set(id, { handle: setTimeout(() => dismissToast(id), duration), remaining: duration, started: Date.now() });
226
- },
227
- pause(id) {
228
- const timer = running.get(id);
229
- if (!timer?.handle) return;
230
- clearTimeout(timer.handle);
231
- timer.remaining = Math.max(0, timer.remaining - (Date.now() - timer.started));
232
- timer.handle = null;
233
- },
234
- resume(id) {
235
- const timer = running.get(id);
236
- if (!timer || timer.handle) return;
237
- if (timer.remaining <= 0) {
238
- dismissToast(id);
239
- return;
240
- }
241
- timer.started = Date.now();
242
- timer.handle = setTimeout(() => dismissToast(id), timer.remaining);
243
- },
244
- clear(id) {
245
- const timer = running.get(id);
246
- if (timer?.handle) clearTimeout(timer.handle);
247
- running.delete(id);
248
- }
249
- };
250
-
251
- // src/components/icons.ts
252
- var ICON_PATHS = {
253
- close: "M6 6l12 12M18 6L6 18",
254
- expand: "M14 4h6v6M10 20H4v-6M20 4l-7 7M4 20l7-7",
255
- shrink: "M4 14h6v6M20 10h-6V4M10 14l-7 7M14 10l7-7",
256
- check: "M5 12.5l4.5 4.5L19 7.5",
257
- alert: "M12 8v5M12 16.5v.01M10.3 3.9L2.6 17.3a2 2 0 001.7 3h15.4a2 2 0 001.7-3L13.7 3.9a2 2 0 00-3.4 0z",
258
- info: "M12 11v6M12 7.5v.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
259
- send: "M12 19V5M5.5 11.5L12 5l6.5 6.5",
260
- mic: "M12 3a3 3 0 00-3 3v6a3 3 0 006 0V6a3 3 0 00-3-3zM5.5 11a6.5 6.5 0 0013 0M12 17.5V21",
261
- sparkle: "M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z",
262
- grip: "M9 6h.01M15 6h.01M9 12h.01M15 12h.01M9 18h.01M15 18h.01"
263
- };
264
- function iconSvg(name, size = 20) {
265
- return `<svg aria-hidden="true" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${ICON_PATHS[name]}"/></svg>`;
266
- }
267
-
268
- // src/components/toast/engine.ts
269
- var STACK = { peek: 16, scaleStep: 0.05, minScale: 0.7, maxVisible: 3, gap: 8, baseZ: 1e3 };
270
- var DISTANCE = { enter: 12, enterBlur: 6, exit: 24, exitBlur: 4, fade: 120 };
271
- var GESTURE = { swipe: 48, wheel: 80, expandDrag: 24, resistance: 0.15, wheelEndMs: 140 };
272
- var TONE_ICONS = { success: "check", error: "alert", info: "info", neutral: null };
273
- function layout(count, expanded, heights) {
274
- let offset = 0;
275
- return Array.from({ length: count }, (_, index) => {
276
- if (expanded) {
277
- const slot = { y: offset, scale: 1, opacity: 1, visible: true, zIndex: STACK.baseZ - index };
278
- offset += (heights[index] ?? 0) + STACK.gap;
279
- return slot;
280
- }
281
- const visible = index < STACK.maxVisible;
282
- return {
283
- y: index * STACK.peek,
284
- scale: Math.max(STACK.minScale, 1 - index * STACK.scaleStep),
285
- opacity: visible ? 1 : 0,
286
- visible,
287
- zIndex: STACK.baseZ - index
288
- };
289
- });
290
- }
291
- function revealLetters(element) {
292
- const text = element.textContent ?? "";
293
- const T = TIMING.toast;
294
- if (!text || text.length > 64 || reduced()) return;
295
- const letters = splitGraphemes(text, 64);
296
- const original = element.innerHTML;
297
- const width = element.getBoundingClientRect().width;
298
- const fragment = document.createDocumentFragment();
299
- const chars = [];
300
- let word = null;
301
- for (const letter of letters) {
302
- if (!letter.trim()) {
303
- word = null;
304
- fragment.append(document.createTextNode(letter));
305
- continue;
306
- }
307
- if (!word) {
308
- word = document.createElement("span");
309
- word.style.display = "inline-block";
310
- word.style.whiteSpace = "nowrap";
311
- fragment.append(word);
312
- }
313
- const char = document.createElement("span");
314
- char.textContent = letter;
315
- char.style.display = "inline-block";
316
- word.append(char);
317
- chars.push(char);
318
- }
319
- if (width) element.style.width = `${width}px`;
320
- element.textContent = "";
321
- element.append(fragment);
322
- gsap.fromTo(
323
- chars,
324
- { opacity: 0, yPercent: 40, filter: "blur(4px)" },
325
- {
326
- opacity: 1,
327
- yPercent: 0,
328
- filter: "blur(0px)",
329
- duration: T.chars,
330
- ease: easeOutCubic,
331
- delay: (index) => T.charsDelay + index * T.charsStagger,
332
- onComplete: () => {
333
- element.innerHTML = original;
334
- element.style.width = "";
335
- }
336
- }
337
- );
338
- }
339
- function morphFrom(card, shell, body, origin) {
340
- const T = TIMING.toast;
341
- const box = shell.getBoundingClientRect();
342
- const start = origin.getBoundingClientRect();
343
- const look = getComputedStyle(origin);
344
- const own = getComputedStyle(shell);
345
- const endRadius = parseFloat(own.borderTopLeftRadius) || 20;
346
- const startRadius = Math.min(parseFloat(look.borderTopLeftRadius) || 0, start.height / 2);
347
- card.style.width = `${box.width}px`;
348
- card.style.height = `${box.height}px`;
349
- body.style.width = `${body.getBoundingClientRect().width}px`;
350
- const towardX = start.left + start.width / 2 < innerWidth / 2 ? 1 : -1;
351
- const towardY = start.top + start.height / 2 < innerHeight / 2 ? 1 : -1;
352
- const offset = { x: start.left - box.left, y: start.top - box.top };
353
- Object.assign(shell.style, {
354
- position: "absolute",
355
- top: "0px",
356
- left: "0px",
357
- width: `${start.width}px`,
358
- height: `${start.height}px`,
359
- borderRadius: `${startRadius}px`,
360
- background: look.backgroundColor,
361
- transform: `translate(${offset.x}px, ${offset.y}px)`,
362
- transition: "none"
363
- });
364
- Object.assign(body.style, { transform: "scale(2)", transformOrigin: "center center", filter: "blur(8px)", opacity: "0" });
365
- origin.style.transition = "opacity 160ms ease";
366
- origin.style.opacity = "0";
367
- void shell.offsetWidth;
368
- shell.style.transition = `background ${T.morphColor}s ease-out ${T.morphColorDelay}s, box-shadow ${T.morphShadow}s ease-out ${T.morphShadowDelay}s`;
369
- shell.style.background = "";
370
- body.style.transition = `opacity ${T.morphColor}s ease-out ${T.morphColorDelay}s`;
371
- body.style.opacity = "1";
372
- const motion = channels(
373
- { x: offset.x, y: offset.y, width: start.width, height: start.height, radius: startRadius, scale: 2, blur: 8 },
374
- (name, value) => {
375
- if (name === "x") offset.x = value;
376
- else if (name === "y") offset.y = value;
377
- else if (name === "width") shell.style.width = `${value}px`;
378
- else if (name === "height") shell.style.height = `${value}px`;
379
- else if (name === "radius") shell.style.borderRadius = `${value}px`;
380
- else if (name === "scale") body.style.transform = `scale(${value})`;
381
- else if (name === "blur") body.style.filter = `blur(${value}px)`;
382
- if (name === "x" || name === "y") shell.style.transform = `translate(${offset.x}px, ${offset.y}px)`;
383
- }
384
- );
385
- void motion.animate(
386
- { x: 0, y: 0, width: box.width, height: box.height, radius: endRadius, scale: 1, blur: 0 },
387
- {
388
- x: spring(144, 14, { velocity: 2400, direction: towardX }),
389
- y: spring(144, 14, { velocity: 2400, direction: towardY }),
390
- width: eased(T.morphSize, bezier(0.8, 0.3, 0.5, 0.8)),
391
- height: eased(T.morphSize, bezier(0.8, 0.3, 0.5, 0.8)),
392
- radius: eased(T.morphRadius, easeOut),
393
- scale: eased(T.morphContent, easeOut),
394
- blur: eased(T.morphContent, easeOut)
395
- }
396
- );
397
- let settled = false;
398
- const settle = () => {
399
- if (settled) return;
400
- settled = true;
401
- clearTimeout(timer);
402
- motion.stop();
403
- for (const name of ["position", "top", "left", "width", "height", "border-radius", "background", "transform", "transition"]) shell.style.removeProperty(name);
404
- card.style.removeProperty("width");
405
- card.style.removeProperty("height");
406
- for (const name of ["transform", "transform-origin", "filter", "opacity", "width", "transition"]) body.style.removeProperty(name);
407
- origin.style.transition = "opacity 300ms ease";
408
- origin.style.opacity = "";
409
- setTimeout(() => {
410
- origin.style.transition = "";
411
- }, 320);
412
- };
413
- const timer = setTimeout(settle, T.safetyMs);
414
- return settle;
415
- }
416
- function buildCard(item, dismissLabel) {
417
- const card = document.createElement("div");
418
- card.className = "mk-toast";
419
- card.dataset.mkToast = String(item.id);
420
- card.dataset.mkTone = item.tone;
421
- const shell = document.createElement("div");
422
- shell.className = "mk-toast__shell";
423
- const body = document.createElement("div");
424
- body.className = "mk-toast__body";
425
- const icon = item.icon ? TONE_ICONS[item.tone] : null;
426
- if (icon) {
427
- const holder = document.createElement("span");
428
- holder.className = "mk-toast__icon";
429
- holder.innerHTML = iconSvg(icon, 18);
430
- body.append(holder);
431
- }
432
- const message = document.createElement("span");
433
- message.className = "mk-toast__message";
434
- message.textContent = item.message;
435
- body.append(message);
436
- if (item.action) {
437
- const action = document.createElement("button");
438
- action.type = "button";
439
- action.className = "mk-toast__action";
440
- action.dataset.mkToastAction = "";
441
- action.textContent = item.action.label;
442
- body.append(action);
443
- }
444
- const dismiss = document.createElement("button");
445
- dismiss.type = "button";
446
- dismiss.className = "mk-toast__dismiss";
447
- dismiss.dataset.mkToastDismiss = "";
448
- dismiss.setAttribute("aria-label", dismissLabel);
449
- dismiss.innerHTML = iconSvg("close", 14);
450
- body.append(dismiss);
451
- shell.append(body);
452
- card.append(shell);
453
- return { card, shell, body, message };
454
- }
455
- function createToaster(host, options = {}) {
456
- if (!host) {
457
- warnOnce("toaster: missing host");
458
- return inert();
459
- }
460
- registerMotion();
461
- const dismissLabel = options.labels?.dismiss ?? "Cerrar";
462
- const scope = createScope();
463
- const cards = /* @__PURE__ */ new Map();
464
- const entered = /* @__PURE__ */ new Set();
465
- const leaving = /* @__PURE__ */ new Set();
466
- const dragging = /* @__PURE__ */ new Set();
467
- const settles = /* @__PURE__ */ new Map();
468
- let expanded = false;
469
- let collapse;
470
- const popover = typeof host.showPopover === "function";
471
- scope.attr(host, "role", "status");
472
- scope.attr(host, "aria-live", "polite");
473
- if (popover) scope.attr(host, "popover", "manual");
474
- const live = () => getToasts().filter((item) => !item.closing).reverse().map((item) => ({ item, entry: cards.get(item.id) })).filter((pair) => pair.entry !== void 0);
475
- const slotsFor = () => {
476
- const entries = live();
477
- return { entries, slots: layout(entries.length, expanded, entries.map(({ entry }) => entry.card.offsetHeight)) };
478
- };
479
- const relayout = (duration = TIMING.toast.relayout, ease = easeOutCubic, stagger = 0) => {
480
- const { entries, slots } = slotsFor();
481
- entries.forEach(({ item, entry }, index) => {
482
- const slot = slots[index];
483
- if (!slot) return;
484
- entry.card.style.zIndex = String(slot.zIndex);
485
- entry.card.style.pointerEvents = slot.visible ? "auto" : "none";
486
- entry.card.setAttribute("aria-hidden", slot.visible ? "false" : "true");
487
- if (dragging.has(item.id) || !entered.has(item.id)) return;
488
- gsap.to(entry.card, {
489
- xPercent: -50,
490
- y: slot.y,
491
- scale: slot.scale,
492
- opacity: slot.opacity,
493
- duration: reduced() ? 0 : duration,
494
- ease,
495
- delay: stagger * index,
496
- overwrite: "auto"
497
- });
498
- });
499
- };
500
- const restingY = (id) => {
501
- const { entries, slots } = slotsFor();
502
- return slots[entries.findIndex(({ item }) => item.id === id)]?.y ?? 0;
503
- };
504
- const openDeck = () => {
505
- clearTimeout(collapse);
506
- if (expanded) return;
507
- expanded = true;
508
- for (const { item } of live()) toastTimers.pause(item.id);
509
- relayout(TIMING.toast.expand, springEase(0.55), TIMING.toast.expandStagger);
510
- };
511
- const closeDeck = () => {
512
- clearTimeout(collapse);
513
- collapse = setTimeout(() => {
514
- if (!expanded) return;
515
- expanded = false;
516
- for (const { item } of live()) toastTimers.resume(item.id);
517
- relayout(TIMING.toast.expand, springEase(0.55), TIMING.toast.expandStagger);
518
- }, TIMING.toast.collapseDelayMs);
519
- };
520
- const showLayer = (visible) => {
521
- if (!popover) return;
522
- try {
523
- if (host.matches(":popover-open")) host.hidePopover();
524
- if (visible) host.showPopover();
525
- } catch {
526
- }
527
- };
528
- const attachGestures = (item, card) => {
529
- let start = null;
530
- let wheel = null;
531
- const move = (offset) => gsap.set(card, { y: restingY(item.id) + offset, opacity: Math.max(0, 1 + Math.min(0, offset) / DISTANCE.fade) });
532
- const cancel = () => {
533
- dragging.delete(item.id);
534
- gsap.to(card, { y: restingY(item.id), opacity: 1, duration: 0.25, ease: easeOutCubic });
535
- if (!expanded) toastTimers.resume(item.id);
536
- };
537
- card.addEventListener("mouseenter", openDeck);
538
- card.addEventListener("mouseleave", closeDeck);
539
- card.addEventListener("pointerdown", (event) => {
540
- if (event.button !== 0 || event.target.closest('button, a, input, textarea, select, [role="button"]')) return;
541
- start = { y: event.clientY, type: event.pointerType };
542
- card.setPointerCapture?.(event.pointerId);
543
- dragging.add(item.id);
544
- gsap.killTweensOf(card);
545
- toastTimers.pause(item.id);
546
- });
547
- card.addEventListener("pointermove", (event) => {
548
- if (!start) return;
549
- const dy = event.clientY - start.y;
550
- move(dy < 0 ? dy : start.type === "touch" ? dy * GESTURE.resistance : 0);
551
- });
552
- const up = (event) => {
553
- if (!start) return;
554
- const dy = event.clientY - start.y;
555
- const type = start.type;
556
- start = null;
557
- if (dy < -GESTURE.swipe) {
558
- dragging.delete(item.id);
559
- dismissToast(item.id);
560
- } else if (type === "touch" && dy > GESTURE.expandDrag) {
561
- cancel();
562
- if (expanded) closeDeck();
563
- else openDeck();
564
- } else cancel();
565
- };
566
- card.addEventListener("pointerup", up);
567
- card.addEventListener("pointercancel", up);
568
- card.addEventListener(
569
- "wheel",
570
- (event) => {
571
- const delta = event.deltaMode === 1 ? event.deltaY * 16 : event.deltaMode === 2 ? event.deltaY * innerHeight : event.deltaY;
572
- if (!wheel) {
573
- wheel = { offset: 0, end: void 0 };
574
- dragging.add(item.id);
575
- toastTimers.pause(item.id);
576
- }
577
- wheel.offset = Math.min(0, wheel.offset - delta);
578
- move(wheel.offset);
579
- clearTimeout(wheel.end);
580
- wheel.end = setTimeout(() => {
581
- const offset = wheel?.offset ?? 0;
582
- wheel = null;
583
- if (offset < -GESTURE.wheel) {
584
- dragging.delete(item.id);
585
- dismissToast(item.id);
586
- } else cancel();
587
- }, GESTURE.wheelEndMs);
588
- },
589
- { passive: true }
590
- );
591
- card.addEventListener("click", (event) => {
592
- const target = event.target;
593
- if (target.closest("[data-mk-toast-dismiss]")) dismissToast(item.id);
594
- else if (target.closest("[data-mk-toast-action]")) {
595
- item.action?.onClick();
596
- dismissToast(item.id);
597
- }
598
- });
599
- };
600
- const finishCard = (id) => {
601
- settles.get(id)?.();
602
- settles.delete(id);
603
- const entry = cards.get(id);
604
- if (entry) {
605
- gsap.killTweensOf(entry.card);
606
- entry.card.remove();
607
- }
608
- cards.delete(id);
609
- entered.delete(id);
610
- leaving.delete(id);
611
- dragging.delete(id);
612
- };
613
- const render = () => {
614
- const list = getToasts();
615
- const ids = new Set(list.map((item) => item.id));
616
- for (const id of [...cards.keys()]) if (!ids.has(id)) finishCard(id);
617
- for (const item of list) {
618
- if (!cards.has(item.id) && !item.closing) {
619
- const entry = buildCard(item, dismissLabel);
620
- cards.set(item.id, entry);
621
- host.append(entry.card);
622
- gsap.set(entry.card, { xPercent: -50 });
623
- attachGestures(item, entry.card);
624
- }
625
- }
626
- showLayer(list.some((item) => !item.closing) || leaving.size > 0);
627
- for (const item of list) {
628
- const entry = cards.get(item.id);
629
- if (!entry) continue;
630
- if (!entered.has(item.id) && !item.closing) {
631
- entered.add(item.id);
632
- toastTimers.start(item.id, item.duration);
633
- relayout();
634
- gsap.killTweensOf(entry.card);
635
- gsap.set(entry.card, { xPercent: -50, y: restingY(item.id), scale: 1, opacity: 1 });
636
- if (expanded) toastTimers.pause(item.id);
637
- revealLetters(entry.message);
638
- if (reduced()) continue;
639
- if (item.origin?.isConnected) settles.set(item.id, morphFrom(entry.card, entry.shell, entry.body, item.origin));
640
- else {
641
- const y = restingY(item.id);
642
- gsap.fromTo(
643
- entry.card,
644
- { y: y - DISTANCE.enter, opacity: 0, filter: `blur(${DISTANCE.enterBlur}px)` },
645
- { y, opacity: 1, filter: "blur(0px)", duration: TIMING.toast.enter, ease: springEase(0.58, -3.5), clearProps: "filter" }
646
- );
647
- }
648
- }
649
- if (item.closing && !leaving.has(item.id)) {
650
- leaving.add(item.id);
651
- toastTimers.clear(item.id);
652
- entry.card.style.zIndex = String(STACK.baseZ + 1);
653
- const done = () => {
654
- finishCard(item.id);
655
- removeToast(item.id);
656
- };
657
- if (reduced()) done();
658
- else {
659
- gsap.to(entry.card, {
660
- y: Number(gsap.getProperty(entry.card, "y")) - DISTANCE.exit,
661
- opacity: 0,
662
- filter: `blur(${DISTANCE.exitBlur}px)`,
663
- duration: TIMING.toast.exit,
664
- ease: easeInCubic,
665
- overwrite: "auto",
666
- onComplete: done
667
- });
668
- }
669
- relayout();
670
- }
671
- }
672
- };
673
- scope.add(subscribeToasts(render));
674
- render();
675
- host.dataset.mkReady = "";
676
- return {
677
- destroy() {
678
- clearTimeout(collapse);
679
- for (const id of [...cards.keys()]) finishCard(id);
680
- showLayer(false);
681
- scope.dispose();
682
- delete host.dataset.mkReady;
683
- }
684
- };
685
- }
686
-
687
- // src/components/chat/engine.ts
688
- var MESSAGE = "[data-mk-message]";
689
- var PENDING_ID = "mk-chat-pending";
690
- var part = (root, name) => root.querySelector(`[data-mk-part="${name}"]`);
691
- var capturePending = (placeholder) => {
692
- registerMotion();
693
- return placeholder ? Flip.getState(placeholder) : null;
694
- };
695
- function morphPendingToReply(state, reply, scroller) {
696
- registerMotion();
697
- if (!state || reduced()) {
698
- if (!reduced()) enterMessages([reply]);
699
- return null;
700
- }
701
- const text = reply.querySelector("[data-mk-text], .message-text");
702
- if (text) text.style.width = `${text.getBoundingClientRect().width}px`;
703
- reply.setAttribute("data-flip-id", PENDING_ID);
704
- const words = text && !text.querySelector("table, pre") ? splitWords(text) : null;
705
- if (words?.parts.length) {
706
- gsap.fromTo(
707
- words.parts,
708
- { y: 16, autoAlpha: 0, filter: "blur(6px)" },
709
- {
710
- y: 0,
711
- autoAlpha: 1,
712
- filter: "blur(0px)",
713
- duration: TIMING.word,
714
- ease: "back.out(1)",
715
- stagger: TIMING.wordStagger,
716
- clearProps: "transform,opacity,visibility,filter",
717
- onComplete: () => words.revert()
718
- }
719
- );
720
- }
721
- return Flip.from(state, {
722
- targets: reply,
723
- duration: TIMING.bubble,
724
- scale: false,
725
- ease: EASE.signature,
726
- onUpdate: () => {
727
- if (scroller) scroller.scrollTop = scroller.scrollHeight;
728
- },
729
- onComplete: () => {
730
- reply.removeAttribute("data-flip-id");
731
- if (text) text.style.width = "";
732
- }
733
- });
734
- }
735
- function chatMessage(role, text) {
736
- const message = document.createElement("div");
737
- message.dataset.mkMessage = "";
738
- message.dataset.mkRole = role;
739
- const body = document.createElement("span");
740
- body.dataset.mkText = "";
741
- body.textContent = text;
742
- message.append(body);
743
- return message;
744
- }
745
- function createChat(root, options = {}) {
746
- const messages = root ? part(root, "messages") : null;
747
- if (!root || !messages) {
748
- warnOnce('chat: needs a [data-mk-part="messages"] container');
749
- return inert({
750
- add: () => document.createElement("div"),
751
- pending() {
752
- },
753
- reply: () => document.createElement("div"),
754
- confirm() {
755
- },
756
- setRecording() {
757
- },
758
- scrollToLatest() {
759
- },
760
- adopt() {
761
- },
762
- messages: null
763
- });
764
- }
765
- registerMotion();
766
- const { clearOnSend = true } = options;
767
- const thinking = options.labels?.thinking ?? "Pensando...";
768
- const scope = createScope();
769
- const composer = part(root, "composer");
770
- const input = composer?.querySelector('[data-mk-part="input"], textarea') ?? null;
771
- const mic = part(root, "mic");
772
- const seen = /* @__PURE__ */ new WeakSet();
773
- let placeholder = null;
774
- let pendingTimer;
775
- let waiting = false;
776
- scope.attr(messages, "role", "log");
777
- scope.attr(messages, "aria-live", "polite");
778
- const existing = [...messages.querySelectorAll(MESSAGE)];
779
- if (options.animateExisting) enterMessages(existing);
780
- for (const message of existing) seen.add(message);
781
- const observer = scope.observe(
782
- new MutationObserver((records) => {
783
- const fresh = [];
784
- for (const record of records) {
785
- for (const node of record.addedNodes) {
786
- if (!(node instanceof HTMLElement) || !node.matches(MESSAGE) || seen.has(node)) continue;
787
- seen.add(node);
788
- fresh.push(node);
789
- }
790
- }
791
- if (!fresh.length) return;
792
- enterMessages(fresh);
793
- scrollToLatest(messages);
794
- })
795
- );
796
- observer.observe(messages, { childList: true });
797
- const clearPending = () => {
798
- clearTimeout(pendingTimer);
799
- pendingTimer = void 0;
800
- waiting = false;
801
- };
802
- const add = (role, text) => {
803
- const message = chatMessage(role, text);
804
- messages.append(message);
805
- return message;
806
- };
807
- const pending = () => {
808
- clearPending();
809
- waiting = true;
810
- pendingTimer = setTimeout(() => {
811
- pendingTimer = void 0;
812
- if (!waiting || placeholder) return;
813
- placeholder = document.createElement("div");
814
- placeholder.dataset.mkMessage = "";
815
- placeholder.dataset.mkRole = "assistant";
816
- placeholder.dataset.mkPending = "";
817
- placeholder.setAttribute("data-flip-id", PENDING_ID);
818
- placeholder.innerHTML = `<span class="mk-chat__thinking">${iconSvg("sparkle", 18)}<span></span></span>`;
819
- placeholder.querySelector("span span").textContent = thinking;
820
- seen.add(placeholder);
821
- messages.append(placeholder);
822
- enterMessages([placeholder]);
823
- scrollToLatest(messages);
824
- }, TIMING.chat.pendingDelayMs);
825
- };
826
- const reply = (content) => {
827
- const message = typeof content === "string" ? chatMessage("assistant", content) : content;
828
- clearPending();
829
- seen.add(message);
830
- const was = placeholder;
831
- placeholder = null;
832
- if (was?.isConnected) {
833
- const state = capturePending(was);
834
- was.replaceWith(message);
835
- morphPendingToReply(state, message, messages);
836
- } else {
837
- if (!message.isConnected) messages.append(message);
838
- enterMessages([message]);
839
- scrollToLatest(messages);
840
- }
841
- return message;
842
- };
843
- const confirm = (bar) => {
844
- if (reduced()) return;
845
- flowIn(bar);
846
- const words = splitWords(bar);
847
- if (!words.parts.length) return;
848
- gsap.fromTo(
849
- words.parts,
850
- { y: 16, autoAlpha: 0, filter: "blur(6px)" },
851
- {
852
- y: 0,
853
- autoAlpha: 1,
854
- filter: "blur(0px)",
855
- duration: TIMING.word,
856
- ease: "back.out(1)",
857
- stagger: TIMING.chat.confirmWordStagger,
858
- clearProps: "transform,opacity,visibility,filter",
859
- onComplete: () => words.revert()
860
- }
861
- );
862
- };
863
- const send = () => {
864
- if (!input) return;
865
- const text = input.value.trim();
866
- if (!text) return;
867
- const allowed = root.dispatchEvent(new CustomEvent("mk:send", { bubbles: true, cancelable: true, detail: { text } }));
868
- if (!allowed) return;
869
- options.onSend?.(text);
870
- if (clearOnSend) {
871
- input.value = "";
872
- fitComposer(input);
873
- }
874
- };
875
- if (input) {
876
- scope.on(input, "input", () => fitComposer(input));
877
- scope.on(input, "keydown", (event) => {
878
- const key = event;
879
- if (key.key !== "Enter" || key.shiftKey || key.isComposing) return;
880
- event.preventDefault();
881
- send();
882
- });
883
- }
884
- if (composer) {
885
- scope.on(composer, "submit", (event) => {
886
- event.preventDefault();
887
- send();
888
- });
889
- const sendButton = part(root, "send");
890
- if (sendButton && !composer.matches("form")) scope.on(sendButton, "click", send);
891
- }
892
- scope.add(() => {
893
- clearPending();
894
- placeholder?.remove();
895
- placeholder = null;
896
- });
897
- if (input) fitComposer(input);
898
- root.dataset.mkReady = "";
899
- return {
900
- add,
901
- pending,
902
- reply,
903
- confirm,
904
- setRecording(on) {
905
- if (mic) scope.attr(mic, "data-mk-recording", on ? "" : null);
906
- },
907
- scrollToLatest: () => scrollToLatest(messages),
908
- adopt: (message) => seen.add(message),
909
- messages,
910
- destroy() {
911
- scope.dispose();
912
- if (input) input.style.removeProperty("height");
913
- delete root.dataset.mkReady;
914
- }
915
- };
916
- }
917
-
918
- // src/components/nav/engine.ts
919
- var NAV_STYLES = ["clasica", "bonita", "dock", "guapa", "tasks", "melon", "tunnel"];
920
- var ITEM = ".mk-nav__buttons .mk-nav__button";
921
- var isStyle = (value) => NAV_STYLES.includes(value);
922
- var valueOf = (item) => item.dataset.mkValue ?? item.getAttribute("href") ?? void 0;
923
- var read = (key) => {
924
- try {
925
- return localStorage.getItem(key);
926
- } catch {
927
- return null;
928
- }
929
- };
930
- var write = (key, value) => {
931
- try {
932
- localStorage.setItem(key, value);
933
- } catch {
934
- }
935
- };
936
- function revealShell(nav) {
937
- if (!entrances()) return null;
938
- const shell = nav.closest(".mk-shell") ?? nav.parentElement;
939
- if (!shell) return null;
940
- const origin = nav.querySelector(".mk-nav__button--first") ?? nav.querySelector(ITEM) ?? nav;
941
- return revealFrom(shell, { origin });
942
- }
943
- function createNav(root, options = {}) {
944
- if (!root) {
945
- warnOnce("nav: missing root");
946
- return inert({ select() {
947
- }, setStyle() {
948
- }, setAccent() {
949
- }, index: -1, style: "bonita", items: [] });
950
- }
951
- const scope = createScope();
952
- const items2 = () => [...root.querySelectorAll(ITEM)];
953
- const fromClass = NAV_STYLES.find((name) => root.classList.contains(`mk-nav--${name}`));
954
- const saved = options.storageKey ? read(options.storageKey) : null;
955
- let style = options.style ?? (isStyle(saved) ? saved : void 0) ?? fromClass ?? "bonita";
956
- const paint = (accent2) => {
957
- const classes = new Set(root.classList);
958
- for (const name of NAV_STYLES) classes.delete(`mk-nav--${name}`);
959
- classes.add("mk-nav");
960
- classes.add(`mk-nav--${style}`);
961
- if (accent2) classes.add("mk-nav--accent");
962
- else classes.delete("mk-nav--accent");
963
- scope.attr(root, "class", [...classes].join(" "));
964
- };
965
- let accent = options.accent ?? root.classList.contains("mk-nav--accent");
966
- paint(accent);
967
- if (root.localName !== "nav") scope.attr(root, "role", "navigation");
968
- if (!root.hasAttribute("aria-label") && !root.hasAttribute("aria-labelledby")) scope.attr(root, "aria-label", options.labels?.nav ?? "Principal");
969
- const mark = (current) => {
970
- for (const item of items2()) scope.attr(item, "aria-current", item === current ? "page" : null);
971
- };
972
- const find = (target) => {
973
- const list = items2();
974
- return typeof target === "number" ? list[target] : list.find((item) => valueOf(item) === target);
975
- };
976
- scope.on(root, "click", (event) => {
977
- const item = event.target?.closest(ITEM);
978
- if (!item || !root.contains(item)) return;
979
- const index = items2().indexOf(item);
980
- const value = valueOf(item);
981
- const detail = { index, value, item, originalEvent: event };
982
- if (!root.dispatchEvent(new CustomEvent("mk:navigate", { bubbles: true, cancelable: true, detail }))) {
983
- event.preventDefault();
984
- return;
985
- }
986
- mark(item);
987
- options.onSelect?.(index, value, item);
988
- });
989
- let reveal = null;
990
- if (options.reveal) reveal = revealShell(root);
991
- root.dataset.mkReady = "";
992
- return {
993
- select(target) {
994
- const item = find(target);
995
- if (!item) warnOnce(`nav: no item ${JSON.stringify(target)}`);
996
- else mark(item);
997
- },
998
- setStyle(next) {
999
- if (!isStyle(next)) {
1000
- warnOnce(`nav: unknown style "${String(next)}" (use ${NAV_STYLES.join(", ")})`);
1001
- return;
1002
- }
1003
- style = next;
1004
- paint(accent);
1005
- if (options.storageKey) write(options.storageKey, next);
1006
- },
1007
- setAccent(on) {
1008
- accent = on;
1009
- paint(accent);
1010
- },
1011
- get index() {
1012
- return items2().findIndex((item) => item.getAttribute("aria-current") === "page");
1013
- },
1014
- get style() {
1015
- return style;
1016
- },
1017
- get items() {
1018
- return items2();
1019
- },
1020
- destroy() {
1021
- reveal?.cancel();
1022
- reveal = null;
1023
- scope.dispose();
1024
- delete root.dataset.mkReady;
1025
- }
1026
- };
1027
- }
1028
-
1029
- // src/components/cursor/engine.ts
1030
- var svg = (extra = "") => `<svg aria-hidden="true" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"${extra}><path d="${ICON_PATHS.send}"/></svg>`;
1031
- function backgroundTone(el) {
1032
- if (!el) return "light";
1033
- if (el.closest("img, video, picture, canvas")) return "dark";
1034
- for (let node = el; node; node = node.parentElement) {
1035
- const match = getComputedStyle(node).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
1036
- if (!match) continue;
1037
- const alpha = match[4] === void 0 ? 1 : parseFloat(match[4]);
1038
- if (alpha <= 0.1) continue;
1039
- const luminance = (Number(match[1]) * 299 + Number(match[2]) * 587 + Number(match[3]) * 114) / 1e3;
1040
- return luminance > TIMING.cursor.light ? "light" : "dark";
1041
- }
1042
- return "light";
1043
- }
1044
- function createCursor(host, options = {}) {
1045
- const variant = options.variant ?? "chip";
1046
- const idle = inert({ visible: false, label: "", position: { x: 0, y: 0 }, tone: "light", update() {
1047
- } });
1048
- if (!host) {
1049
- warnOnce("cursor: missing element");
1050
- return idle;
1051
- }
1052
- const coarse = typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
1053
- if (coarse || reduced()) return idle;
1054
- registerMotion();
1055
- const scope = createScope();
1056
- const t = TIMING.cursor;
1057
- const zone = options.zone ?? (variant === "dot" ? host.parentElement ?? document.body : document.documentElement);
1058
- scope.attr(host, "class", [host.getAttribute("class"), "mk-cursor", `mk-cursor--${variant}`].filter(Boolean).join(" "));
1059
- scope.attr(host, "aria-hidden", "true");
1060
- const pointer = { x: -100, y: -100, placed: false };
1061
- const position = { x: -100, y: -100 };
1062
- const velocity = { x: 0, y: 0 };
1063
- let visible = false;
1064
- let label = "";
1065
- let tone = "light";
1066
- const made = [];
1067
- let chip = null;
1068
- let chipLabel = null;
1069
- let chipIcon = null;
1070
- let word = null;
1071
- const size = { width: 0, height: 0 };
1072
- if (variant === "chip") {
1073
- chip = document.createElement("span");
1074
- chip.className = "mk-cursor__chip mk-glass";
1075
- chip.innerHTML = '<span class="mk-glass__shadow" aria-hidden="true"></span><span class="mk-cursor__icon mk-icon"></span><span class="mk-cursor__label"></span>';
1076
- chipIcon = chip.querySelector(".mk-cursor__icon");
1077
- chipLabel = chip.querySelector(".mk-cursor__label");
1078
- host.append(chip);
1079
- made.push(chip);
1080
- gsap.set(chip, { autoAlpha: 0, scale: 0.8 });
1081
- } else {
1082
- const arrows = options.arrows ?? "y";
1083
- scope.attr(host, "data-mk-arrows", arrows);
1084
- scope.attr(zone, "data-mk-cursor-zone", "");
1085
- const dot = document.createElement("span");
1086
- dot.className = "mk-cursor__dot";
1087
- dot.innerHTML = arrows === "none" ? '<span class="mk-cursor__circle"></span><span class="mk-cursor__text"><span class="mk-cursor__word"></span></span>' : `<span class="mk-cursor__circle"></span><span class="mk-cursor__arrow mk-cursor__arrow--start">${svg()}</span><span class="mk-cursor__text"><span class="mk-cursor__word"></span></span><span class="mk-cursor__arrow mk-cursor__arrow--end">${svg()}</span>`;
1088
- word = dot.querySelector(".mk-cursor__word");
1089
- if (word) word.textContent = options.text ?? "";
1090
- host.append(dot);
1091
- made.push(dot);
1092
- scope.attr(host, "data-mk-hidden", "");
1093
- }
1094
- const appear = motionSpring(...TIMING.springs.cursorAppear);
1095
- const showChip = (next) => {
1096
- if (!chip || !chipLabel || !chipIcon) return;
1097
- const text = next?.dataset.mkCursor ?? "";
1098
- if (text) {
1099
- label = text;
1100
- chipLabel.textContent = text;
1101
- chipIcon.textContent = next?.dataset.mkCursorIcon ?? "";
1102
- chip.classList.toggle("mk-cursor__chip--circle", next?.dataset.mkCursorShape === "circle");
1103
- const box = chip.getBoundingClientRect();
1104
- size.width = box.width;
1105
- size.height = box.height;
1106
- }
1107
- if (Boolean(text) === visible) return;
1108
- visible = Boolean(text);
1109
- if (!visible) label = "";
1110
- gsap.to(chip, {
1111
- autoAlpha: visible ? 1 : 0,
1112
- scale: visible ? 1 : 0.8,
1113
- duration: appear.duration,
1114
- ease: appear.ease,
1115
- overwrite: "auto"
1116
- });
1117
- };
1118
- let inZone = false;
1119
- let overHide = false;
1120
- const syncDot = () => scope.attr(host, "data-mk-hidden", inZone && !overHide ? null : "");
1121
- const wordFor = (target) => {
1122
- if (!word) return;
1123
- const source = target?.closest("[data-mk-cursor]");
1124
- word.textContent = source && zone.contains(source) ? source.dataset.mkCursor ?? "" : options.text ?? "";
1125
- };
1126
- const place = (event) => {
1127
- if (event.pointerType === "touch") return;
1128
- pointer.x = event.clientX;
1129
- pointer.y = event.clientY;
1130
- if (!pointer.placed) {
1131
- pointer.placed = true;
1132
- if (variant === "dot") {
1133
- position.x = pointer.x;
1134
- position.y = pointer.y;
1135
- }
1136
- }
1137
- };
1138
- scope.on(window, "pointermove", (event) => place(event), { passive: true });
1139
- const overTarget = variant === "chip" ? document : zone;
1140
- scope.on(overTarget, "pointerover", (event) => {
1141
- const target = event.target;
1142
- if (variant === "chip") {
1143
- const source = target?.closest("[data-mk-cursor]") ?? null;
1144
- showChip(source && zone.contains(source) ? source : null);
1145
- return;
1146
- }
1147
- overHide = Boolean(target?.closest("[data-mk-cursor-hide]"));
1148
- wordFor(target);
1149
- syncDot();
1150
- });
1151
- if (variant === "dot") {
1152
- scope.on(zone, "pointerenter", (event) => {
1153
- place(event);
1154
- inZone = true;
1155
- visible = true;
1156
- syncDot();
1157
- });
1158
- scope.on(zone, "pointerleave", () => {
1159
- inZone = false;
1160
- visible = false;
1161
- scope.attr(host, "data-mk-pressed", null);
1162
- syncDot();
1163
- });
1164
- scope.on(zone, "pointerdown", () => scope.attr(host, "data-mk-pressed", ""));
1165
- scope.on(window, "pointerup", () => scope.attr(host, "data-mk-pressed", null));
1166
- }
1167
- const [stiffness, damping] = TIMING.springs.cursorFollow;
1168
- const update = (dt = 1) => {
1169
- if (variant === "chip") {
1170
- const halfW = size.width / 2 + t.gutter;
1171
- const halfH = size.height / 2 + t.gutter;
1172
- const tx = size.width ? Math.min(Math.max(pointer.x, halfW), innerWidth - halfW) : pointer.x;
1173
- const ty = size.height ? Math.min(Math.max(pointer.y, halfH), innerHeight - halfH) : pointer.y;
1174
- const steps = Math.max(1, Math.ceil(dt * 4));
1175
- const h = dt / 60 / steps;
1176
- for (let i = 0; i < steps; i++) {
1177
- velocity.x += (stiffness * (tx - position.x) - damping * velocity.x) * h;
1178
- velocity.y += (stiffness * (ty - position.y) - damping * velocity.y) * h;
1179
- position.x += velocity.x * h;
1180
- position.y += velocity.y * h;
1181
- }
1182
- if (visible && typeof document.elementFromPoint === "function") {
1183
- tone = backgroundTone(document.elementFromPoint(pointer.x, pointer.y));
1184
- if (chip && chip.dataset.mkTone !== tone) chip.dataset.mkTone = tone;
1185
- }
1186
- } else {
1187
- const step = 1 - Math.pow(1 - t.dotLerp, dt);
1188
- position.x += (pointer.x - position.x) * step;
1189
- position.y += (pointer.y - position.y) * step;
1190
- }
1191
- host.style.transform = `translate3d(${position.x.toFixed(2)}px, ${position.y.toFixed(2)}px, 0) translate(-50%, -50%)`;
1192
- };
1193
- scope.add(subscribe(update));
1194
- update(0);
1195
- host.dataset.mkReady = "";
1196
- return {
1197
- get visible() {
1198
- return visible;
1199
- },
1200
- get label() {
1201
- return label;
1202
- },
1203
- get position() {
1204
- return { ...position };
1205
- },
1206
- get tone() {
1207
- return tone;
1208
- },
1209
- update,
1210
- destroy() {
1211
- if (chip) gsap.killTweensOf(chip);
1212
- scope.dispose();
1213
- for (const node of made.splice(0)) node.remove();
1214
- host.style.removeProperty("transform");
1215
- if (host.getAttribute("style") === "") host.removeAttribute("style");
1216
- delete host.dataset.mkReady;
1217
- }
1218
- };
1219
- }
1220
-
1221
- // src/components/island/engine.ts
1222
- var sequence = 0;
1223
- var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
1224
- function createIsland(root, options = {}) {
1225
- const shell = root?.querySelector(".mk-island__shell") ?? null;
1226
- const panels = root ? [...root.querySelectorAll("[data-mk-island-panel]")] : [];
1227
- if (!root || !shell || !panels.length) {
1228
- warnOnce("island: needs .mk-island__shell and at least one [data-mk-island-panel]");
1229
- return inert({ open() {
1230
- }, close() {
1231
- }, toggle() {
1232
- }, ready() {
1233
- }, active: null });
1234
- }
1235
- const t = TIMING.island;
1236
- const { entrance = true, closeOnScroll = true, closeOnNavigate = true, focusOnOpen = true, onChange } = options;
1237
- registerMotion();
1238
- const scope = createScope();
1239
- const toggles = [...root.querySelectorAll("[data-mk-island-toggle]")];
1240
- const cta = root.querySelector(".mk-island__cta");
1241
- const backdrops = [...root.querySelectorAll("[data-mk-island-close]")];
1242
- let active = null;
1243
- let trigger = null;
1244
- let scrollStart = 0;
1245
- let armed = false;
1246
- let focusTimer;
1247
- let entered = false;
1248
- let entry = null;
1249
- const flag = (el, name, on) => {
1250
- if (el) scope.attr(el, name, on ? "" : null);
1251
- };
1252
- const sync = () => {
1253
- for (const panel of panels) {
1254
- const on = panel.dataset.mkIslandPanel === active;
1255
- if (active) flag(panel, "hidden", !on);
1256
- scope.attr(panel, "aria-hidden", String(!on));
1257
- flag(panel, "inert", !on);
1258
- }
1259
- for (const button of toggles) scope.attr(button, "aria-expanded", String(button.dataset.mkIslandToggle === active));
1260
- flag(cta, "inert", Boolean(active));
1261
- for (const backdrop of backdrops) {
1262
- flag(backdrop, "inert", !active);
1263
- scope.attr(backdrop, "aria-hidden", String(!active));
1264
- }
1265
- flag(root, "data-mk-open", Boolean(active));
1266
- scope.attr(root, "data-mk-active", active);
1267
- };
1268
- const stagger = (opening) => {
1269
- root.querySelectorAll("[data-mk-island-item]").forEach((item, index) => {
1270
- scope.attr(item, "style", item.getAttribute("style"));
1271
- item.style.setProperty("--mk-island-delay", opening ? `${Number((t.delay + index * t.step).toFixed(3))}s` : "0s");
1272
- });
1273
- };
1274
- const close = (returnFocus = true) => {
1275
- if (scope.disposed || !active) return;
1276
- clearTimeout(focusTimer);
1277
- active = null;
1278
- stagger(false);
1279
- sync();
1280
- onChange?.(null);
1281
- if (returnFocus && trigger?.isConnected) trigger.focus({ preventScroll: true });
1282
- };
1283
- const open = (name = panels[0].dataset.mkIslandPanel, source) => {
1284
- if (scope.disposed) return;
1285
- const panel = panels.find((el) => el.dataset.mkIslandPanel === name);
1286
- if (!panel) return;
1287
- const wasClosed = !active;
1288
- clearTimeout(focusTimer);
1289
- active = name;
1290
- trigger = source ?? toggles.find((el) => el.dataset.mkIslandToggle === name) ?? trigger;
1291
- if (wasClosed) {
1292
- scrollStart = window.scrollY;
1293
- armed = false;
1294
- stagger(true);
1295
- }
1296
- sync();
1297
- onChange?.(name);
1298
- if (focusOnOpen)
1299
- focusTimer = setTimeout(
1300
- () => {
1301
- if (active !== name) return;
1302
- const first = [...panel.querySelectorAll(FOCUSABLE)].find((el) => !el.closest("[hidden], [inert]"));
1303
- (first ?? panel).focus({ preventScroll: true });
1304
- },
1305
- reduced() ? 0 : t.focus
1306
- );
1307
- };
1308
- const toggle = (name = panels[0].dataset.mkIslandPanel, source) => active === name ? close(false) : open(name, source);
1309
- for (const panel of panels) {
1310
- if (!panel.id) scope.attr(panel, "id", `mk-island-panel-${++sequence}`);
1311
- if (!panel.hasAttribute("tabindex")) scope.attr(panel, "tabindex", "-1");
1312
- if (!panel.hasAttribute("role")) scope.attr(panel, "role", "region");
1313
- }
1314
- for (const button of toggles) {
1315
- const panel = panels.find((el) => el.dataset.mkIslandPanel === button.dataset.mkIslandToggle);
1316
- if (panel) scope.attr(button, "aria-controls", panel.id);
1317
- scope.on(button, "click", () => toggle(button.dataset.mkIslandToggle, button));
1318
- }
1319
- for (const backdrop of backdrops) scope.on(backdrop, "click", () => close(true));
1320
- scope.on(document, "keydown", (event) => {
1321
- if (active && event.key === "Escape") {
1322
- event.preventDefault();
1323
- close(true);
1324
- }
1325
- });
1326
- if (closeOnNavigate)
1327
- scope.on(root, "click", (event) => {
1328
- const link = event.target?.closest?.("a[href]");
1329
- if (active && link?.closest("[data-mk-island-panel]")) close(false);
1330
- });
1331
- if (closeOnScroll) {
1332
- const arm = () => {
1333
- if (active) armed = true;
1334
- };
1335
- scope.on(window, "wheel", arm, { passive: true });
1336
- scope.on(window, "touchmove", arm, { passive: true });
1337
- scope.on(
1338
- window,
1339
- "scroll",
1340
- () => {
1341
- if (active && armed && Math.abs(window.scrollY - scrollStart) > 1) close(false);
1342
- },
1343
- { passive: true }
1344
- );
1345
- }
1346
- scope.add(() => clearTimeout(focusTimer));
1347
- const moving = entrance !== false && entrances();
1348
- if (moving) gsap.set(shell, { yPercent: t.offset });
1349
- const ready = () => {
1350
- if (scope.disposed || entered || !moving) return;
1351
- entered = true;
1352
- entry = gsap.to(shell, { yPercent: 0, duration: t.entrance });
1353
- };
1354
- scope.add(() => {
1355
- entry?.kill();
1356
- if (moving) gsap.set(shell, { clearProps: "transform,translate" });
1357
- if (shell.getAttribute("style") === "") shell.removeAttribute("style");
1358
- });
1359
- sync();
1360
- if (entrance === true) ready();
1361
- scope.attr(root, "data-mk-ready", "");
1362
- return {
1363
- open: (name) => open(name),
1364
- close,
1365
- toggle: (name) => toggle(name),
1366
- ready,
1367
- get active() {
1368
- return active;
1369
- },
1370
- destroy: () => scope.dispose()
1371
- };
1372
- }
1373
-
1374
- export {
1375
- enterMessages,
1376
- fitComposer,
1377
- scrollToLatest,
1378
- createThemePicker,
1379
- ICON_PATHS,
1380
- iconSvg,
1381
- TOAST_LIMIT,
1382
- getToasts,
1383
- subscribeToasts,
1384
- dismissToast,
1385
- removeToast,
1386
- clearToasts,
1387
- toast,
1388
- toastTimers,
1389
- createToaster,
1390
- PENDING_ID,
1391
- capturePending,
1392
- morphPendingToReply,
1393
- chatMessage,
1394
- createChat,
1395
- NAV_STYLES,
1396
- revealShell,
1397
- createNav,
1398
- createCursor,
1399
- createIsland
1400
- };
1401
- //# sourceMappingURL=chunk-6P4P7NU6.js.map