animot-presenter 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,10 +8,11 @@
8
8
  import CounterRenderer from './renderers/CounterRenderer.svelte';
9
9
  import ChartRenderer from './renderers/ChartRenderer.svelte';
10
10
  import IconRenderer from './renderers/IconRenderer.svelte';
11
- import { easeInOutCubic, getBackgroundStyle, hashFraction, getFloatAnimName, computeFloatAmp, computeFloatSpeed } from './engine/utils';
11
+ import { easeInOutCubic, getEasingFn, getBackgroundStyle, hashFraction, getFloatAnimName, computeFloatAmp, computeFloatSpeed } from './engine/utils';
12
12
  import type {
13
13
  AnimotProject, AnimotPresenterProps, CanvasElement, CodeElement, TextElement,
14
14
  ArrowElement, ImageElement, ShapeElement, CounterElement, ChartElement, IconElement,
15
+ SvgElement, MotionPathElement, PathPoint,
15
16
  Slide, CodeAnimationMode, AnimatableProperty
16
17
  } from './types';
17
18
  import './styles/presenter.css';
@@ -21,16 +22,90 @@
21
22
  interface AnimatedElement {
22
23
  x: TweenValue; y: TweenValue; width: TweenValue; height: TweenValue;
23
24
  rotation: TweenValue; skewX: TweenValue; skewY: TweenValue;
24
- tiltX: TweenValue; tiltY: TweenValue; opacity: TweenValue; borderRadius: TweenValue;
25
+ tiltX: TweenValue; tiltY: TweenValue; perspective: TweenValue;
26
+ opacity: TweenValue; borderRadius: TweenValue;
25
27
  fontSize: TweenValue | null;
26
28
  fillColor: ReturnType<typeof tween<string>> | null;
27
29
  strokeColor: ReturnType<typeof tween<string>> | null;
28
30
  strokeWidth: TweenValue | null;
29
31
  shapeMorph: TweenValue | null;
32
+ motionPathProgress: TweenValue | null;
30
33
  }
31
34
 
32
35
  interface ShapeMorphState { fromType: string; toType: string; }
33
36
 
37
+ // Active motion path loop cancellation tokens
38
+ let motionPathLoopAbort: AbortController | null = null;
39
+ function cancelMotionPathLoops() {
40
+ if (motionPathLoopAbort) { motionPathLoopAbort.abort(); motionPathLoopAbort = null; }
41
+ }
42
+
43
+ // --- Motion Path Utilities ---
44
+ function buildPresenterPathD(points: PathPoint[], closed: boolean): string {
45
+ if (points.length < 2) return '';
46
+ let d = `M ${points[0].x} ${points[0].y}`;
47
+ for (let i = 1; i < points.length; i++) {
48
+ const prev = points[i - 1], curr = points[i];
49
+ const cp1x = prev.x + (prev.handleOut?.x ?? 0), cp1y = prev.y + (prev.handleOut?.y ?? 0);
50
+ const cp2x = curr.x + (curr.handleIn?.x ?? 0), cp2y = curr.y + (curr.handleIn?.y ?? 0);
51
+ if (prev.handleOut || curr.handleIn) d += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${curr.x} ${curr.y}`;
52
+ else d += ` L ${curr.x} ${curr.y}`;
53
+ }
54
+ if (closed && points.length > 2) {
55
+ const last = points[points.length - 1], first = points[0];
56
+ const cp1x = last.x + (last.handleOut?.x ?? 0), cp1y = last.y + (last.handleOut?.y ?? 0);
57
+ const cp2x = first.x + (first.handleIn?.x ?? 0), cp2y = first.y + (first.handleIn?.y ?? 0);
58
+ if (last.handleOut || first.handleIn) d += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${first.x} ${first.y}`;
59
+ else d += ` Z`;
60
+ }
61
+ return d;
62
+ }
63
+
64
+ function cubicBez(p0: number, p1: number, p2: number, p3: number, t: number): number {
65
+ const mt = 1 - t;
66
+ return mt * mt * mt * p0 + 3 * mt * mt * t * p1 + 3 * mt * t * t * p2 + t * t * t * p3;
67
+ }
68
+ function cubicBezDeriv(p0: number, p1: number, p2: number, p3: number, t: number): number {
69
+ const mt = 1 - t;
70
+ return 3 * mt * mt * (p1 - p0) + 6 * mt * t * (p2 - p1) + 3 * t * t * (p3 - p2);
71
+ }
72
+
73
+ function getPresenterPointOnPath(points: PathPoint[], closed: boolean, progress: number): { x: number; y: number; angle: number } {
74
+ if (points.length < 2) return { x: points[0]?.x ?? 0, y: points[0]?.y ?? 0, angle: 0 };
75
+ const segs: { p0x: number; p0y: number; p1x: number; p1y: number; p2x: number; p2y: number; p3x: number; p3y: number; length: number }[] = [];
76
+ const segCount = closed ? points.length : points.length - 1;
77
+ for (let i = 0; i < segCount; i++) {
78
+ const curr = points[i], next = points[(i + 1) % points.length];
79
+ const p0x = curr.x, p0y = curr.y;
80
+ const p1x = curr.x + (curr.handleOut?.x ?? 0), p1y = curr.y + (curr.handleOut?.y ?? 0);
81
+ const p2x = next.x + (next.handleIn?.x ?? 0), p2y = next.y + (next.handleIn?.y ?? 0);
82
+ const p3x = next.x, p3y = next.y;
83
+ let length = 0, prevPx = p0x, prevPy = p0y;
84
+ for (let s = 1; s <= 20; s++) {
85
+ const t = s / 20;
86
+ const px = cubicBez(p0x, p1x, p2x, p3x, t), py = cubicBez(p0y, p1y, p2y, p3y, t);
87
+ length += Math.sqrt((px - prevPx) ** 2 + (py - prevPy) ** 2);
88
+ prevPx = px; prevPy = py;
89
+ }
90
+ segs.push({ p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, length });
91
+ }
92
+ const totalLength = segs.reduce((sum, s) => sum + s.length, 0);
93
+ const targetLength = progress * totalLength;
94
+ let accum = 0;
95
+ for (const seg of segs) {
96
+ if (accum + seg.length >= targetLength || seg === segs[segs.length - 1]) {
97
+ const t = Math.max(0, Math.min(1, seg.length > 0 ? (targetLength - accum) / seg.length : 0));
98
+ return {
99
+ x: cubicBez(seg.p0x, seg.p1x, seg.p2x, seg.p3x, t),
100
+ y: cubicBez(seg.p0y, seg.p1y, seg.p2y, seg.p3y, t),
101
+ angle: Math.atan2(cubicBezDeriv(seg.p0y, seg.p1y, seg.p2y, seg.p3y, t), cubicBezDeriv(seg.p0x, seg.p1x, seg.p2x, seg.p3x, t)) * (180 / Math.PI)
102
+ };
103
+ }
104
+ accum += seg.length;
105
+ }
106
+ return { x: points[0].x, y: points[0].y, angle: 0 };
107
+ }
108
+
34
109
  let {
35
110
  src, data, autoplay = false, loop = false, controls = true, arrows = false,
36
111
  progress: showProgress = true, keyboard = true, duration: durationOverride,
@@ -195,13 +270,15 @@
195
270
  skewY: tween(element.skewY ?? 0, { duration: 500 }),
196
271
  tiltX: tween(element.tiltX ?? 0, { duration: 500 }),
197
272
  tiltY: tween(element.tiltY ?? 0, { duration: 500 }),
273
+ perspective: tween(element.perspective ?? 1000, { duration: 500 }),
198
274
  opacity: tween(startOpacity, { duration: 300 }),
199
275
  borderRadius: tween(br, { duration: 500 }),
200
276
  fontSize: textEl ? tween(textEl.fontSize, { duration: 500 }) : null,
201
277
  fillColor: shapeEl ? tween(shapeEl.fillColor, { duration: 500 }) : null,
202
278
  strokeColor: shapeEl ? tween(shapeEl.strokeColor, { duration: 500 }) : null,
203
279
  strokeWidth: shapeEl ? tween(shapeEl.strokeWidth, { duration: 500 }) : null,
204
- shapeMorph: shapeEl ? tween(1, { duration: 500 }) : null
280
+ shapeMorph: shapeEl ? tween(1, { duration: 500 }) : null,
281
+ motionPathProgress: element.motionPathConfig ? tween(0, { duration: 500 }) : null
205
282
  });
