@almadar/ui 6.51.2 → 6.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/{EntityBindingContext-BbxrwSb9.d.ts → AwaitingSkeletonContext-Bh399p1x.d.ts} +17 -2
  2. package/dist/{Canvas3DHost-CLC_jXh_.d.ts → Canvas3DHost-B-y2TNij.d.ts} +1 -1
  3. package/dist/{FileTree-RKQqRhIM.d.ts → FileTree-DUi7e7Z8.d.ts} +5 -5
  4. package/dist/{GameAudioProvider-BgWLggIy.d.ts → GameAudioProvider-DyBvYP2c.d.ts} +1 -1
  5. package/dist/{NavStackContext-B-XLULCO.d.ts → NavStackContext-04NLdXsY.d.ts} +8 -0
  6. package/dist/Typography-JGSDH4JP.js +5 -0
  7. package/dist/{UISlotContext-DNM8iXna.d.ts → UISlotContext-CS487Uoc.d.ts} +1 -1
  8. package/dist/{UISlotRenderer-7UYHFW4S.js → UISlotRenderer-JT34I3O7.js} +9 -9
  9. package/dist/avl/index.cjs +39958 -38525
  10. package/dist/avl/index.d.ts +8 -5
  11. package/dist/avl/index.js +37 -26
  12. package/dist/{chunk-JXUYEO7U.js → chunk-2LFBQ4IS.js} +7 -1
  13. package/dist/{chunk-GIPZSCND.js → chunk-3EF5SDIJ.js} +31157 -31206
  14. package/dist/{chunk-GHXWGQTX.js → chunk-4OZDHCZV.js} +1 -1
  15. package/dist/{chunk-HQDKHLFG.js → chunk-64WACXM7.js} +15 -1
  16. package/dist/{chunk-FGHRVJCI.js → chunk-6O37FCX4.js} +13 -2
  17. package/dist/{chunk-BUEIZY54.js → chunk-7LM6I3NA.js} +8 -7
  18. package/dist/chunk-CBIJP5PL.js +284 -0
  19. package/dist/{chunk-MWPX2O4M.js → chunk-GMDMOYXD.js} +3 -16
  20. package/dist/{chunk-YKHNK5JH.js → chunk-GODZKHAL.js} +11 -4
  21. package/dist/{chunk-T7IH5ABO.js → chunk-IKULQIDX.js} +199 -88
  22. package/dist/{chunk-NTO555KF.js → chunk-JRW33U35.js} +183 -62
  23. package/dist/{chunk-OLZ3EEML.js → chunk-KQXXO42S.js} +106 -90
  24. package/dist/chunk-OKWD7LPO.js +80 -0
  25. package/dist/{chunk-YHY5K2Y7.js → chunk-PEST66PN.js} +6 -0
  26. package/dist/{chunk-L55SAFKL.js → chunk-UGHAADU5.js} +77 -1
  27. package/dist/{chunk-6CFYRP4S.js → chunk-VHTJRS2N.js} +4 -8
  28. package/dist/{chunk-R4V7IZQP.js → chunk-WPNM2KC6.js} +2 -2
  29. package/dist/{cn-CO-0rolG.d.ts → cn-ClXGlIOY.d.ts} +2 -2
  30. package/dist/components/index.cjs +46084 -44766
  31. package/dist/components/index.d.ts +645 -699
  32. package/dist/components/index.js +621 -13
  33. package/dist/context/index.cjs +20 -7
  34. package/dist/context/index.d.ts +3 -3
  35. package/dist/context/index.js +3 -3
  36. package/dist/hooks/index.cjs +126 -9
  37. package/dist/hooks/index.d.ts +32 -7
  38. package/dist/hooks/index.js +7 -7
  39. package/dist/lib/drawable/three/index.cjs +79 -2
  40. package/dist/lib/drawable/three/index.d.ts +3 -3
  41. package/dist/lib/drawable/three/index.js +7 -7
  42. package/dist/lib/index.cjs +116 -2
  43. package/dist/lib/index.d.ts +29 -6
  44. package/dist/lib/index.js +6 -5
  45. package/dist/locales/index.cjs +231 -3
  46. package/dist/locales/index.js +155 -3
  47. package/dist/{paintDispatch-CMgOyVjS.d.ts → paintDispatch-_6EhJCiL.d.ts} +6 -6
  48. package/dist/providers/index.cjs +36404 -35028
  49. package/dist/providers/index.d.ts +34 -9
  50. package/dist/providers/index.js +82 -19
  51. package/dist/renderer/index.cjs +6 -0
  52. package/dist/renderer/index.d.ts +1 -1
  53. package/dist/renderer/index.js +1 -1
  54. package/dist/runtime/index.cjs +37861 -36437
  55. package/dist/runtime/index.d.ts +6 -4
  56. package/dist/runtime/index.js +11 -11
  57. package/dist/ssr/index.cjs +584 -181
  58. package/dist/ssr/index.d.cts +36 -44
  59. package/dist/ssr/index.d.ts +36 -44
  60. package/dist/ssr/index.js +527 -124
  61. package/dist/{themeTokens-CN9WCXQc.d.ts → themeTokens-Ccu7EmGY.d.ts} +4 -2
  62. package/dist/{useUISlots-CwUWM1ra.d.ts → useUISlots-1dk2TR4j.d.ts} +1 -9
  63. package/dist/{verificationRegistry-DBHfOR9y.d.ts → verificationRegistry-CD7Xb40L.d.ts} +1 -1
  64. package/locales/ar.json +77 -1
  65. package/locales/en.json +77 -1
  66. package/locales/sl.json +77 -1
  67. package/package.json +13 -10
  68. package/tailwind-preset.cjs +37 -1
  69. package/themes/_base.css +114 -8
  70. package/themes/_contract.md +6 -0
  71. package/themes/almadar-website.css +2 -0
  72. package/themes/almadar.css +2 -0
  73. package/themes/aqua.css +5 -3
  74. package/themes/arabesque.css +9 -7
  75. package/themes/art-deco.css +5 -3
  76. package/themes/art-nouveau.css +4 -0
  77. package/themes/atelier.css +10 -8
  78. package/themes/bauhaus.css +2 -0
  79. package/themes/bloomberg-dense.css +13 -11
  80. package/themes/blueprint.css +2 -0
  81. package/themes/clay.css +2 -0
  82. package/themes/comic.css +12 -10
  83. package/themes/constructivist.css +2 -0
  84. package/themes/corporate.css +4 -2
  85. package/themes/cyberpunk.css +2 -0
  86. package/themes/frutiger-aero.css +2 -0
  87. package/themes/game-adventure.css +1 -0
  88. package/themes/game-rpg.css +1 -0
  89. package/themes/game-sci-fi.css +1 -0
  90. package/themes/game-ui-pack.css +1 -0
  91. package/themes/gazette.css +10 -8
  92. package/themes/glass.css +17 -15
  93. package/themes/gothic.css +2 -0
  94. package/themes/ink-wash.css +2 -0
  95. package/themes/kawaii.css +2 -0
  96. package/themes/kiosk.css +17 -15
  97. package/themes/linear-clean.css +12 -10
  98. package/themes/luxury.css +2 -0
  99. package/themes/mac-classic.css +2 -0
  100. package/themes/material.css +2 -0
  101. package/themes/memphis.css +2 -0
  102. package/themes/mid-century.css +2 -0
  103. package/themes/minimalist.css +2 -0
  104. package/themes/mudcloth.css +2 -0
  105. package/themes/neon.css +2 -0
  106. package/themes/neumorphic.css +2 -0
  107. package/themes/newsprint.css +2 -0
  108. package/themes/notion-editorial.css +12 -10
  109. package/themes/pixel.css +2 -0
  110. package/themes/prism.css +20 -18
  111. package/themes/retro.css +2 -0
  112. package/themes/risograph.css +2 -0
  113. package/themes/scandi.css +2 -0
  114. package/themes/sketch.css +2 -0
  115. package/themes/solarpunk.css +2 -0
  116. package/themes/steampunk.css +2 -0
  117. package/themes/swiss.css +2 -0
  118. package/themes/terminal.css +8 -6
  119. package/themes/trait-wars.css +2 -0
  120. package/themes/ukiyo-e.css +2 -0
  121. package/themes/vaporwave.css +2 -0
  122. package/themes/victorian.css +2 -0
  123. package/themes/wabi-sabi.css +2 -0
  124. package/themes/win95.css +2 -0
  125. package/themes/wireframe.css +2 -0
  126. package/themes/zine.css +2 -0
  127. package/dist/Typography-2Z3JKWHN.js +0 -5
