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