206
283
  const currentSlideEl = getElementInSlide(currentSlide, element.id);
207
284
  elementContent.set(element.id, JSON.parse(JSON.stringify(currentSlideEl || element)));
@@ -213,6 +290,46 @@
213
290
  previousCodeContent = new Map(previousCodeContent);
214
291
  }
215
292
 
293
+ async function animateMotionPaths(slide: Slide) {
294
+ cancelMotionPathLoops();
295
+ motionPathLoopAbort = new AbortController();
296
+ const signal = motionPathLoopAbort.signal;
297
+
298
+ const resets: Promise<void>[] = [];
299
+ for (const element of slide.canvas.elements) {
300
+ if (element.motionPathConfig) {
301
+ const animated = animatedElements.get(element.id);
302
+ if (animated?.motionPathProgress) {
303
+ resets.push(animated.motionPathProgress.to(0, { duration: 0 }));
304
+ }
305
+ }
306
+ }
307
+ await Promise.all(resets);
308
+ for (const element of slide.canvas.elements) {
309
+ if (element.motionPathConfig) {
310
+ const animated = animatedElements.get(element.id);
311
+ if (animated?.motionPathProgress) {
312
+ const config = element.animationConfig;
313
+ const duration = config?.duration ?? 2000;
314
+ const easing = getEasingFn(config?.easing);
315
+ const shouldLoop = element.motionPathConfig.loop;
316
+
317
+ if (shouldLoop) {
318
+ (async () => {
319
+ while (!signal.aborted) {
320
+ await animated.motionPathProgress!.to(0, { duration: 0 });
321
+ await animated.motionPathProgress!.to(1, { duration, easing });
322
+ if (!signal.aborted) await new Promise(r => setTimeout(r, 50));
323
+ }
324
+ })();
325
+ } else {
326
+ animated.motionPathProgress.to(1, { duration, easing });
327
+ }
328
+ }
329
+ }
330
+ }
331
+ }
332
+
216
333
  // Animate to slide
217
334
  async function animateToSlide(targetIndex: number) {
218
335
  if (isTransitioning || targetIndex < 0 || targetIndex >= slides.length) return;
@@ -221,6 +338,7 @@
221
338
  transitionDirection = targetIndex > currentSlideIndex ? 'forward' : 'backward';
222
339
  const targetSlide = slides[targetIndex];
223
340
  clearAllTypewriterAnimations();
341
+ cancelMotionPathLoops();
224
342
  const transition = targetSlide.transition;
225
343
  const duration = durationOverride ?? transition.duration;
226
344
  transitionDurationMs = duration;
@@ -249,6 +367,7 @@
249
367
  animated.rotation.to(targetEl.rotation, { duration: 0 });
250
368
  animated.skewX.to(targetEl.skewX ?? 0, { duration: 0 }); animated.skewY.to(targetEl.skewY ?? 0, { duration: 0 });
251
369
  animated.tiltX.to(targetEl.tiltX ?? 0, { duration: 0 }); animated.tiltY.to(targetEl.tiltY ?? 0, { duration: 0 });
370
+ animated.perspective.to(targetEl.perspective ?? 1000, { duration: 0 });
252
371
  animated.opacity.to(1, { duration: 0 });
253
372
  animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: 0 });
254
373
  if (targetEl.type === 'text') animated.fontSize?.to((targetEl as TextElement).fontSize, { duration: 0 });
@@ -258,6 +377,7 @@
258
377
  animated.strokeColor?.to(s.strokeColor, { duration: 0 });
259
378
  animated.strokeWidth?.to(s.strokeWidth, { duration: 0 });
260
379
  }
380
+ if (animated.motionPathProgress) animated.motionPathProgress.to(0, { duration: 0 });
261
381
  }
262
382
  } else if (animated) { animated.opacity.to(0, { duration: 0 }); }
263
383
  }
@@ -288,6 +408,7 @@
288
408
  transitionClass = `transition-${transition.type}-in`;
289
409
  await new Promise(r => setTimeout(r, duration * 0.6));
290
410
  transitionClass = '';
411
+ animateMotionPaths(targetSlide);
291
412
  isTransitioning = false;
292
413
  onslidechange?.(targetIndex, slides.length);
293
414
  if (targetIndex === slides.length - 1 && !loop) oncomplete?.();
@@ -310,6 +431,7 @@
310
431
  await animated.skewY.to(currentEl.skewY ?? 0, { duration: 0 });
311
432
  await animated.tiltX.to(currentEl.tiltX ?? 0, { duration: 0 });
312
433
  await animated.tiltY.to(currentEl.tiltY ?? 0, { duration: 0 });
434
+ await animated.perspective.to(currentEl.perspective ?? 1000, { duration: 0 });
313
435
  await animated.borderRadius.to((currentEl as any).borderRadius ?? 0, { duration: 0 });
314
436
  await animated.opacity.to(1, { duration: 0 });
315
437
  if (currentEl.type === 'text' && animated.fontSize) await animated.fontSize.to((currentEl as TextElement).fontSize, { duration: 0 });