@@ -1,5 +1,5 @@
1
1
  import { useEventBus } from './chunk-6EV3OEBQ.js';
2
- import { useCallback, useSyncExternalStore, useRef, useEffect, useState, useMemo } from 'react';
2
+ import { createContext, useCallback, useSyncExternalStore, useRef, useState, useContext, useEffect, useMemo } from 'react';
3
3
 
4
4
  function useMediaQuery(query) {
5
5
  const subscribe = useCallback(
@@ -20,6 +20,122 @@ function useMediaQuery(query) {
20
20
  function hasMatchMedia() {
21
21
  return typeof window !== "undefined" && typeof window.matchMedia === "function";
22
22
  }
23
+ var counts = /* @__PURE__ */ new Map();
24
+ var keyListeners = /* @__PURE__ */ new Map();
25
+ var seq = 0;
26
+ function notify(key) {
27
+ keyListeners.get(key)?.forEach((cb) => cb());
28
+ }
29
+ function newPendingKey() {
30
+ seq += 1;
31
+ return `pending-${seq}`;
32
+ }
33
+ function beginPending(key) {
34
+ counts.set(key, (counts.get(key) ?? 0) + 1);
35
+ if (counts.get(key) === 1) notify(key);
36
+ }
37
+ function endPending(key) {
38
+ const n = (counts.get(key) ?? 0) - 1;
39
+ if (n > 0) {
40
+ counts.set(key, n);
41
+ return;
42
+ }
43
+ counts.delete(key);
44
+ notify(key);
45
+ }
46
+ function isPending(key) {
47
+ return (counts.get(key) ?? 0) > 0;
48
+ }
49
+ function subscribePending(key, cb) {
50
+ let set = keyListeners.get(key);
51
+ if (!set) {
52
+ set = /* @__PURE__ */ new Set();
53
+ keyListeners.set(key, set);
54
+ }
55
+ set.add(cb);
56
+ return () => {
57
+ set?.delete(cb);
58
+ if (set && set.size === 0) keyListeners.delete(key);
59
+ };
60
+ }
61
+ var PendingScopeContext = createContext(null);
62
+ function usePendingScopeValue() {
63
+ const keys = useRef(/* @__PURE__ */ new Set());
64
+ const listeners = useRef(/* @__PURE__ */ new Set());
65
+ const emitChange = useCallback(() => listeners.current.forEach((cb) => cb()), []);
66
+ return useState(() => ({
67
+ track: (key) => {
68
+ if (!isPending(key)) return;
69
+ keys.current.add(key);
70
+ emitChange();
71
+ const unsub = subscribePending(key, () => {
72
+ if (isPending(key)) return;
73
+ keys.current.delete(key);
74
+ unsub();
75
+ emitChange();
76
+ });
77
+ },
78
+ hasPending: () => keys.current.size > 0,
79
+ subscribe: (cb) => {
80
+ listeners.current.add(cb);
81
+ return () => listeners.current.delete(cb);
82
+ }
83
+ }))[0];
84
+ }
85
+ function useScopeHasPending(scope) {
86
+ return useSyncExternalStore(
87
+ useCallback((cb) => scope ? scope.subscribe(cb) : () => {
88
+ }, [scope]),
89
+ () => scope ? scope.hasPending() : false,
90
+ () => false
91
+ );
92
+ }
93
+ function usePendingAction() {
94
+ const scope = useContext(PendingScopeContext);
95
+ const [key, setKey] = useState(null);
96
+ const pending = useSyncExternalStore(
97
+ useCallback((cb) => key ? subscribePending(key, cb) : () => {
98
+ }, [key]),
99
+ () => key ? isPending(key) : false,
100
+ () => false
101
+ );
102
+ useEffect(() => {
103
+ if (key && !pending) setKey(null);
104
+ }, [key, pending]);
105
+ const activate = useCallback((emit) => {
106
+ const k = newPendingKey();
107
+ emit(k);
108
+ if (!isPending(k)) return;
109
+ scope?.track(k);
110
+ setKey(k);
111
+ }, [scope]);
112
+ return { pending, activate };
113
+ }
114
+ function usePendingRows() {
115
+ const [rows, setRows] = useState(/* @__PURE__ */ new Map());
116
+ const unsubs = useRef(/* @__PURE__ */ new Map());
117
+ useEffect(() => () => unsubs.current.forEach((u) => u()), []);
118
+ const activateRow = useCallback((rowId, emit) => {
119
+ const k = newPendingKey();
120
+ emit(k);
121
+ if (!isPending(k)) return;
122
+ setRows((prev) => new Map(prev).set(rowId, k));
123
+ unsubs.current.get(rowId)?.();
124
+ unsubs.current.set(rowId, subscribePending(k, () => {
125
+ if (isPending(k)) return;
126
+ unsubs.current.get(rowId)?.();
127
+ unsubs.current.delete(rowId);
128
+ setRows((prev) => {
129
+ if (prev.get(rowId) !== k) return prev;
130
+ const next = new Map(prev);
131
+ next.delete(rowId);
132
+ return next;
133
+ });
134
+ }));
135
+ }, []);
136
+ const isRowPending = useCallback((rowId) => rows.has(rowId), [rows]);
137
+ return { isRowPending, activateRow };
138
+ }
23
139
  function useInfiniteScroll(onLoadMore, options = {}) {
24
140
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
25
141
  const observerRef = useRef(null);
@@ -50,6 +166,11 @@ function useInfiniteScroll(onLoadMore, options = {}) {
50
166
  }, [rootMargin]);
51
167
  return { sentinelRef };
52
168
  }
169
+
170
+ // hooks/usePrefersReducedMotion.ts
171
+ function usePrefersReducedMotion() {
172
+ return useMediaQuery("(prefers-reduced-motion: reduce)");
173
+ }
53
174
  function useSwipeGesture(callbacks, options = {}) {
54
175
  const { threshold = 50, velocityThreshold = 0.3, preventDefault = false } = options;
55
176
  const startX = useRef(0);
@@ -428,6 +549,66 @@ function parseQueryBinding(binding) {
428
549
  field: parts.length > 1 ? parts.slice(1).join(".") : void 0
429
550
  };
430
551
  }
552
+ function usePullToRefresh(onRefresh, options = {}) {
553
+ const { threshold = 60, maxPull = 120 } = options;
554
+ const [pullDistance, setPullDistance] = useState(0);
555
+ const [isPulling, setIsPulling] = useState(false);
556
+ const [isRefreshing, setIsRefreshing] = useState(false);
557
+ const startY = useRef(0);
558
+ const scrollTopRef = useRef(0);
559
+ const onTouchStart = useCallback((e) => {
560
+ const container = e.currentTarget;
561
+ scrollTopRef.current = container.scrollTop;
562
+ if (scrollTopRef.current <= 0) {
563
+ startY.current = e.touches[0].clientY;
564
+ setIsPulling(true);
565
+ }
566
+ }, []);
567
+ const onTouchMove = useCallback((e) => {
568
+ if (!isPulling || isRefreshing) return;
569
+ const container = e.currentTarget;
570
+ if (container.scrollTop > 0) {
571
+ setPullDistance(0);
572
+ return;
573
+ }
574
+ const dy = e.touches[0].clientY - startY.current;
575
+ if (dy > 0) {
576
+ const distance = Math.min(dy * 0.5, maxPull);
577
+ setPullDistance(distance);
578
+ }
579
+ }, [isPulling, isRefreshing, maxPull]);
580
+ const onTouchEnd = useCallback(() => {
581
+ if (!isPulling) return;
582
+ setIsPulling(false);
583
+ if (pullDistance >= threshold && !isRefreshing) {
584
+ setIsRefreshing(true);
585
+ setPullDistance(threshold);
586
+ onRefresh();
587
+ } else {
588
+ setPullDistance(0);
589
+ }
590
+ }, [isPulling, pullDistance, threshold, isRefreshing, onRefresh]);
591
+ const endRefresh = useCallback(() => {
592
+ setIsRefreshing(false);
593
+ setPullDistance(0);
594
+ }, []);
595
+ const containerProps = {
596
+ onTouchStart,
597
+ onTouchMove,
598
+ onTouchEnd,
599
+ style: {
600
+ transform: pullDistance > 0 ? `translateY(${pullDistance}px)` : void 0,
601
+ transition: isPulling ? "none" : "transform 300ms ease-out"
602
+ }
603
+ };
604
+ return {
605
+ pullDistance,
606
+ isPulling,
607
+ isRefreshing,
608
+ containerProps,
609
+ endRefresh
610
+ };
611
+ }
431
612
  function useDragReorder(initialItems, onReorder) {
432
613
  const [items, setItems] = useState(initialItems);
433
614
  const [dragIndex, setDragIndex] = useState(-1);
@@ -496,65 +677,5 @@ function useDragReorder(initialItems, onReorder) {
496
677
  getItemProps
497
678
  };
498
679
  }
499
- function usePullToRefresh(onRefresh, options = {}) {
500
- const { threshold = 60, maxPull = 120 } = options;
501
- const [pullDistance, setPullDistance] = useState(0);
502
- const [isPulling, setIsPulling] = useState(false);
503
- const [isRefreshing, setIsRefreshing] = useState(false);
504
- const startY = useRef(0);
505
- const scrollTopRef = useRef(0);
506
- const onTouchStart = useCallback((e) => {
507
- const container = e.currentTarget;
508
- scrollTopRef.current = container.scrollTop;
509
- if (scrollTopRef.current <= 0) {
510
- startY.current = e.touches[0].clientY;
511
- setIsPulling(true);
512
- }
513
- }, []);
514
- const onTouchMove = useCallback((e) => {
515
- if (!isPulling || isRefreshing) return;
516
- const container = e.currentTarget;
517
- if (container.scrollTop > 0) {
518
- setPullDistance(0);
519
- return;
520
- }
521
- const dy = e.touches[0].clientY - startY.current;
522
- if (dy > 0) {
523
- const distance = Math.min(dy * 0.5, maxPull);
524
- setPullDistance(distance);
525
- }
526
- }, [isPulling, isRefreshing, maxPull]);
527
- const onTouchEnd = useCallback(() => {
528
- if (!isPulling) return;
529
- setIsPulling(false);
530
- if (pullDistance >= threshold && !isRefreshing) {
531
- setIsRefreshing(true);
532
- setPullDistance(threshold);
533
- onRefresh();
534
- } else {
535
- setPullDistance(0);
536
- }
537
- }, [isPulling, pullDistance, threshold, isRefreshing, onRefresh]);
538
- const endRefresh = useCallback(() => {
539
- setIsRefreshing(false);
540
- setPullDistance(0);
541
- }, []);
542
- const containerProps = {
543
- onTouchStart,
544
- onTouchMove,
545
- onTouchEnd,
546
- style: {
547
- transform: pullDistance > 0 ? `translateY(${pullDistance}px)` : void 0,
548
- transition: isPulling ? "none" : "transform 300ms ease-out"
549
- }
550
- };
551
- return {
552
- pullDistance,
553
- isPulling,
554
- isRefreshing,
555
- containerProps,
556
- endRefresh
557
- };
558
- }
559
680
 
560
- export { ALMADAR_DND_MIME, parseQueryBinding, useAuthContext, useCanvasGestures, useDragReorder, useDraggable, useDropZone, useInfiniteScroll, useLongPress, useMediaQuery, usePullToRefresh, useQuerySingleton, useSwipeGesture };
681
+ export { ALMADAR_DND_MIME, PendingScopeContext, beginPending, endPending, parseQueryBinding, useAuthContext, useCanvasGestures, useDragReorder, useDraggable, useDropZone, useInfiniteScroll, useLongPress, useMediaQuery, usePendingAction, usePendingRows, usePendingScopeValue, usePrefersReducedMotion, usePullToRefresh, useQuerySingleton, useScopeHasPending, useSwipeGesture };
@@ -1,8 +1,10 @@
1
- import { useSyncExternalStore } from 'react';
1
+ import { domPassthrough, cn } from './chunk-64WACXM7.js';
2
+ import { useState, useRef, useCallback, useLayoutEffect, useEffect, useSyncExternalStore } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { ENTER_DELAY_STEPS, ENTER_ANIMATIONS, isFileValue } from '@almadar/core';
5
+ export { getNestedValue } from '@almadar/core';
2
6
  import { perfStore, pushPerfEntry } from '@almadar/runtime/ui';
3
7
  export { PERF_NAMESPACE, clearPerf, perfEnd, perfGauge, perfStart, perfTime, perfTimeAsync } from '@almadar/runtime/ui';
4
- import { isFileValue } from '@almadar/core';
5
- export { getNestedValue } from '@almadar/core';
6
8
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
7
9
 
8
10
  // lib/jazari/svg-paths.ts
@@ -85,17 +87,97 @@ function eightPointedStarPath(cx, cy, outerR, innerR) {
85
87
  function arrowheadPath(size) {
86
88
  return `M 0 0 L ${size} ${size / 2} L 0 ${size} Z`;
87
89
  }
88
- function usePerfBuffer() {
89
- return useSyncExternalStore(perfStore.subscribe, perfStore.getSnapshot, perfStore.getSnapshot);
90
+ var SAFE_EXIT_MS = 1e3;
91
+ var motionEnabledCache = null;
92
+ function isMotionEnabled() {
93
+ if (typeof document === "undefined") return true;
94
+ if (motionEnabledCache !== null) return motionEnabledCache;
95
+ const v = getComputedStyle(document.documentElement).getPropertyValue("--motion-enable").trim().toLowerCase();
96
+ motionEnabledCache = v !== "off";
97
+ return motionEnabledCache;
98
+ }
99
+ function usePresence(show, opts) {
100
+ const { animation, animate = true, onExited } = opts;
101
+ const [mounted, setMounted] = useState(show);
102
+ const [exiting, setExiting] = useState(false);
103
+ const prev = useRef(show);
104
+ const onExitedRef = useRef(onExited);
105
+ onExitedRef.current = onExited;
106
+ const safeTimer = useRef(null);
107
+ const clearSafe = useCallback(() => {
108
+ if (safeTimer.current) {
109
+ clearTimeout(safeTimer.current);
110
+ safeTimer.current = null;
111
+ }
112
+ }, []);
113
+ const finishExit = useCallback(() => {
114
+ clearSafe();
115
+ setExiting(false);
116
+ setMounted(false);
117
+ onExitedRef.current?.();
118
+ }, [clearSafe]);
119
+ useLayoutEffect(() => {
120
+ const moving = animate && isMotionEnabled();
121
+ if (show && !prev.current) {
122
+ setExiting(false);
123
+ setMounted(true);
124
+ } else if (!show && prev.current) {
125
+ if (moving) {
126
+ setExiting(true);
127
+ clearSafe();
128
+ safeTimer.current = setTimeout(finishExit, SAFE_EXIT_MS);
129
+ } else {
130
+ setMounted(false);
131
+ setExiting(false);
132
+ onExitedRef.current?.();
133
+ }
134
+ }
135
+ prev.current = show;
136
+ }, [show, animate, clearSafe, finishExit]);
137
+ useEffect(() => () => clearSafe(), [clearSafe]);
138
+ const disabled = !animate || !isMotionEnabled();
139
+ const className = disabled ? "" : show ? `animate-${animation}-in` : `animate-${animation}-out`;
140
+ const onAnimationEnd = useCallback(
141
+ (e) => {
142
+ if (e.target !== e.currentTarget) return;
143
+ if (exiting) finishExit();
144
+ },
145
+ [exiting, finishExit]
146
+ );
147
+ return { mounted, exiting, className, onAnimationEnd };
90
148
  }
91
- var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
92
- pushPerfEntry({
93
- name: `profiler:${id}:${phase}`,
94
- durationMs: actualDuration,
95
- ts: commitTime,
96
- detail: { baseDuration }
97
- });
149
+ var Presence = ({ show, className, children, ...rest }) => {
150
+ const { mounted, className: animClass, onAnimationEnd } = usePresence(show, rest);
151
+ if (!mounted) return null;
152
+ return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), onAnimationEnd, ...domPassthrough(rest), children });
98
153
  };
154
+ Presence.displayName = "Presence";
155
+ var ENTER_SLOT_CLASS = "almadar-enter-slot";
156
+ function asEnterAnimation(value) {
157
+ return ENTER_ANIMATIONS.find((e) => e === value);
158
+ }
159
+ var MAX_ENTER_DELAY = ENTER_DELAY_STEPS.length;
160
+ function enterClassName(enter, enterDelay) {
161
+ if (enter === void 0 || enter === "none" || !isMotionEnabled()) return void 0;
162
+ const base = `almadar-enter-${enter}`;
163
+ if (enterDelay === void 0 || !Number.isInteger(enterDelay) || enterDelay < 1 || enterDelay > MAX_ENTER_DELAY) return base;
164
+ return `${base} almadar-enter-delay-${enterDelay}`;
165
+ }
166
+ function slotPlaysDefaultEnter(rootEnter) {
167
+ return rootEnter === void 0 && isMotionEnabled();
168
+ }
169
+ function useSlotEnterRestart(ref, contentKey, active) {
170
+ const lastKey = useRef(contentKey);
171
+ useLayoutEffect(() => {
172
+ if (lastKey.current === contentKey) return;
173
+ lastKey.current = contentKey;
174
+ const el = ref.current;
175
+ if (!el || !active) return;
176
+ el.classList.remove(ENTER_SLOT_CLASS);
177
+ void el.offsetWidth;
178
+ el.classList.add(ENTER_SLOT_CLASS);
179
+ }, [contentKey, active, ref]);
180
+ }
99
181
 
100
182
  // lib/editorMotions.ts
101
183
  var EDITOR_MOTIONS = [
@@ -579,6 +661,17 @@ function middleSnake(a, aLo, aHi, b, bLo, bHi) {
579
661
  }
580
662
  throw new Error("computeLineDiff: no middle snake \u2014 unreachable for differing texts");
581
663
  }
664
+ function usePerfBuffer() {
665
+ return useSyncExternalStore(perfStore.subscribe, perfStore.getSnapshot, perfStore.getSnapshot);
666
+ }
667
+ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
668
+ pushPerfEntry({
669
+ name: `profiler:${id}:${phase}`,
670
+ durationMs: actualDuration,
671
+ ts: commitTime,
672
+ detail: { baseDuration }
673
+ });
674
+ };
582
675
 
