domet 1.0.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.
@@ -0,0 +1,488 @@
1
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
2
+ const DEFAULT_VISIBILITY_THRESHOLD = 0.6;
3
+ const DEFAULT_HYSTERESIS_MARGIN = 150;
4
+ const SCROLL_IDLE_MS = 100;
5
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
6
+ export function useDomet(sectionIds, containerRef = null, options = {}) {
7
+ const { offset = 0, offsetRatio = 0.08, debounceMs = 10, visibilityThreshold = DEFAULT_VISIBILITY_THRESHOLD, hysteresisMargin = DEFAULT_HYSTERESIS_MARGIN, behavior = "auto", onActiveChange, onSectionEnter, onSectionLeave, onScrollStart, onScrollEnd, } = options;
8
+ const _sectionIdsKey = JSON.stringify(sectionIds);
9
+ const stableSectionIds = useMemo(() => sectionIds, [sectionIds]);
10
+ const sectionIndexMap = useMemo(() => {
11
+ const map = new Map();
12
+ stableSectionIds.forEach((id, i) => map.set(id, i));
13
+ return map;
14
+ }, [stableSectionIds]);
15
+ const [activeId, setActiveId] = useState(stableSectionIds[0] || null);
16
+ const [scroll, setScroll] = useState({
17
+ y: 0,
18
+ progress: 0,
19
+ direction: null,
20
+ velocity: 0,
21
+ isScrolling: false,
22
+ maxScroll: 0,
23
+ viewportHeight: 0,
24
+ offset: 0,
25
+ });
26
+ const [sections, setSections] = useState({});
27
+ const [containerElement, setContainerElement] = useState(null);
28
+ const refs = useRef({});
29
+ const refCallbacks = useRef({});
30
+ const activeIdRef = useRef(stableSectionIds[0] || null);
31
+ const lastScrollY = useRef(0);
32
+ const lastScrollTime = useRef(Date.now());
33
+ const rafId = useRef(null);
34
+ const isThrottled = useRef(false);
35
+ const throttleTimeoutId = useRef(null);
36
+ const hasPendingScroll = useRef(false);
37
+ const isProgrammaticScrolling = useRef(false);
38
+ const programmaticScrollTimeoutId = useRef(null);
39
+ const isScrollingRef = useRef(false);
40
+ const scrollIdleTimeoutRef = useRef(null);
41
+ const prevSectionsInViewport = useRef(new Set());
42
+ const recalculateRef = useRef(() => { });
43
+ const scrollCleanupRef = useRef(null);
44
+ const callbackRefs = useRef({
45
+ onActiveChange,
46
+ onSectionEnter,
47
+ onSectionLeave,
48
+ onScrollStart,
49
+ onScrollEnd,
50
+ });
51
+ callbackRefs.current = {
52
+ onActiveChange,
53
+ onSectionEnter,
54
+ onSectionLeave,
55
+ onScrollStart,
56
+ onScrollEnd,
57
+ };
58
+ const getEffectiveOffset = useCallback(() => {
59
+ return offset;
60
+ }, [offset]);
61
+ const getScrollBehavior = useCallback(() => {
62
+ if (behavior === "auto") {
63
+ if (typeof window === "undefined")
64
+ return "instant";
65
+ const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
66
+ return prefersReducedMotion ? "instant" : "smooth";
67
+ }
68
+ return behavior;
69
+ }, [behavior]);
70
+ useIsomorphicLayoutEffect(() => {
71
+ const nextContainer = containerRef?.current ?? null;
72
+ if (nextContainer !== containerElement) {
73
+ setContainerElement(nextContainer);
74
+ }
75
+ }, [containerRef, containerElement]);
76
+ const registerRef = useCallback((id) => {
77
+ const existing = refCallbacks.current[id];
78
+ if (existing)
79
+ return existing;
80
+ const callback = (el) => {
81
+ if (el) {
82
+ refs.current[id] = el;
83
+ }
84
+ else {
85
+ delete refs.current[id];
86
+ }
87
+ };
88
+ refCallbacks.current[id] = callback;
89
+ return callback;
90
+ }, []);
91
+ const scrollToSection = useCallback((id) => {
92
+ if (!stableSectionIds.includes(id)) {
93
+ if (process.env.NODE_ENV !== "production") {
94
+ console.warn(`[domet] scrollToSection: id "${id}" not in sectionIds`);
95
+ }
96
+ return;
97
+ }
98
+ const element = refs.current[id];
99
+ if (!element)
100
+ return;
101
+ if (programmaticScrollTimeoutId.current) {
102
+ clearTimeout(programmaticScrollTimeoutId.current);
103
+ }
104
+ scrollCleanupRef.current?.();
105
+ isProgrammaticScrolling.current = true;
106
+ activeIdRef.current = id;
107
+ setActiveId(id);
108
+ const container = containerElement;
109
+ const elementRect = element.getBoundingClientRect();
110
+ const effectiveOffset = getEffectiveOffset() + 10;
111
+ const scrollTarget = container || window;
112
+ const unlockScroll = () => {
113
+ isProgrammaticScrolling.current = false;
114
+ if (programmaticScrollTimeoutId.current) {
115
+ clearTimeout(programmaticScrollTimeoutId.current);
116
+ programmaticScrollTimeoutId.current = null;
117
+ }
118
+ requestAnimationFrame(() => {
119
+ recalculateRef.current();
120
+ });
121
+ };
122
+ let debounceTimer = null;
123
+ let isUnlocked = false;
124
+ const cleanup = () => {
125
+ if (debounceTimer) {
126
+ clearTimeout(debounceTimer);
127
+ debounceTimer = null;
128
+ }
129
+ scrollTarget.removeEventListener("scroll", handleScrollActivity);
130
+ if ("onscrollend" in scrollTarget) {
131
+ scrollTarget.removeEventListener("scrollend", handleScrollEnd);
132
+ }
133
+ scrollCleanupRef.current = null;
134
+ };
135
+ const doUnlock = () => {
136
+ if (isUnlocked)
137
+ return;
138
+ isUnlocked = true;
139
+ cleanup();
140
+ unlockScroll();
141
+ };
142
+ const resetDebounce = () => {
143
+ if (debounceTimer) {
144
+ clearTimeout(debounceTimer);
145
+ }
146
+ debounceTimer = setTimeout(doUnlock, SCROLL_IDLE_MS);
147
+ };
148
+ const handleScrollActivity = () => {
149
+ resetDebounce();
150
+ };
151
+ const handleScrollEnd = () => {
152
+ doUnlock();
153
+ };
154
+ scrollTarget.addEventListener("scroll", handleScrollActivity, {
155
+ passive: true,
156
+ });
157
+ if ("onscrollend" in scrollTarget) {
158
+ scrollTarget.addEventListener("scrollend", handleScrollEnd, {
159
+ once: true,
160
+ });
161
+ }
162
+ scrollCleanupRef.current = cleanup;
163
+ const scrollBehavior = getScrollBehavior();
164
+ if (container) {
165
+ const containerRect = container.getBoundingClientRect();
166
+ const relativeTop = elementRect.top - containerRect.top + container.scrollTop;
167
+ container.scrollTo({
168
+ top: relativeTop - effectiveOffset,
169
+ behavior: scrollBehavior,
170
+ });
171
+ }
172
+ else {
173
+ const absoluteTop = elementRect.top + window.scrollY;
174
+ window.scrollTo({
175
+ top: absoluteTop - effectiveOffset,
176
+ behavior: scrollBehavior,
177
+ });
178
+ }
179
+ if (scrollBehavior === "instant") {
180
+ doUnlock();
181
+ }
182
+ else {
183
+ resetDebounce();
184
+ }
185
+ }, [stableSectionIds, containerElement, getEffectiveOffset, getScrollBehavior]);
186
+ const sectionProps = useCallback((id) => ({
187
+ id,
188
+ ref: registerRef(id),
189
+ "data-domet": id,
190
+ }), [registerRef]);
191
+ const navProps = useCallback((id) => ({
192
+ onClick: () => scrollToSection(id),
193
+ "aria-current": activeId === id ? "page" : undefined,
194
+ "data-active": activeId === id,
195
+ }), [activeId, scrollToSection]);
196
+ useEffect(() => {
197
+ const idsSet = new Set(stableSectionIds);
198
+ for (const id of Object.keys(refs.current)) {
199
+ if (!idsSet.has(id)) {
200
+ delete refs.current[id];
201
+ }
202
+ }
203
+ for (const id of Object.keys(refCallbacks.current)) {
204
+ if (!idsSet.has(id)) {
205
+ delete refCallbacks.current[id];
206
+ }
207
+ }
208
+ const currentActive = activeIdRef.current;
209
+ const nextActive = currentActive && idsSet.has(currentActive)
210
+ ? currentActive
211
+ : (stableSectionIds[0] ?? null);
212
+ if (nextActive !== currentActive) {
213
+ activeIdRef.current = nextActive;
214
+ }
215
+ setActiveId((prev) => (prev !== nextActive ? nextActive : prev));
216
+ }, [stableSectionIds]);
217
+ const getSectionBounds = useCallback(() => {
218
+ const container = containerElement;
219
+ const scrollTop = container ? container.scrollTop : window.scrollY;
220
+ const containerTop = container ? container.getBoundingClientRect().top : 0;
221
+ return stableSectionIds
222
+ .map((id) => {
223
+ const el = refs.current[id];
224
+ if (!el)
225
+ return null;
226
+ const rect = el.getBoundingClientRect();
227
+ const relativeTop = container
228
+ ? rect.top - containerTop + scrollTop
229
+ : rect.top + window.scrollY;
230
+ return {
231
+ id,
232
+ top: relativeTop,
233
+ bottom: relativeTop + rect.height,
234
+ height: rect.height,
235
+ };
236
+ })
237
+ .filter((bounds) => bounds !== null);
238
+ }, [stableSectionIds, containerElement]);
239
+ const calculateActiveSection = useCallback(() => {
240
+ if (isProgrammaticScrolling.current)
241
+ return;
242
+ const container = containerElement;
243
+ const currentActiveId = activeIdRef.current;
244
+ const now = Date.now();
245
+ const scrollY = container ? container.scrollTop : window.scrollY;
246
+ const viewportHeight = container
247
+ ? container.clientHeight
248
+ : window.innerHeight;
249
+ const scrollHeight = container
250
+ ? container.scrollHeight
251
+ : document.documentElement.scrollHeight;
252
+ const maxScroll = Math.max(0, scrollHeight - viewportHeight);
253
+ const scrollProgress = maxScroll > 0 ? scrollY / maxScroll : 0;
254
+ const scrollDirection = scrollY === lastScrollY.current
255
+ ? null
256
+ : scrollY > lastScrollY.current
257
+ ? "down"
258
+ : "up";
259
+ const deltaTime = now - lastScrollTime.current;
260
+ const deltaY = scrollY - lastScrollY.current;
261
+ const velocity = deltaTime > 0 ? Math.abs(deltaY) / deltaTime : 0;
262
+ lastScrollY.current = scrollY;
263
+ lastScrollTime.current = now;
264
+ const sectionBounds = getSectionBounds();
265
+ if (sectionBounds.length === 0)
266
+ return;
267
+ const baseOffset = getEffectiveOffset();
268
+ const effectiveOffset = Math.max(baseOffset, viewportHeight * offsetRatio);
269
+ const triggerLine = scrollY + effectiveOffset;
270
+ const viewportTop = scrollY;
271
+ const viewportBottom = scrollY + viewportHeight;
272
+ const scores = sectionBounds.map((section) => {
273
+ const visibleTop = Math.max(section.top, viewportTop);
274
+ const visibleBottom = Math.min(section.bottom, viewportBottom);
275
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop);
276
+ const visibilityRatio = section.height > 0 ? visibleHeight / section.height : 0;
277
+ const visibleInViewportRatio = viewportHeight > 0 ? visibleHeight / viewportHeight : 0;
278
+ const isInViewport = section.bottom > viewportTop && section.top < viewportBottom;
279
+ const sectionProgress = (() => {
280
+ if (section.height === 0)
281
+ return 0;
282
+ const entryPoint = viewportBottom;
283
+ const _exitPoint = viewportTop;
284
+ const totalTravel = viewportHeight + section.height;
285
+ const traveled = entryPoint - section.top;
286
+ return Math.max(0, Math.min(1, traveled / totalTravel));
287
+ })();
288
+ let score = 0;
289
+ if (visibilityRatio >= visibilityThreshold) {
290
+ score += 1000 + visibilityRatio * 500;
291
+ }
292
+ else if (isInViewport) {
293
+ score += visibleInViewportRatio * 800;
294
+ }
295
+ const sectionIndex = sectionIndexMap.get(section.id) ?? 0;
296
+ if (scrollDirection &&
297
+ isInViewport &&
298
+ section.top <= triggerLine &&
299
+ section.bottom > triggerLine) {
300
+ score += 200;
301
+ }
302
+ score -= sectionIndex * 0.1;
303
+ return {
304
+ id: section.id,
305
+ score,
306
+ visibilityRatio,
307
+ isInViewport,
308
+ bounds: section,
309
+ progress: sectionProgress,
310
+ };
311
+ });
312
+ const isAtBottom = scrollY + viewportHeight >= scrollHeight - 5;
313
+ const isAtTop = scrollY <= 5;
314
+ let newActiveId = null;
315
+ if (isAtBottom && stableSectionIds.length > 0) {
316
+ newActiveId = stableSectionIds[stableSectionIds.length - 1];
317
+ }
318
+ else if (isAtTop && stableSectionIds.length > 0) {
319
+ newActiveId = stableSectionIds[0];
320
+ }
321
+ else {
322
+ const visibleScores = scores.filter((s) => s.isInViewport);
323
+ const candidates = visibleScores.length > 0 ? visibleScores : scores;
324
+ candidates.sort((a, b) => b.score - a.score);
325
+ if (candidates.length > 0) {
326
+ const bestCandidate = candidates[0];
327
+ const currentScore = scores.find((s) => s.id === currentActiveId);
328
+ const shouldSwitch = !currentScore ||
329
+ !currentScore.isInViewport ||
330
+ bestCandidate.score > currentScore.score + hysteresisMargin ||
331
+ bestCandidate.id === currentActiveId;
332
+ newActiveId = shouldSwitch ? bestCandidate.id : currentActiveId;
333
+ }
334
+ }
335
+ if (newActiveId !== currentActiveId) {
336
+ activeIdRef.current = newActiveId;
337
+ setActiveId(newActiveId);
338
+ callbackRefs.current.onActiveChange?.(newActiveId, currentActiveId);
339
+ }
340
+ const currentInViewport = new Set(scores.filter((s) => s.isInViewport).map((s) => s.id));
341
+ const prevInViewport = prevSectionsInViewport.current;
342
+ for (const id of currentInViewport) {
343
+ if (!prevInViewport.has(id)) {
344
+ callbackRefs.current.onSectionEnter?.(id);
345
+ }
346
+ }
347
+ for (const id of prevInViewport) {
348
+ if (!currentInViewport.has(id)) {
349
+ callbackRefs.current.onSectionLeave?.(id);
350
+ }
351
+ }
352
+ prevSectionsInViewport.current = currentInViewport;
353
+ const newScrollState = {
354
+ y: scrollY,
355
+ progress: Math.max(0, Math.min(1, scrollProgress)),
356
+ direction: scrollDirection,
357
+ velocity,
358
+ isScrolling: isScrollingRef.current,
359
+ maxScroll,
360
+ viewportHeight,
361
+ offset: effectiveOffset,
362
+ };
363
+ const newSections = {};
364
+ for (const s of scores) {
365
+ newSections[s.id] = {
366
+ bounds: {
367
+ top: s.bounds.top,
368
+ bottom: s.bounds.bottom,
369
+ height: s.bounds.height,
370
+ },
371
+ visibility: Math.round(s.visibilityRatio * 100) / 100,
372
+ progress: Math.round(s.progress * 100) / 100,
373
+ isInViewport: s.isInViewport,
374
+ isActive: s.id === newActiveId,
375
+ };
376
+ }
377
+ setScroll(newScrollState);
378
+ setSections(newSections);
379
+ }, [
380
+ stableSectionIds,
381
+ sectionIndexMap,
382
+ getEffectiveOffset,
383
+ offsetRatio,
384
+ visibilityThreshold,
385
+ hysteresisMargin,
386
+ getSectionBounds,
387
+ containerElement,
388
+ ]);
389
+ recalculateRef.current = calculateActiveSection;
390
+ useEffect(() => {
391
+ const container = containerElement;
392
+ const scrollTarget = container || window;
393
+ const scheduleCalculate = () => {
394
+ if (rafId.current) {
395
+ cancelAnimationFrame(rafId.current);
396
+ }
397
+ rafId.current = requestAnimationFrame(() => {
398
+ rafId.current = null;
399
+ calculateActiveSection();
400
+ });
401
+ };
402
+ const handleScrollEnd = () => {
403
+ isScrollingRef.current = false;
404
+ setScroll((prev) => ({ ...prev, isScrolling: false }));
405
+ callbackRefs.current.onScrollEnd?.();
406
+ };
407
+ const handleScroll = () => {
408
+ if (!isScrollingRef.current) {
409
+ isScrollingRef.current = true;
410
+ setScroll((prev) => ({ ...prev, isScrolling: true }));
411
+ callbackRefs.current.onScrollStart?.();
412
+ }
413
+ if (scrollIdleTimeoutRef.current) {
414
+ clearTimeout(scrollIdleTimeoutRef.current);
415
+ }
416
+ scrollIdleTimeoutRef.current = setTimeout(handleScrollEnd, SCROLL_IDLE_MS);
417
+ if (isThrottled.current) {
418
+ hasPendingScroll.current = true;
419
+ return;
420
+ }
421
+ isThrottled.current = true;
422
+ hasPendingScroll.current = false;
423
+ if (throttleTimeoutId.current) {
424
+ clearTimeout(throttleTimeoutId.current);
425
+ }
426
+ scheduleCalculate();
427
+ throttleTimeoutId.current = setTimeout(() => {
428
+ isThrottled.current = false;
429
+ throttleTimeoutId.current = null;
430
+ if (hasPendingScroll.current) {
431
+ hasPendingScroll.current = false;
432
+ handleScroll();
433
+ }
434
+ }, debounceMs);
435
+ };
436
+ const handleResize = () => {
437
+ scheduleCalculate();
438
+ };
439
+ calculateActiveSection();
440
+ const deferredRecalcId = setTimeout(() => {
441
+ calculateActiveSection();
442
+ }, 0);
443
+ scrollTarget.addEventListener("scroll", handleScroll, { passive: true });
444
+ window.addEventListener("resize", handleResize, { passive: true });
445
+ return () => {
446
+ clearTimeout(deferredRecalcId);
447
+ scrollTarget.removeEventListener("scroll", handleScroll);
448
+ window.removeEventListener("resize", handleResize);
449
+ if (rafId.current) {
450
+ cancelAnimationFrame(rafId.current);
451
+ rafId.current = null;
452
+ }
453
+ if (throttleTimeoutId.current) {
454
+ clearTimeout(throttleTimeoutId.current);
455
+ throttleTimeoutId.current = null;
456
+ }
457
+ if (programmaticScrollTimeoutId.current) {
458
+ clearTimeout(programmaticScrollTimeoutId.current);
459
+ programmaticScrollTimeoutId.current = null;
460
+ }
461
+ if (scrollIdleTimeoutRef.current) {
462
+ clearTimeout(scrollIdleTimeoutRef.current);
463
+ scrollIdleTimeoutRef.current = null;
464
+ }
465
+ scrollCleanupRef.current?.();
466
+ isThrottled.current = false;
467
+ hasPendingScroll.current = false;
468
+ isProgrammaticScrolling.current = false;
469
+ isScrollingRef.current = false;
470
+ };
471
+ }, [calculateActiveSection, debounceMs, containerElement]);
472
+ const activeIndex = useMemo(() => {
473
+ if (!activeId)
474
+ return -1;
475
+ return sectionIndexMap.get(activeId) ?? -1;
476
+ }, [activeId, sectionIndexMap]);
477
+ return {
478
+ activeId,
479
+ activeIndex,
480
+ scroll,
481
+ sections,
482
+ registerRef,
483
+ scrollToSection,
484
+ sectionProps,
485
+ navProps,
486
+ };
487
+ }
488
+ export default useDomet;
@@ -0,0 +1,60 @@
1
+ import type { RefObject } from "react";
2
+ export type SectionBounds = {
3
+ top: number;
4
+ bottom: number;
5
+ height: number;
6
+ };
7
+ export type ScrollState = {
8
+ y: number;
9
+ progress: number;
10
+ direction: "up" | "down" | null;
11
+ velocity: number;
12
+ isScrolling: boolean;
13
+ maxScroll: number;
14
+ viewportHeight: number;
15
+ offset: number;
16
+ };
17
+ export type SectionState = {
18
+ bounds: SectionBounds;
19
+ visibility: number;
20
+ progress: number;
21
+ isInViewport: boolean;
22
+ isActive: boolean;
23
+ };
24
+ export type ScrollBehavior = "smooth" | "instant" | "auto";
25
+ export type DometOptions = {
26
+ offset?: number;
27
+ offsetRatio?: number;
28
+ debounceMs?: number;
29
+ visibilityThreshold?: number;
30
+ hysteresisMargin?: number;
31
+ behavior?: ScrollBehavior;
32
+ onActiveChange?: (id: string | null, prevId: string | null) => void;
33
+ onSectionEnter?: (id: string) => void;
34
+ onSectionLeave?: (id: string) => void;
35
+ onScrollStart?: () => void;
36
+ onScrollEnd?: () => void;
37
+ };
38
+ export type SectionProps = {
39
+ id: string;
40
+ ref: (el: HTMLElement | null) => void;
41
+ "data-domet": string;
42
+ };
43
+ export type NavProps = {
44
+ onClick: () => void;
45
+ "aria-current": "page" | undefined;
46
+ "data-active": boolean;
47
+ };
48
+ export type UseDometReturn = {
49
+ activeId: string | null;
50
+ activeIndex: number;
51
+ scroll: ScrollState;
52
+ sections: Record<string, SectionState>;
53
+ registerRef: (id: string) => (el: HTMLElement | null) => void;
54
+ scrollToSection: (id: string) => void;
55
+ sectionProps: (id: string) => SectionProps;
56
+ navProps: (id: string) => NavProps;
57
+ };
58
+ export declare function useDomet(sectionIds: string[], containerRef?: RefObject<HTMLElement> | null, options?: DometOptions): UseDometReturn;
59
+ export default useDomet;
60
+ //# sourceMappingURL=useScrowl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScrowl.d.ts","sourceRoot":"","sources":["../src/useScrowl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAgBvC,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE3D,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACpE,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9D,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,YAAY,CAAC;IAC3C,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,QAAQ,CAAC;CACpC,CAAC;AAaF,wBAAgB,QAAQ,CACtB,UAAU,EAAE,MAAM,EAAE,EACpB,YAAY,GAAE,SAAS,CAAC,WAAW,CAAC,GAAG,IAAW,EAClD,OAAO,GAAE,YAAiB,GACzB,cAAc,CAulBhB;AAED,eAAe,QAAQ,CAAC"}