@@ -336,7 +458,7 @@
336
458
  const elementDuration = animConfig?.duration ?? duration;
337
459
 
338
460
  if (targetEl) {
339
- const easing = easeInOutCubic;
461
+ const easing = getEasingFn(animConfig?.easing);
340
462
  const propertySequences = targetEl.animationConfig?.propertySequences;
341
463
  if (targetEl.type === 'text') {
342
464
  const textEl = targetEl as TextElement;
@@ -361,6 +483,7 @@
361
483
  if (!sequencedProps.has('skew')) { anims.push(animated.skewX.to(targetEl.skewX ?? 0, { duration: elementDuration, easing })); anims.push(animated.skewY.to(targetEl.skewY ?? 0, { duration: elementDuration, easing })); }
362
484
  if (!sequencedProps.has('size')) { anims.push(animated.width.to(targetEl.size.width, { duration: elementDuration, easing })); anims.push(animated.height.to(targetEl.size.height, { duration: elementDuration, easing })); }
363
485
  if (!sequencedProps.has('borderRadius')) anims.push(animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: elementDuration, easing }));
486
+ if (!sequencedProps.has('perspective')) anims.push(animated.perspective.to(targetEl.perspective ?? 1000, { duration: elementDuration, easing }));
364
487
  const sortedSeqs = [...propertySequences].sort((a, b) => a.order - b.order);
365
488
  let cumulativeDelay = 0;
366
489
  for (const seq of sortedSeqs) {
@@ -379,6 +502,7 @@
379
502
  animated.strokeColor?.to(s.strokeColor, { duration: seqDuration, easing });
380
503
  animated.strokeWidth?.to(s.strokeWidth, { duration: seqDuration, easing });
381
504
  }
505
+ else if (seq.property === 'perspective') animated.perspective.to(targetEl.perspective ?? 1000, { duration: seqDuration, easing });
382
506
  }, seqDelay);
383
507
  cumulativeDelay = seqDelay + seqDuration;
384
508
  }
@@ -393,8 +517,19 @@
393
517
  anims.push(animated.skewY.to(targetEl.skewY ?? 0, { duration: elementDuration, easing }));
394
518
  anims.push(animated.tiltX.to(targetEl.tiltX ?? 0, { duration: elementDuration, easing }));
395
519
  anims.push(animated.tiltY.to(targetEl.tiltY ?? 0, { duration: elementDuration, easing }));
520
+ anims.push(animated.perspective.to(targetEl.perspective ?? 1000, { duration: elementDuration, easing }));
396
521
  anims.push(animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: elementDuration, easing }));
397
522
  }
523
+ // Motion path progress — await reset, then animate forward
524
+ if (animated.motionPathProgress && targetEl.motionPathConfig) {
525
+ const shouldLoop = targetEl.motionPathConfig.loop;
526
+ if (!shouldLoop) {
527
+ anims.push((async () => {
528
+ await animated.motionPathProgress!.to(0, { duration: 0 });
529
+ await animated.motionPathProgress!.to(1, { duration: elementDuration, easing });
530
+ })());
531
+ }
532
+ }
398
533
  if (targetEl.type === 'text' && animated.fontSize) anims.push(animated.fontSize.to((targetEl as TextElement).fontSize, { duration: elementDuration, easing }));
399
534
  if (targetEl.type === 'shape' && currentEl?.type === 'shape') {
400
535
  const ts = targetEl as ShapeElement;
@@ -416,12 +551,25 @@
416
551
  if (animated.strokeColor) anims.push(animated.strokeColor.to(s.strokeColor, { duration: elementDuration, easing }));
417
552
  if (animated.strokeWidth) anims.push(animated.strokeWidth.to(s.strokeWidth, { duration: elementDuration, easing }));
418
553
  }
419
- if (!currentEl) anims.push(animated.opacity.to(1, { duration: elementDuration / 2, easing }));
554
+ if (!currentEl) {
555
+ const entrance = targetEl.animationConfig?.entrance ?? 'fade';
556
+ if (entrance === 'fade') {
557
+ anims.push(animated.opacity.to(1, { duration: elementDuration / 2, easing }));
558
+ } else {
559
+ anims.push(animated.opacity.to(1, { duration: 0 }));
560
+ }
561
+ }
420
562
  return anims;
421
563
  }
422
564
  });
423
565
  } else if (currentEl) {
424
- animationTasks.push({ elementId, order, delay, elementDuration, run: () => [animated.opacity.to(0, { duration: elementDuration / 2, easing: easeInOutCubic })] });
566
+ const exit = currentEl.animationConfig?.exit ?? 'fade';
567
+ if (exit === 'fade') {
568
+ const fadeOutDuration = Math.min(elementDuration / 2, 300);
569
+ animationTasks.push({ elementId, order, delay, elementDuration, run: () => [animated.opacity.to(0, { duration: fadeOutDuration, easing: easeInOutCubic })] });
570
+ } else {
571
+ animationTasks.push({ elementId, order, delay: 0, elementDuration: 0, run: () => [animated.opacity.to(0, { duration: 0 })] });
572
+ }
425
573
  }
426
574
  }
427
575
 
@@ -479,6 +627,7 @@
479
627
  elementContent = newElementContent;
480
628
  currentSlideIndex = targetIndex;
481
629
  isTransitioning = false;
630
+ animateMotionPaths(slides[targetIndex]);
482
631
  onslidechange?.(targetIndex, slides.length);
483
632
  if (targetIndex === slides.length - 1 && !loop) oncomplete?.();
484
633
  }
@@ -570,6 +719,7 @@
570
719
  initAllAnimatedElements();
571
720
  await loadCodeHighlights();
572
721
  loading = false;
722
+ if (currentSlide) setTimeout(() => animateMotionPaths(currentSlide!), 300);
573
723
  if (autoplay) isAutoplay = true;
574
724
  } catch (e: any) { error = e.message; loading = false; }
575
725
  }
@@ -632,17 +782,27 @@
632
782
  {@const floatCfg = element?.floatingAnimation}
633
783
  {@const hasFloat = floatCfg?.enabled}
634
784
  {@const floatGroupId = element?.groupId}