583
676
  // lib/lessonSegmentUtils.ts
584
677
  function parseMarkdownWithCodeBlocks(content) {
@@ -766,83 +859,6 @@ function toLogMetaValue(v) {
766
859
  return String(v);
767
860
  }
768
861
 
769
- // lib/parseLessonSegments.ts
770
- function extractTagContent(content, tagName) {
771
- const closedTagRegex = new RegExp(`<${tagName}>([\\s\\S]*?)<\\/${tagName}>`, "i");
772
- const closedMatch = content.match(closedTagRegex);
773
- if (closedMatch) {
774
- return { content: closedMatch[1].trim(), fullMatch: closedMatch[0] };
775
- }
776
- const unclosedTagRegex = new RegExp(
777
- `<${tagName}>([\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$)`,
778
- "i"
779
- );
780
- const unclosedMatch = content.match(unclosedTagRegex);
781
- if (unclosedMatch) {
782
- return { content: unclosedMatch[1].trim(), fullMatch: unclosedMatch[0] };
783
- }
784
- return null;
785
- }
786
- function parseLessonSegments(lesson) {
787
- if (!lesson) return [];
788
- let content = lesson.replace(/<prq>[\s\S]*?<\/prq>/gi, "").trim();
789
- const segments = [];
790
- const activateResult = extractTagContent(content, "activate");
791
- if (activateResult) {
792
- segments.push({ type: "activate", question: activateResult.content });
793
- content = content.replace(activateResult.fullMatch, "").trim();
794
- }
795
- const connectResult = extractTagContent(content, "connect");
796
- if (connectResult) {
797
- segments.push({ type: "connect", content: connectResult.content });
798
- content = content.replace(connectResult.fullMatch, "").trim();
799
- }
800
- const tagRegex = new RegExp(
801
- '(?<reflect><reflect>(?<reflectClosed>[\\s\\S]*?)<\\/reflect>)|(?<reflectUnclosed><reflect>(?<reflectOpen>[\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$))|(?<bloom><bloom\\s+level="(?<bloomLevel>remember|understand|apply|analyze|evaluate|create)">(?<bloomClosed>[\\s\\S]*?)<\\/bloom>)|(?<bloomUnclosed><bloom\\s+level="(?<bloomLevelUn>remember|understand|apply|analyze|evaluate|create)">(?<bloomOpen>[\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$))|(?<quiz><question>(?<quizQuestion>[\\s\\S]*?)<\\/question>\\s*<answer>(?<quizAnswer>[\\s\\S]*?)<\\/answer>)|(?<visualize><visualize\\s+type="(?<vizType>algorithms|math|physics|biology|chemistry|probability)"\\s+description="(?<vizDesc>[^"]*?)"\\s*\\/?>)',
802
- "gi"
803
- );
804
- let lastIndex = 0;
805
- let match;
806
- while ((match = tagRegex.exec(content)) !== null) {
807
- const before = content.slice(lastIndex, match.index);
808
- if (before.trim()) {
809
- segments.push(...parseMarkdownWithCodeBlocks(before));
810
- }
811
- const g = match.groups ?? {};
812
- if (g.reflect || g.reflectUnclosed) {
813
- const prompt = (g.reflectClosed ?? g.reflectOpen ?? "").trim();
814
- if (prompt) segments.push({ type: "reflect", prompt });
815
- } else if (g.bloom || g.bloomUnclosed) {
816
- const level = g.bloomLevel ?? g.bloomLevelUn;
817
- const bloomContent = g.bloomClosed ?? g.bloomOpen ?? "";
818
- if (level && bloomContent) {
819
- const qMatch = bloomContent.match(/<question>([\s\S]*?)<\/question>/i);
820
- const aMatch = bloomContent.match(/<answer>([\s\S]*?)<\/answer>/i);
821
- if (qMatch && aMatch) {
822
- segments.push({ type: "bloom", level, question: qMatch[1].trim(), answer: aMatch[1].trim() });
823
- } else if (qMatch) {
824
- segments.push({ type: "bloom", level, question: qMatch[1].trim(), answer: "(Answer not provided)" });
825
- } else {
826
- const clean = bloomContent.replace(/^\*\*Question\s*\d*:?\*\*\s*/i, "").replace(/^\*\*Q\d*:?\*\*\s*/i, "").trim();
827
- if (clean) segments.push({ type: "bloom", level, question: clean, answer: "(See answers section below)" });
828
- }
829
- }
830
- } else if (g.quiz) {
831
- segments.push({ type: "quiz", question: g.quizQuestion.trim(), answer: g.quizAnswer.trim() });
832
- } else if (g.visualize) {
833
- segments.push({
834
- type: "visualization",
835
- visualizationType: g.vizType,
836
- description: g.vizDesc ?? ""
837
- });
838
- }
839
- lastIndex = tagRegex.lastIndex;
840
- }
841
- const remaining = content.slice(lastIndex);
842
- if (remaining.trim()) segments.push(...parseMarkdownWithCodeBlocks(remaining));
843
- return segments;
844
- }
845
-
846
862
  // lib/traitRegistry.ts
847
863
  var traits = /* @__PURE__ */ new Map();
848
864
  var listeners = /* @__PURE__ */ new Set();
@@ -1102,4 +1118,4 @@ function initDebugShortcut() {
1102
1118
  return () => window.removeEventListener("keydown", handleKeyDown);
1103
1119
  }
1104
1120
 
1105
- export { EDITOR_MOTIONS, EDITOR_OPERATORS, INDENT_UNIT, applyMotion, applyOperator, arrowheadPath, brainIconPath, clearDebugEvents, clearEntityProvider, clearGuardHistory, clearTicks, clearTraits, computeLineDiff, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, eightPointedStarPath, gearTeethPath, getAllTicks, getAllTraits, getDebugEvents, getEntitiesByType, getEntityById, getEntitySnapshot, getEventsBySource, getEventsByType, getGuardEvaluationsForTrait, getGuardHistory, getRecentEvents, getRecentGuardEvaluations, getTick, getTrait, initDebugShortcut, isDebugEnabled, isDebugEnabled2, isEditorMotion, isEditorOperator, lockIconPath, logDebugEvent, logEffectExecuted, logError, logEventFired, logInfo, logStateChange, logWarning, motionRange, onDebugToggle, parseContentSegments, parseLessonSegments, parseMarkdownWithCodeBlocks, parseMarkdownWithCodeBlocks2, pipeIconPath, profilerOnRender, recordGuardEvaluation, registerTick, registerTrait, resolveImageUrl, setDebugEnabled, setEntityProvider, setTickActive, subscribeToDebugEvents, subscribeToGuardChanges, subscribeToTickChanges, subscribeToTraitChanges, toggleDebug, unregisterTick, unregisterTrait, updateGuardResult, updateTickExecution, updateTraitState, usePerfBuffer };
1121
+ export { EDITOR_MOTIONS, EDITOR_OPERATORS, ENTER_SLOT_CLASS, INDENT_UNIT, MAX_ENTER_DELAY, Presence, applyMotion, applyOperator, arrowheadPath, asEnterAnimation, brainIconPath, clearDebugEvents, clearEntityProvider, clearGuardHistory, clearTicks, clearTraits, computeLineDiff, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, eightPointedStarPath, enterClassName, gearTeethPath, getAllTicks, getAllTraits, getDebugEvents, getEntitiesByType, getEntityById, getEntitySnapshot, getEventsBySource, getEventsByType, getGuardEvaluationsForTrait, getGuardHistory, getRecentEvents, getRecentGuardEvaluations, getTick, getTrait, initDebugShortcut, isDebugEnabled, isDebugEnabled2, isEditorMotion, isEditorOperator, isMotionEnabled, lockIconPath, logDebugEvent, logEffectExecuted, logError, logEventFired, logInfo, logStateChange, logWarning, motionRange, onDebugToggle, parseContentSegments, parseMarkdownWithCodeBlocks, parseMarkdownWithCodeBlocks2, pipeIconPath, profilerOnRender, recordGuardEvaluation, registerTick, registerTrait, resolveImageUrl, setDebugEnabled, setEntityProvider, setTickActive, slotPlaysDefaultEnter, subscribeToDebugEvents, subscribeToGuardChanges, subscribeToTickChanges, subscribeToTraitChanges, toggleDebug, unregisterTick, unregisterTrait, updateGuardResult, updateTickExecution, updateTraitState, usePerfBuffer, usePresence, useSlotEnterRestart };
@@ -0,0 +1,80 @@
1
+ import { parseMarkdownWithCodeBlocks } from './chunk-KQXXO42S.js';
2
+
3
+ // lib/parseLessonSegments.ts
4
+ function extractTagContent(content, tagName) {
5
+ const closedTagRegex = new RegExp(`<${tagName}>([\\s\\S]*?)<\\/${tagName}>`, "i");
6
+ const closedMatch = content.match(closedTagRegex);
7
+ if (closedMatch) {
8
+ return { content: closedMatch[1].trim(), fullMatch: closedMatch[0] };
9
+ }
10
+ const unclosedTagRegex = new RegExp(
11
+ `<${tagName}>([\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$)`,
12
+ "i"
13
+ );
14
+ const unclosedMatch = content.match(unclosedTagRegex);
15
+ if (unclosedMatch) {
16
+ return { content: unclosedMatch[1].trim(), fullMatch: unclosedMatch[0] };
17
+ }
18
+ return null;
19
+ }
20
+ function parseLessonSegments(lesson) {
21
+ if (!lesson) return [];
22
+ let content = lesson.replace(/<prq>[\s\S]*?<\/prq>/gi, "").trim();
23
+ const segments = [];
24
+ const activateResult = extractTagContent(content, "activate");
25
+ if (activateResult) {
26
+ segments.push({ type: "activate", question: activateResult.content });
27
+ content = content.replace(activateResult.fullMatch, "").trim();
28
+ }
29
+ const connectResult = extractTagContent(content, "connect");
30
+ if (connectResult) {
31
+ segments.push({ type: "connect", content: connectResult.content });
32
+ content = content.replace(connectResult.fullMatch, "").trim();
33
+ }
34
+ const tagRegex = new RegExp(
35
+ '(?<reflect><reflect>(?<reflectClosed>[\\s\\S]*?)<\\/reflect>)|(?<reflectUnclosed><reflect>(?<reflectOpen>[\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$))|(?<bloom><bloom\\s+level="(?<bloomLevel>remember|understand|apply|analyze|evaluate|create)">(?<bloomClosed>[\\s\\S]*?)<\\/bloom>)|(?<bloomUnclosed><bloom\\s+level="(?<bloomLevelUn>remember|understand|apply|analyze|evaluate|create)">(?<bloomOpen>[\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$))|(?<quiz><question>(?<quizQuestion>[\\s\\S]*?)<\\/question>\\s*<answer>(?<quizAnswer>[\\s\\S]*?)<\\/answer>)|(?<visualize><visualize\\s+type="(?<vizType>algorithms|math|physics|biology|chemistry|probability)"\\s+description="(?<vizDesc>[^"]*?)"\\s*\\/?>)',
36
+ "gi"
37
+ );
38
+ let lastIndex = 0;
39
+ let match;
40
+ while ((match = tagRegex.exec(content)) !== null) {
41
+ const before = content.slice(lastIndex, match.index);
42
+ if (before.trim()) {
43
+ segments.push(...parseMarkdownWithCodeBlocks(before));
44
+ }
45
+ const g = match.groups ?? {};
46
+ if (g.reflect || g.reflectUnclosed) {
47
+ const prompt = (g.reflectClosed ?? g.reflectOpen ?? "").trim();
48
+ if (prompt) segments.push({ type: "reflect", prompt });
49
+ } else if (g.bloom || g.bloomUnclosed) {
50
+ const level = g.bloomLevel ?? g.bloomLevelUn;
51
+ const bloomContent = g.bloomClosed ?? g.bloomOpen ?? "";
52
+ if (level && bloomContent) {
53
+ const qMatch = bloomContent.match(/<question>([\s\S]*?)<\/question>/i);
54
+ const aMatch = bloomContent.match(/<answer>([\s\S]*?)<\/answer>/i);
55
+ if (qMatch && aMatch) {
56
+ segments.push({ type: "bloom", level, question: qMatch[1].trim(), answer: aMatch[1].trim() });
57
+ } else if (qMatch) {
58
+ segments.push({ type: "bloom", level, question: qMatch[1].trim(), answer: "(Answer not provided)" });
59
+ } else {
60
+ const clean = bloomContent.replace(/^\*\*Question\s*\d*:?\*\*\s*/i, "").replace(/^\*\*Q\d*:?\*\*\s*/i, "").trim();
61
+ if (clean) segments.push({ type: "bloom", level, question: clean, answer: "(See answers section below)" });
62
+ }
63
+ }
64
+ } else if (g.quiz) {
65
+ segments.push({ type: "quiz", question: g.quizQuestion.trim(), answer: g.quizAnswer.trim() });
66
+ } else if (g.visualize) {
67
+ segments.push({
68
+ type: "visualization",
69
+ visualizationType: g.vizType,
70
+ description: g.vizDesc ?? ""
71
+ });
72
+ }
73
+ lastIndex = tagRegex.lastIndex;
74
+ }
75
+ const remaining = content.slice(lastIndex);
76
+ if (remaining.trim()) segments.push(...parseMarkdownWithCodeBlocks(remaining));
77
+ return segments;
78
+ }
79
+
80
+ export { parseLessonSegments };
@@ -11,6 +11,12 @@ var SLOT_DEFINITIONS = {
11
11
  name: "sidebar",
12
12
  type: "inline"
13
13
  },
14
+ dock: {
15
+ name: "dock",
16
+ type: "portal",
17
+ portalTarget: "body",
18
+ zIndex: 850
19
+ },
14
20
  // -------------------------------------------------------------------------
15
21
  // Portal Slots - Render to document.body via React Portal
16
22
  // -------------------------------------------------------------------------