635
- {#if element && animated && animated.opacity.current > 0.01 && element.visible !== false}
785
+ {@const mpConfig = element?.motionPathConfig}
786
+ {@const mpElement = mpConfig ? currentSlide?.canvas.elements.find(el => el.id === mpConfig.motionPathId) as MotionPathElement | undefined : undefined}
787
+ {@const mpProgress = animated?.motionPathProgress?.current ?? 0}
788
+ {@const mpPoint = mpElement && mpConfig ? getPresenterPointOnPath(mpElement.points, mpElement.closed, (mpConfig.startPercent + (mpConfig.endPercent - mpConfig.startPercent) * mpProgress) / 100) : null}
789
+ {@const elemX = mpPoint ? mpPoint.x - (animated?.width.current ?? 0) / 2 : animated?.x.current ?? 0}
790
+ {@const elemY = mpPoint ? mpPoint.y - (animated?.height.current ?? 0) / 2 : animated?.y.current ?? 0}
791
+ {@const mpRotation = mpPoint && mpConfig?.autoRotate
792
+ ? mpPoint.angle + (mpConfig.orientationOffset ?? 0)
793
+ : null}
794
+ {#if element && animated && animated.opacity.current > 0.01 && element.visible !== false && !(element.type === 'motionPath' && !(element as MotionPathElement).showInPresentation)}
636
795
  <div
637
796
  class="animot-element"
638
797
  class:floating={hasFloat}
639
- style:left="{animated.x.current}px"
640
- style:top="{animated.y.current}px"
798
+ style:left="{elemX}px"
799
+ style:top="{elemY}px"
641
800
  style:width="{animated.width.current}px"
642
801
  style:height="{animated.height.current}px"
643
802
  style:opacity={animated.opacity.current}
644
- style:transform="perspective(1000px) rotateX({animated.tiltX.current}deg) rotateY({animated.tiltY.current}deg) rotate({animated.rotation.current}deg) skewX({animated.skewX.current}deg) skewY({animated.skewY.current}deg)"
803
+ style:transform="perspective({animated.perspective.current}px) rotateX({animated.tiltX.current}deg) rotateY({animated.tiltY.current}deg) rotate({mpRotation ?? animated.rotation.current}deg) skewX({animated.skewX.current}deg) skewY({animated.skewY.current}deg)"
645
804
  style:transform-origin={element.tiltOrigin ?? 'center'}
805
+ style:backface-visibility={element.backfaceVisibility ?? 'visible'}
646
806
  style:z-index={element.zIndex}
647
807
  style:--float-amp="{hasFloat ? computeFloatAmp(floatCfg, floatGroupId || elementId) : 10}px"
648
808
  style:--float-speed="{hasFloat ? computeFloatSpeed(floatCfg, floatGroupId || elementId) : 3}s"
@@ -722,7 +882,7 @@
722
882
  {@const arrowAnimDuration = arrowEl.animation?.duration ?? 500}
723
883
  {@const isStyledArrow = arrowEl.style !== 'solid'}
724
884
  {@const baseDashArray = arrowEl.style === 'dashed' ? '10,5' : arrowEl.style === 'dotted' ? '2,5' : 'none'}
725
- <svg class="animot-arrow-element" class:arrow-animate-draw={arrowAnimMode === 'draw' && !isStyledArrow} class:arrow-animate-grow={arrowAnimMode === 'grow'} viewBox="0 0 {arrowEl.size.width} {arrowEl.size.height}" preserveAspectRatio="none" style="overflow: visible; --arrow-anim-duration: {arrowAnimDuration}ms;">
885
+ <svg class="animot-arrow-element" class:arrow-animate-draw={arrowAnimMode === 'draw' && !isStyledArrow} class:arrow-animate-grow={arrowAnimMode === 'grow'} viewBox="0 0 {arrowEl.size.width} {arrowEl.size.height}" preserveAspectRatio="none" style="--arrow-anim-duration: {arrowAnimDuration}ms;">
726
886
  <path class="arrow-path" d={pathD} fill="none" stroke={arrowEl.color} stroke-width={arrowEl.strokeWidth} stroke-dasharray={baseDashArray} stroke-linecap="round" stroke-linejoin="round" use:animateStyledArrowDraw={{ enabled: arrowAnimMode === 'draw' && isStyledArrow, duration: arrowAnimDuration, dashPattern: baseDashArray, startX: arrowEl.startPoint.x, endX: arrowEl.endPoint.x, slideIndex: currentSlideIndex }} />
727
887
  {#if arrowEl.showHead !== false}
728
888
  <path class="arrow-head" class:arrow-head-styled-draw={arrowAnimMode === 'draw' && isStyledArrow} d={arrowHeadPath} fill="none" stroke={arrowEl.color} stroke-width={arrowEl.strokeWidth} stroke-linecap="round" stroke-linejoin="round" style={arrowAnimMode === 'draw' && isStyledArrow ? `--arrow-anim-duration: ${arrowAnimDuration}ms;` : ''} />
@@ -731,7 +891,7 @@
731
891
  {:else if element.type === 'image'}
732
892
  {@const imgEl = element as ImageElement}
733
893
  {@const clipPath = imgEl.clipMask?.enabled ? (imgEl.clipMask.shapeType === 'circle' ? 'circle(50% at 50% 50%)' : imgEl.clipMask.shapeType === 'ellipse' ? 'ellipse(50% 50% at 50% 50%)' : imgEl.clipMask.shapeType === 'triangle' ? 'polygon(50% 0%, 0% 100%, 100% 100%)' : imgEl.clipMask.shapeType === 'star' ? 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)' : imgEl.clipMask.shapeType === 'hexagon' ? 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)' : (imgEl.clipMask.borderRadius ?? 0) > 0 ? `inset(0 round ${imgEl.clipMask.borderRadius}px)` : 'none') : 'none'}
734
- <img class="animot-image-element" src={imgEl.src} alt="" style:object-fit={imgEl.objectFit} style:border-radius="{imgEl.clipMask?.enabled ? 0 : imgEl.borderRadius}px" style:opacity={imgEl.opacity} style:filter={imgEl.blur ? `blur(${imgEl.blur}px)` : 'none'} style:clip-path={clipPath} />
894
+ <img class="animot-image-element" src={imgEl.src} alt="" style:object-fit={imgEl.objectFit} style:border-radius="{imgEl.clipMask?.enabled ? 0 : imgEl.borderRadius}px" style:opacity={imgEl.opacity} style:filter={imgEl.blur ? `blur(${imgEl.blur}px)` : 'none'} style:clip-path={clipPath} style:background-color={imgEl.backgroundColor ?? 'transparent'} />
735
895
  {:else if element.type === 'shape'}
736
896
  {@const shapeEl = element as ShapeElement}
737
897
  {@const animFill = animated.fillColor?.current ?? shapeEl.fillColor}
@@ -758,6 +918,23 @@
758
918
  <ChartRenderer element={element as ChartElement} slideId={currentSlide?.id ?? ''} />
759
919
  {:else if element.type === 'icon'}
760
920
  <IconRenderer element={element as IconElement} />
921
+ {:else if element.type === 'svg'}
922
+ {@const svgEl = element as SvgElement}
923
+ {@const svgInner = (() => { const m = svgEl.svgContent.trim().match(/^<svg[^>]*>([\s\S]*)<\/svg>$/i); return m ? m[1] : svgEl.svgContent; })()}
924
+ <div class="animot-svg-element" style:opacity={svgEl.opacity}>
925
+ <svg width="100%" height="100%" viewBox="0 0 {svgEl.size.width} {svgEl.size.height}" preserveAspectRatio={svgEl.preserveAspectRatio} xmlns="http://www.w3.org/2000/svg">
926
+ <g style={svgEl.color ? `fill:${svgEl.color};stroke:${svgEl.color}` : ''}>
927
+ {@html svgInner}
928
+ </g>
929
+ </svg>
930
+ </div>
931
+ {:else if element.type === 'motionPath'}
932
+ {@const mpEl = element as MotionPathElement}
933
+ {#if mpEl.showInPresentation}
934
+ <svg width="100%" height="100%" style="position:absolute;top:0;left:0;pointer-events:none;overflow:visible;">
935
+ <path d={buildPresenterPathD(mpEl.points, mpEl.closed)} stroke={mpEl.pathColor} stroke-width={mpEl.pathWidth} fill="none" stroke-dasharray="8 4" />
936
+ </svg>
937
+ {/if}
761
938
  {/if}
762
939
  </div>
763
940
  {/if}
@@ -848,6 +1025,7 @@
848
1025
  position: absolute;
849
1026
  box-sizing: border-box;
850
1027
  will-change: transform, opacity, left, top, width, height;
1028
+ isolation: isolate;
851
1029
  }
852
1030
 
853
1031
  .animot-element.floating {
@@ -929,6 +1107,20 @@
929
1107
  @keyframes animot-zoomOut { from { transform: scale(1.5); opacity: 0; } to { transform: scale(1); opacity: 1; } }
930
1108
  @keyframes animot-flipIn { from { transform: perspective(1000px) rotateY(-90deg); opacity: 0; } to { transform: perspective(1000px) rotateY(0); opacity: 1; } }
931
1109
 
1110
+ /* Flip-X transition */
1111
+ .animot-canvas.transition-flip-x-out { transform: perspective(1000px) rotateX(90deg); opacity: 0; }
1112
+ .animot-canvas.transition-flip-x-in { animation: animot-flipXIn calc(var(--transition-duration) * 0.6) ease forwards; }
1113
+ @keyframes animot-flipXIn { from { transform: perspective(1000px) rotateX(-90deg); opacity: 0; } to { transform: perspective(1000px) rotateX(0); opacity: 1; } }
1114
+
1115
+ /* Flip-Y transition */
1116
+ .animot-canvas.transition-flip-y-out { transform: perspective(1000px) rotateY(90deg); opacity: 0; }
1117
+ .animot-canvas.transition-flip-y-in { animation: animot-flipYIn calc(var(--transition-duration) * 0.6) ease forwards; }
1118
+ @keyframes animot-flipYIn { from { transform: perspective(1000px) rotateY(-90deg); opacity: 0; } to { transform: perspective(1000px) rotateY(0); opacity: 1; } }
1119
+
1120
+ /* SVG element */
1121
+ .animot-svg-element { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
1122
+ .animot-svg-element :global(svg) { width: 100%; height: 100%; }
1123
+
932
1124
  /* Controls */
933
1125
  .animot-controls {
934
1126
  position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%);
@@ -1 +1 @@
1
- .code-morph.svelte-1hmk0l{width:100%;height:100%;margin:0;padding:0;background:transparent;font-family:var(--font-mono, "JetBrains Mono", monospace);font-size:inherit;line-height:1.6;white-space:pre-wrap;word-wrap:break-word;color:#e6edf3;box-sizing:border-box;overflow:visible}.code-morph.svelte-1hmk0l pre{margin:0;padding:16px;background:transparent!important;font-family:var(--font-mono);font-size:inherit;line-height:1.6;overflow:visible}.code-morph.svelte-1hmk0l code{font-family:inherit;font-size:inherit;font-weight:inherit}.code-morph.svelte-1hmk0l .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.code-morph.svelte-1hmk0l .word-highlight{background:var(--highlight-color, #facc15);color:#000;border-radius:3px;padding:1px 3px;margin:0 -3px;-webkit-box-decoration-break:clone;box-decoration-break:clone;animation:svelte-1hmk0l-fadeHighlight var(--duration, 1s) ease-out forwards}@keyframes svelte-1hmk0l-fadeHighlight{0%{background:var(--highlight-color, #facc15);color:#000}70%{background:var(--highlight-color, #facc15);color:#000}to{background:transparent;color:inherit}}.counter.svelte-1er5jjj{width:100%;height:100%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;overflow:hidden;white-space:nowrap}.chart.svelte-1mq2d7j{width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}.chart-title.svelte-1mq2d7j{font-weight:600;text-align:center;margin-bottom:8px}.chart-content.svelte-1mq2d7j{flex:1;min-height:0}.chart-content.svelte-1mq2d7j svg:where(.svelte-1mq2d7j){width:100%;height:100%}.bar-chart.svelte-1mq2d7j,.line-chart.svelte-1mq2d7j{overflow:visible}.icon-element.svelte-2ld65o{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.icon-element.svelte-2ld65o svg{width:100%;height:100%}@keyframes float-vertical{0%,to{translate:0 0}50%{translate:0 calc(-1 * var(--float-amp, 10px))}}@keyframes float-horizontal{0%,to{translate:0 0}50%{translate:var(--float-amp, 10px) 0}}@keyframes float-both-0{0%{translate:0 0}15%{translate:calc(.8 * var(--float-amp, 10px)) calc(-.6 * var(--float-amp, 10px))}35%{translate:calc(-.4 * var(--float-amp, 10px)) calc(-1 * var(--float-amp, 10px))}55%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.3 * var(--float-amp, 10px))}75%{translate:calc(.3 * var(--float-amp, 10px)) calc(.7 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-1{0%{translate:0 0}20%{translate:calc(-.7 * var(--float-amp, 10px)) calc(-.8 * var(--float-amp, 10px))}40%{translate:calc(.5 * var(--float-amp, 10px)) calc(-.3 * var(--float-amp, 10px))}60%{translate:calc(.9 * var(--float-amp, 10px)) calc(.6 * var(--float-amp, 10px))}80%{translate:calc(-.4 * var(--float-amp, 10px)) calc(.9 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-2{0%{translate:0 0}12%{translate:calc(.6 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}30%{translate:calc(1 * var(--float-amp, 10px)) calc(-.4 * var(--float-amp, 10px))}50%{translate:calc(-.3 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}70%{translate:calc(-.8 * var(--float-amp, 10px)) calc(.2 * var(--float-amp, 10px))}88%{translate:calc(.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-3{0%{translate:0 0}17%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.4 * var(--float-amp, 10px))}33%{translate:calc(-.5 * var(--float-amp, 10px)) calc(-.7 * var(--float-amp, 10px))}50%{translate:calc(.7 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}67%{translate:calc(.9 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}83%{translate:calc(-.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}.animot-presenter.svelte-16ocdv9{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;background:transparent}.animot-canvas-wrapper.svelte-16ocdv9{display:flex;align-items:center;justify-content:center}.animot-canvas.svelte-16ocdv9{position:relative;overflow:hidden}.animot-element.svelte-16ocdv9{position:absolute;box-sizing:border-box;will-change:transform,opacity,left,top,width,height}.animot-element.floating.svelte-16ocdv9{animation-duration:var(--float-speed, 3s);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-delay:var(--float-delay, 0s)}.animot-code-block.svelte-16ocdv9{width:100%;height:100%;background:#0d1117;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006;margin:0;box-sizing:border-box}.animot-code-block.transparent-bg.svelte-16ocdv9{background:transparent;box-shadow:none}.animot-code-block.transparent-bg.svelte-16ocdv9 .animot-code-header:where(.svelte-16ocdv9){background:transparent;border-bottom-color:#ffffff1a}.animot-code-header.svelte-16ocdv9{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#161b22;border-bottom:1px solid #30363d;flex-shrink:0}.animot-window-controls.svelte-16ocdv9{display:flex;gap:8px}.animot-control.svelte-16ocdv9{width:12px;height:12px;border-radius:50%;display:block}.macos.svelte-16ocdv9 .animot-control.close:where(.svelte-16ocdv9){background:#ff5f57}.macos.svelte-16ocdv9 .animot-control.minimize:where(.svelte-16ocdv9){background:#febc2e}.macos.svelte-16ocdv9 .animot-control.maximize:where(.svelte-16ocdv9){background:#28c840}.animot-filename.svelte-16ocdv9{color:#8b949e;font-size:14px;flex:1}.animot-code-content.svelte-16ocdv9{flex:1;overflow:hidden;position:relative}.animot-highlighted-code.svelte-16ocdv9{width:100%;height:100%}.animot-code-content.svelte-16ocdv9 pre,.animot-highlighted-code.svelte-16ocdv9 pre{margin:0;padding:16px;background:transparent!important;line-height:1.6;font-size:inherit;overflow:visible}.animot-highlighted-code.svelte-16ocdv9 code{font-family:inherit;font-size:inherit;font-weight:inherit}.animot-highlighted-code.svelte-16ocdv9 .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.animot-text-element.svelte-16ocdv9{width:100%;height:100%;display:flex;align-items:center;white-space:pre-wrap;word-wrap:break-word}.animot-typewriter-cursor.svelte-16ocdv9{animation:svelte-16ocdv9-animot-blink .7s infinite;font-weight:100}@keyframes svelte-16ocdv9-animot-blink{0%,50%{opacity:1}51%,to{opacity:0}}.animot-arrow-element.svelte-16ocdv9{width:100%;height:100%}.arrow-animate-draw.svelte-16ocdv9 .arrow-path:where(.svelte-16ocdv9){stroke-dasharray:1000;stroke-dashoffset:1000;animation:svelte-16ocdv9-animot-arrow-draw var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-head-styled-draw.svelte-16ocdv9{opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-draw.svelte-16ocdv9 .arrow-head:where(.svelte-16ocdv9){opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-grow.svelte-16ocdv9{transform-origin:left center;animation:svelte-16ocdv9-animot-arrow-grow var(--arrow-anim-duration, .5s) ease-out forwards}@keyframes svelte-16ocdv9-animot-arrow-draw{to{stroke-dashoffset:0}}@keyframes svelte-16ocdv9-animot-arrow-head-appear{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-arrow-grow{0%{transform:scaleX(0);opacity:0}to{transform:scaleX(1);opacity:1}}.animot-image-element.svelte-16ocdv9{width:100%;height:100%;display:block}.animot-shape-element.svelte-16ocdv9{width:100%;height:100%;display:block;overflow:visible}.animot-canvas.svelte-16ocdv9{--transition-duration: .5s;transition:transform calc(var(--transition-duration) * .4) ease,opacity calc(var(--transition-duration) * .4) ease}.animot-canvas.transition-fade-out.svelte-16ocdv9{opacity:0}.animot-canvas.transition-fade-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-fadeIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.forward.svelte-16ocdv9{transform:translate(-100%);opacity:0}.animot-canvas.transition-slide-left-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromRight calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.backward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-left-in.backward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-right-out.forward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-right-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-up-out.svelte-16ocdv9{transform:translateY(-100%);opacity:0}.animot-canvas.transition-slide-up-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromBottom calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-down-out.svelte-16ocdv9{transform:translateY(100%);opacity:0}.animot-canvas.transition-slide-down-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromTop calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-in-out.svelte-16ocdv9{transform:scale(.5);opacity:0}.animot-canvas.transition-zoom-in-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-out-out.svelte-16ocdv9{transform:scale(1.5);opacity:0}.animot-canvas.transition-zoom-out-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomOut calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-flip-out.svelte-16ocdv9{transform:perspective(1000px) rotateY(90deg);opacity:0}.animot-canvas.transition-flip-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromBottom{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromTop{0%{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomIn{0%{transform:scale(.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomOut{0%{transform:scale(1.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-flipIn{0%{transform:perspective(1000px) rotateY(-90deg);opacity:0}to{transform:perspective(1000px) rotateY(0);opacity:1}}.animot-controls.svelte-16ocdv9{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:8px;padding:8px 16px;background:#000000b3;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-radius:10px;opacity:0;transition:opacity .3s ease .15s;z-index:100}.animot-presenter.svelte-16ocdv9:hover .animot-controls:where(.svelte-16ocdv9),.animot-menu-visible.svelte-16ocdv9 .animot-controls:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9){display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;border:none;cursor:pointer;background:#ffffff1a;color:#fff;transition:background .2s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):hover:not(:disabled){background:#fff3}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-controls.svelte-16ocdv9 button.active:where(.svelte-16ocdv9){background:#6366f199}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9) svg:where(.svelte-16ocdv9){width:16px;height:16px}.animot-slide-indicator.svelte-16ocdv9{font-size:12px;color:#fff;min-width:50px;text-align:center;font-family:system-ui,sans-serif}.animot-arrow.svelte-16ocdv9{position:absolute;top:50%;transform:translateY(-50%);width:40px;height:40px;border-radius:50%;border:none;cursor:pointer;background:#00000080;color:#fff;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .3s .15s;z-index:100;padding:0;margin:0}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-arrow.svelte-16ocdv9:hover:not(:disabled){background:#000000b3}.animot-arrow.svelte-16ocdv9 svg:where(.svelte-16ocdv9){width:20px;height:20px}.animot-arrow-left.svelte-16ocdv9{left:8px}.animot-arrow-right.svelte-16ocdv9{right:8px}.animot-progress-bar.svelte-16ocdv9{position:absolute;bottom:0;left:0;right:0;height:3px;background:#ffffff1a;z-index:100;opacity:0;transition:opacity .3s .15s}.animot-presenter.svelte-16ocdv9:hover .animot-progress-bar:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-progress-fill.svelte-16ocdv9{height:100%;background:linear-gradient(135deg,#7c3aed,#ec4899);transition:width .6s ease}.animot-loading.svelte-16ocdv9{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.animot-spinner.svelte-16ocdv9{width:32px;height:32px;border:3px solid rgba(255,255,255,.2);border-top-color:#7c3aed;border-radius:50%;animation:svelte-16ocdv9-animot-spin .8s linear infinite}@keyframes svelte-16ocdv9-animot-spin{to{transform:rotate(360deg)}}.animot-error.svelte-16ocdv9{color:#ef4444;padding:20px;text-align:center;font-family:system-ui,sans-serif}
1
+ .code-morph.svelte-1hmk0l{width:100%;height:100%;margin:0;padding:0;background:transparent;font-family:var(--font-mono, "JetBrains Mono", monospace);font-size:inherit;line-height:1.6;white-space:pre-wrap;word-wrap:break-word;color:#e6edf3;box-sizing:border-box;overflow:visible}.code-morph.svelte-1hmk0l pre{margin:0;padding:16px;background:transparent!important;font-family:var(--font-mono);font-size:inherit;line-height:1.6;overflow:visible}.code-morph.svelte-1hmk0l code{font-family:inherit;font-size:inherit;font-weight:inherit}.code-morph.svelte-1hmk0l .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.code-morph.svelte-1hmk0l .word-highlight{background:var(--highlight-color, #facc15);color:#000;border-radius:3px;padding:1px 3px;margin:0 -3px;-webkit-box-decoration-break:clone;box-decoration-break:clone;animation:svelte-1hmk0l-fadeHighlight var(--duration, 1s) ease-out forwards}@keyframes svelte-1hmk0l-fadeHighlight{0%{background:var(--highlight-color, #facc15);color:#000}70%{background:var(--highlight-color, #facc15);color:#000}to{background:transparent;color:inherit}}.counter.svelte-1er5jjj{width:100%;height:100%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;overflow:hidden;white-space:nowrap}.chart.svelte-1mq2d7j{width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}.chart-title.svelte-1mq2d7j{font-weight:600;text-align:center;margin-bottom:8px}.chart-content.svelte-1mq2d7j{flex:1;min-height:0}.chart-content.svelte-1mq2d7j svg:where(.svelte-1mq2d7j){width:100%;height:100%}.bar-chart.svelte-1mq2d7j,.line-chart.svelte-1mq2d7j{overflow:visible}.icon-element.svelte-2ld65o{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.icon-element.svelte-2ld65o svg{width:100%;height:100%}@keyframes float-vertical{0%,to{translate:0 0}50%{translate:0 calc(-1 * var(--float-amp, 10px))}}@keyframes float-horizontal{0%,to{translate:0 0}50%{translate:var(--float-amp, 10px) 0}}@keyframes float-both-0{0%{translate:0 0}15%{translate:calc(.8 * var(--float-amp, 10px)) calc(-.6 * var(--float-amp, 10px))}35%{translate:calc(-.4 * var(--float-amp, 10px)) calc(-1 * var(--float-amp, 10px))}55%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.3 * var(--float-amp, 10px))}75%{translate:calc(.3 * var(--float-amp, 10px)) calc(.7 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-1{0%{translate:0 0}20%{translate:calc(-.7 * var(--float-amp, 10px)) calc(-.8 * var(--float-amp, 10px))}40%{translate:calc(.5 * var(--float-amp, 10px)) calc(-.3 * var(--float-amp, 10px))}60%{translate:calc(.9 * var(--float-amp, 10px)) calc(.6 * var(--float-amp, 10px))}80%{translate:calc(-.4 * var(--float-amp, 10px)) calc(.9 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-2{0%{translate:0 0}12%{translate:calc(.6 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}30%{translate:calc(1 * var(--float-amp, 10px)) calc(-.4 * var(--float-amp, 10px))}50%{translate:calc(-.3 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}70%{translate:calc(-.8 * var(--float-amp, 10px)) calc(.2 * var(--float-amp, 10px))}88%{translate:calc(.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-3{0%{translate:0 0}17%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.4 * var(--float-amp, 10px))}33%{translate:calc(-.5 * var(--float-amp, 10px)) calc(-.7 * var(--float-amp, 10px))}50%{translate:calc(.7 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}67%{translate:calc(.9 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}83%{translate:calc(-.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}.animot-presenter.svelte-16ocdv9{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;background:transparent}.animot-canvas-wrapper.svelte-16ocdv9{display:flex;align-items:center;justify-content:center}.animot-canvas.svelte-16ocdv9{position:relative;overflow:hidden}.animot-element.svelte-16ocdv9{position:absolute;box-sizing:border-box;will-change:transform,opacity,left,top,width,height;isolation:isolate}.animot-element.floating.svelte-16ocdv9{animation-duration:var(--float-speed, 3s);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-delay:var(--float-delay, 0s)}.animot-code-block.svelte-16ocdv9{width:100%;height:100%;background:#0d1117;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006;margin:0;box-sizing:border-box}.animot-code-block.transparent-bg.svelte-16ocdv9{background:transparent;box-shadow:none}.animot-code-block.transparent-bg.svelte-16ocdv9 .animot-code-header:where(.svelte-16ocdv9){background:transparent;border-bottom-color:#ffffff1a}.animot-code-header.svelte-16ocdv9{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#161b22;border-bottom:1px solid #30363d;flex-shrink:0}.animot-window-controls.svelte-16ocdv9{display:flex;gap:8px}.animot-control.svelte-16ocdv9{width:12px;height:12px;border-radius:50%;display:block}.macos.svelte-16ocdv9 .animot-control.close:where(.svelte-16ocdv9){background:#ff5f57}.macos.svelte-16ocdv9 .animot-control.minimize:where(.svelte-16ocdv9){background:#febc2e}.macos.svelte-16ocdv9 .animot-control.maximize:where(.svelte-16ocdv9){background:#28c840}.animot-filename.svelte-16ocdv9{color:#8b949e;font-size:14px;flex:1}.animot-code-content.svelte-16ocdv9{flex:1;overflow:hidden;position:relative}.animot-highlighted-code.svelte-16ocdv9{width:100%;height:100%}.animot-code-content.svelte-16ocdv9 pre,.animot-highlighted-code.svelte-16ocdv9 pre{margin:0;padding:16px;background:transparent!important;line-height:1.6;font-size:inherit;overflow:visible}.animot-highlighted-code.svelte-16ocdv9 code{font-family:inherit;font-size:inherit;font-weight:inherit}.animot-highlighted-code.svelte-16ocdv9 .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.animot-text-element.svelte-16ocdv9{width:100%;height:100%;display:flex;align-items:center;white-space:pre-wrap;word-wrap:break-word}.animot-typewriter-cursor.svelte-16ocdv9{animation:svelte-16ocdv9-animot-blink .7s infinite;font-weight:100}@keyframes svelte-16ocdv9-animot-blink{0%,50%{opacity:1}51%,to{opacity:0}}.animot-arrow-element.svelte-16ocdv9{width:100%;height:100%}.arrow-animate-draw.svelte-16ocdv9 .arrow-path:where(.svelte-16ocdv9){stroke-dasharray:1000;stroke-dashoffset:1000;animation:svelte-16ocdv9-animot-arrow-draw var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-head-styled-draw.svelte-16ocdv9{opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-draw.svelte-16ocdv9 .arrow-head:where(.svelte-16ocdv9){opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-grow.svelte-16ocdv9{transform-origin:left center;animation:svelte-16ocdv9-animot-arrow-grow var(--arrow-anim-duration, .5s) ease-out forwards}@keyframes svelte-16ocdv9-animot-arrow-draw{to{stroke-dashoffset:0}}@keyframes svelte-16ocdv9-animot-arrow-head-appear{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-arrow-grow{0%{transform:scaleX(0);opacity:0}to{transform:scaleX(1);opacity:1}}.animot-image-element.svelte-16ocdv9{width:100%;height:100%;display:block}.animot-shape-element.svelte-16ocdv9{width:100%;height:100%;display:block;overflow:visible}.animot-canvas.svelte-16ocdv9{--transition-duration: .5s;transition:transform calc(var(--transition-duration) * .4) ease,opacity calc(var(--transition-duration) * .4) ease}.animot-canvas.transition-fade-out.svelte-16ocdv9{opacity:0}.animot-canvas.transition-fade-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-fadeIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.forward.svelte-16ocdv9{transform:translate(-100%);opacity:0}.animot-canvas.transition-slide-left-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromRight calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.backward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-left-in.backward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-right-out.forward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-right-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-up-out.svelte-16ocdv9{transform:translateY(-100%);opacity:0}.animot-canvas.transition-slide-up-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromBottom calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-down-out.svelte-16ocdv9{transform:translateY(100%);opacity:0}.animot-canvas.transition-slide-down-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromTop calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-in-out.svelte-16ocdv9{transform:scale(.5);opacity:0}.animot-canvas.transition-zoom-in-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-out-out.svelte-16ocdv9{transform:scale(1.5);opacity:0}.animot-canvas.transition-zoom-out-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomOut calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-flip-out.svelte-16ocdv9{transform:perspective(1000px) rotateY(90deg);opacity:0}.animot-canvas.transition-flip-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromBottom{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromTop{0%{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomIn{0%{transform:scale(.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomOut{0%{transform:scale(1.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-flipIn{0%{transform:perspective(1000px) rotateY(-90deg);opacity:0}to{transform:perspective(1000px) rotateY(0);opacity:1}}.animot-canvas.transition-flip-x-out.svelte-16ocdv9{transform:perspective(1000px) rotateX(90deg);opacity:0}.animot-canvas.transition-flip-x-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipXIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-flipXIn{0%{transform:perspective(1000px) rotateX(-90deg);opacity:0}to{transform:perspective(1000px) rotateX(0);opacity:1}}.animot-canvas.transition-flip-y-out.svelte-16ocdv9{transform:perspective(1000px) rotateY(90deg);opacity:0}.animot-canvas.transition-flip-y-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipYIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-flipYIn{0%{transform:perspective(1000px) rotateY(-90deg);opacity:0}to{transform:perspective(1000px) rotateY(0);opacity:1}}.animot-svg-element.svelte-16ocdv9{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.animot-svg-element.svelte-16ocdv9 svg{width:100%;height:100%}.animot-controls.svelte-16ocdv9{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:8px;padding:8px 16px;background:#000000b3;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-radius:10px;opacity:0;transition:opacity .3s ease .15s;z-index:100}.animot-presenter.svelte-16ocdv9:hover .animot-controls:where(.svelte-16ocdv9),.animot-menu-visible.svelte-16ocdv9 .animot-controls:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9){display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;border:none;cursor:pointer;background:#ffffff1a;color:#fff;transition:background .2s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):hover:not(:disabled){background:#fff3}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-controls.svelte-16ocdv9 button.active:where(.svelte-16ocdv9){background:#6366f199}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9) svg:where(.svelte-16ocdv9){width:16px;height:16px}.animot-slide-indicator.svelte-16ocdv9{font-size:12px;color:#fff;min-width:50px;text-align:center;font-family:system-ui,sans-serif}.animot-arrow.svelte-16ocdv9{position:absolute;top:50%;transform:translateY(-50%);width:40px;height:40px;border-radius:50%;border:none;cursor:pointer;background:#00000080;color:#fff;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .3s .15s;z-index:100;padding:0;margin:0}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-arrow.svelte-16ocdv9:hover:not(:disabled){background:#000000b3}.animot-arrow.svelte-16ocdv9 svg:where(.svelte-16ocdv9){width:20px;height:20px}.animot-arrow-left.svelte-16ocdv9{left:8px}.animot-arrow-right.svelte-16ocdv9{right:8px}.animot-progress-bar.svelte-16ocdv9{position:absolute;bottom:0;left:0;right:0;height:3px;background:#ffffff1a;z-index:100;opacity:0;transition:opacity .3s .15s}.animot-presenter.svelte-16ocdv9:hover .animot-progress-bar:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-progress-fill.svelte-16ocdv9{height:100%;background:linear-gradient(135deg,#7c3aed,#ec4899);transition:width .6s ease}.animot-loading.svelte-16ocdv9{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.animot-spinner.svelte-16ocdv9{width:32px;height:32px;border:3px solid rgba(255,255,255,.2);border-top-color:#7c3aed;border-radius:50%;animation:svelte-16ocdv9-animot-spin .8s linear infinite}@keyframes svelte-16ocdv9-animot-spin{to{transform:rotate(360deg)}}.animot-error.svelte-16ocdv9{color:#ef4444;padding:20px;text-align:center;font-family:system-ui,sans-serif}