@vysmo/scroll 0.1.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 (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/dist/index.d.ts +10 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +6 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/observer.d.ts +36 -0
  8. package/dist/observer.d.ts.map +1 -0
  9. package/dist/observer.js +82 -0
  10. package/dist/observer.js.map +1 -0
  11. package/dist/progress.d.ts +14 -0
  12. package/dist/progress.d.ts.map +1 -0
  13. package/dist/progress.js +36 -0
  14. package/dist/progress.js.map +1 -0
  15. package/dist/scroll-effect.d.ts +37 -0
  16. package/dist/scroll-effect.d.ts.map +1 -0
  17. package/dist/scroll-effect.js +39 -0
  18. package/dist/scroll-effect.js.map +1 -0
  19. package/dist/scroll-transition.d.ts +38 -0
  20. package/dist/scroll-transition.d.ts.map +1 -0
  21. package/dist/scroll-transition.js +42 -0
  22. package/dist/scroll-transition.js.map +1 -0
  23. package/dist/types.d.ts +18 -0
  24. package/dist/types.d.ts.map +1 -0
  25. package/dist/types.js +2 -0
  26. package/dist/types.js.map +1 -0
  27. package/dist/zones.d.ts +56 -0
  28. package/dist/zones.d.ts.map +1 -0
  29. package/dist/zones.js +94 -0
  30. package/dist/zones.js.map +1 -0
  31. package/package.json +52 -0
  32. package/src/__tests__/__screenshots__/progress.test.ts/createScrollProgress-reaches-1-after-fully-scrolling-past-the-element-1.png +0 -0
  33. package/src/__tests__/observer.test.ts +60 -0
  34. package/src/__tests__/progress.test.ts +126 -0
  35. package/src/__tests__/scroll-effect.test.ts +135 -0
  36. package/src/__tests__/scroll-transition.test.ts +167 -0
  37. package/src/__tests__/ssr.test.ts +37 -0
  38. package/src/__tests__/types-check.ts +86 -0
  39. package/src/__tests__/zones.test.ts +175 -0
  40. package/src/index.ts +9 -0
  41. package/src/observer.ts +89 -0
  42. package/src/progress.ts +39 -0
  43. package/src/scroll-effect.ts +72 -0
  44. package/src/scroll-transition.ts +80 -0
  45. package/src/types.ts +19 -0
  46. package/src/zones.ts +103 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maesto LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @vysmo/scroll
2
+
3
+ Three primitives that bind scroll progress to the rest of the ecosystem: `createScrollProgress` (raw 0–1 emitter), `createScrollTransition` (drives any `@vysmo/transitions` render), `createScrollEffect` (drives any `@vysmo/effects` params). One shared rAF-throttled observer underneath. Headless — you own the canvas.
4
+
5
+ [Live demos](https://vysmo.com/scroll) · [Source](https://github.com/vysmodev)
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @vysmo/scroll
11
+ ```
12
+
13
+ For `createScrollTransition` / `createScrollEffect` you also need the corresponding consumer package:
14
+
15
+ ```bash
16
+ pnpm add @vysmo/transitions # for createScrollTransition
17
+ pnpm add @vysmo/effects # for createScrollEffect
18
+ ```
19
+
20
+ ## Quick start
21
+
22
+ ### Raw scroll progress
23
+
24
+ ```ts
25
+ import { createScrollProgress } from "@vysmo/scroll";
26
+
27
+ const handle = createScrollProgress({
28
+ element: document.querySelector(".hero")!,
29
+ onProgress: (p) => console.log(p), // 0..1 as the element sweeps the viewport
30
+ });
31
+
32
+ // later
33
+ handle.destroy();
34
+ ```
35
+
36
+ `progress` runs from `0` (element's top edge at the viewport's bottom) to `1` (element's bottom edge at the viewport's top). Pass `ease: (t) => …` to remap — anything from `@vysmo/easings` works.
37
+
38
+ ### Drive a transition with scroll
39
+
40
+ ```ts
41
+ import { Runner, crossZoom } from "@vysmo/transitions";
42
+ import { createScrollTransition } from "@vysmo/scroll";
43
+
44
+ const canvas = document.querySelector("canvas")!;
45
+ const runner = new Runner({ canvas });
46
+
47
+ createScrollTransition({
48
+ section: document.querySelector(".scroll-section")!,
49
+ runner,
50
+ transition: crossZoom,
51
+ from: imageA,
52
+ to: imageB,
53
+ });
54
+ // renders crossZoom on every scroll tick, automatically passing progress
55
+ ```
56
+
57
+ ### Drive an effect with scroll
58
+
59
+ ```ts
60
+ import { Runner, blur } from "@vysmo/effects";
61
+ import { createScrollEffect } from "@vysmo/scroll";
62
+
63
+ const runner = new Runner({ canvas });
64
+
65
+ createScrollEffect({
66
+ section: document.querySelector(".hero")!,
67
+ runner,
68
+ effect: blur,
69
+ source: image,
70
+ paramsAt: (progress) => ({ radius: progress * 20 }), // unblur as user scrolls down
71
+ });
72
+ ```
73
+
74
+ ## Zone helpers
75
+
76
+ Most scroll-driven UIs don't want a single linear 0..1 — they want phases (intro → hold → outro). The `zones.ts` helpers compose:
77
+
78
+ ```ts
79
+ import { scrollPlateau, scrollRange, scrollZones, smoothstep } from "@vysmo/scroll";
80
+
81
+ scrollRange(0.2, 0.8); // remap [0.2, 0.8] → [0, 1]; clamp outside
82
+ scrollPlateau(0.4, 0.6); // 0..1 with a flat 1.0 plateau between [0.4, 0.6]
83
+ scrollZones(0.2, 0.8); // inverse of plateau — peaks at the edges, 0 in the middle
84
+ smoothstep; // t² (3 - 2t) — common easing for scroll
85
+ ```
86
+
87
+ All return plain `(progress: number) => number` functions — pipe through `createScrollProgress`'s `ease` option, or use directly.
88
+
89
+ ## Shared observer
90
+
91
+ Every primitive registers with one rAF-throttled `IntersectionObserver` + `scroll` listener — N elements cost one rAF tick per frame, not N. Access directly if you need it:
92
+
93
+ ```ts
94
+ import { sharedScrollObserver } from "@vysmo/scroll";
95
+
96
+ const obs = sharedScrollObserver();
97
+ obs.subscribe(element, { onScroll: (rect, vp) => /* ... */ });
98
+ ```
99
+
100
+ ## Characteristics
101
+
102
+ - **DOM-only.** Browser scrolling is the substrate; no Node fallback (the SSR test asserts the module loads, not that it functions without DOM).
103
+ - **Tiny.** ~2 KB gzipped for the full bundle; ~1 KB for `createScrollProgress` alone.
104
+ - **Tree-shakable.** `createScrollTransition` and `createScrollEffect` only pull in their respective peer libraries' types.
105
+ - **One rAF.** Observer is shared across all primitives.
106
+ - **SSR-safe at module load.** Guarded `typeof window` checks; observer is lazy-instantiated.
107
+
108
+ ## License
109
+
110
+ MIT.
@@ -0,0 +1,10 @@
1
+ export { createScrollProgress } from "./progress.js";
2
+ export { createScrollTransition } from "./scroll-transition.js";
3
+ export type { ScrollTransitionOptions } from "./scroll-transition.js";
4
+ export { createScrollEffect } from "./scroll-effect.js";
5
+ export type { ScrollEffectOptions } from "./scroll-effect.js";
6
+ export { sharedScrollObserver, ScrollObserver } from "./observer.js";
7
+ export type { ScrollSubscriber } from "./observer.js";
8
+ export { scrollPlateau, scrollRange, scrollZones, smoothstep } from "./zones.js";
9
+ export type { EaseFn, Handle, ScrollProgressOptions } from "./types.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACrE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjF,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { createScrollProgress } from "./progress.js";
2
+ export { createScrollTransition } from "./scroll-transition.js";
3
+ export { createScrollEffect } from "./scroll-effect.js";
4
+ export { sharedScrollObserver, ScrollObserver } from "./observer.js";
5
+ export { scrollPlateau, scrollRange, scrollZones, smoothstep } from "./zones.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shared rAF-throttled scroll / resize observer. Every primitive in this
3
+ * package (progress, parallax, sticky-pin, horizontal-section) registers
4
+ * its element here; the observer keeps one passive `scroll` listener on
5
+ * `window` for the lifetime of any subscription and tears it down once
6
+ * the last subscriber unsubscribes.
7
+ *
8
+ * Safe to import at module load: no window access happens until
9
+ * `subscribe()` is called.
10
+ */
11
+ export interface ScrollSubscriber {
12
+ onScroll(rect: DOMRect, viewport: {
13
+ readonly width: number;
14
+ readonly height: number;
15
+ }): void;
16
+ }
17
+ export declare class ScrollObserver {
18
+ private subs;
19
+ private listening;
20
+ private rafId;
21
+ private readonly scheduler;
22
+ constructor();
23
+ subscribe(element: HTMLElement, subscriber: ScrollSubscriber): () => void;
24
+ private start;
25
+ private stop;
26
+ private scheduleUpdate;
27
+ /**
28
+ * Run every subscriber's callback with the current viewport + element
29
+ * rect. Exposed for tests that need deterministic flushes without
30
+ * waiting on rAF.
31
+ */
32
+ flush(): void;
33
+ }
34
+ /** Lazy singleton — first call creates the observer. SSR-safe. */
35
+ export declare function sharedScrollObserver(): ScrollObserver;
36
+ //# sourceMappingURL=observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CACN,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAC5D,IAAI,CAAC;CACT;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,IAAI,CAA4C;IACxD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;;IAMvC,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAUzE,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,IAAI;IAWZ,OAAO,CAAC,cAAc;IAStB;;;;OAIG;IACH,KAAK,IAAI,IAAI;CAUd;AAID,kEAAkE;AAClE,wBAAgB,oBAAoB,IAAI,cAAc,CAGrD"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Shared rAF-throttled scroll / resize observer. Every primitive in this
3
+ * package (progress, parallax, sticky-pin, horizontal-section) registers
4
+ * its element here; the observer keeps one passive `scroll` listener on
5
+ * `window` for the lifetime of any subscription and tears it down once
6
+ * the last subscriber unsubscribes.
7
+ *
8
+ * Safe to import at module load: no window access happens until
9
+ * `subscribe()` is called.
10
+ */
11
+ export class ScrollObserver {
12
+ subs = new Map();
13
+ listening = false;
14
+ rafId = null;
15
+ scheduler;
16
+ constructor() {
17
+ this.scheduler = () => this.scheduleUpdate();
18
+ }
19
+ subscribe(element, subscriber) {
20
+ this.subs.set(element, subscriber);
21
+ if (!this.listening)
22
+ this.start();
23
+ this.scheduleUpdate();
24
+ return () => {
25
+ this.subs.delete(element);
26
+ if (this.subs.size === 0)
27
+ this.stop();
28
+ };
29
+ }
30
+ start() {
31
+ if (typeof window === "undefined")
32
+ return;
33
+ window.addEventListener("scroll", this.scheduler, { passive: true });
34
+ window.addEventListener("resize", this.scheduler, { passive: true });
35
+ this.listening = true;
36
+ }
37
+ stop() {
38
+ if (typeof window === "undefined")
39
+ return;
40
+ window.removeEventListener("scroll", this.scheduler);
41
+ window.removeEventListener("resize", this.scheduler);
42
+ if (this.rafId !== null) {
43
+ cancelAnimationFrame(this.rafId);
44
+ this.rafId = null;
45
+ }
46
+ this.listening = false;
47
+ }
48
+ scheduleUpdate() {
49
+ if (this.rafId !== null)
50
+ return;
51
+ if (typeof requestAnimationFrame === "undefined")
52
+ return;
53
+ this.rafId = requestAnimationFrame(() => {
54
+ this.rafId = null;
55
+ this.flush();
56
+ });
57
+ }
58
+ /**
59
+ * Run every subscriber's callback with the current viewport + element
60
+ * rect. Exposed for tests that need deterministic flushes without
61
+ * waiting on rAF.
62
+ */
63
+ flush() {
64
+ if (typeof window === "undefined")
65
+ return;
66
+ const viewport = {
67
+ width: window.innerWidth,
68
+ height: window.innerHeight,
69
+ };
70
+ for (const [el, sub] of this.subs) {
71
+ sub.onScroll(el.getBoundingClientRect(), viewport);
72
+ }
73
+ }
74
+ }
75
+ let _shared = null;
76
+ /** Lazy singleton — first call creates the observer. SSR-safe. */
77
+ export function sharedScrollObserver() {
78
+ if (!_shared)
79
+ _shared = new ScrollObserver();
80
+ return _shared;
81
+ }
82
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,MAAM,OAAO,cAAc;IACjB,IAAI,GAAG,IAAI,GAAG,EAAiC,CAAC;IAChD,SAAS,GAAG,KAAK,CAAC;IAClB,KAAK,GAAkB,IAAI,CAAC;IACnB,SAAS,CAAa;IAEvC;QACE,IAAI,CAAC,SAAS,GAAG,GAAS,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;IACrD,CAAC;IAED,SAAS,CAAC,OAAoB,EAAE,UAA4B;QAC1D,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1B,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;gBAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACxC,CAAC,CAAC;IACJ,CAAC;IAEO,KAAK;QACX,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAEO,IAAI;QACV,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACrD,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACxB,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAEO,cAAc;QACpB,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO;QAChC,IAAI,OAAO,qBAAqB,KAAK,WAAW;YAAE,OAAO;QACzD,IAAI,CAAC,KAAK,GAAG,qBAAqB,CAAC,GAAG,EAAE;YACtC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK;QACH,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,QAAQ,GAAG;YACf,KAAK,EAAE,MAAM,CAAC,UAAU;YACxB,MAAM,EAAE,MAAM,CAAC,WAAW;SAClB,CAAC;QACX,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAClC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,qBAAqB,EAAE,EAAE,QAAQ,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;CACF;AAED,IAAI,OAAO,GAA0B,IAAI,CAAC;AAE1C,kEAAkE;AAClE,MAAM,UAAU,oBAAoB;IAClC,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;IAC7C,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Handle, ScrollProgressOptions } from "./types.js";
2
+ /**
3
+ * Emits a continuous [0, 1] value as `element` sweeps across the viewport.
4
+ *
5
+ * progress = 0 → element's top edge is at the viewport's bottom edge
6
+ * (element has just entered from below)
7
+ * progress = 1 → element's bottom edge is at the viewport's top edge
8
+ * (element has just exited through the top)
9
+ *
10
+ * The curve is linear by default; pass `ease: (t) => ...` to remap —
11
+ * any easing from `@vysmo/easings` works without importing it here.
12
+ */
13
+ export declare function createScrollProgress(options: ScrollProgressOptions): Handle;
14
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEhE;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,GAC7B,MAAM,CAsBR"}
@@ -0,0 +1,36 @@
1
+ import { sharedScrollObserver } from "./observer.js";
2
+ /**
3
+ * Emits a continuous [0, 1] value as `element` sweeps across the viewport.
4
+ *
5
+ * progress = 0 → element's top edge is at the viewport's bottom edge
6
+ * (element has just entered from below)
7
+ * progress = 1 → element's bottom edge is at the viewport's top edge
8
+ * (element has just exited through the top)
9
+ *
10
+ * The curve is linear by default; pass `ease: (t) => ...` to remap —
11
+ * any easing from `@vysmo/easings` works without importing it here.
12
+ */
13
+ export function createScrollProgress(options) {
14
+ const observer = sharedScrollObserver();
15
+ let lastProgress = Number.NaN;
16
+ const unsubscribe = observer.subscribe(options.element, {
17
+ onScroll(rect, viewport) {
18
+ const span = viewport.height + rect.height;
19
+ if (span <= 0)
20
+ return;
21
+ const raw = (viewport.height - rect.top) / span;
22
+ const clamped = raw < 0 ? 0 : raw > 1 ? 1 : raw;
23
+ const mapped = options.ease ? options.ease(clamped) : clamped;
24
+ if (mapped === lastProgress)
25
+ return;
26
+ lastProgress = mapped;
27
+ options.onProgress(mapped);
28
+ },
29
+ });
30
+ return {
31
+ destroy() {
32
+ unsubscribe();
33
+ },
34
+ };
35
+ }
36
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGrD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA8B;IAE9B,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,IAAI,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC;IAE9B,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE;QACtD,QAAQ,CAAC,IAAI,EAAE,QAAQ;YACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3C,IAAI,IAAI,IAAI,CAAC;gBAAE,OAAO;YACtB,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;YAChD,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9D,IAAI,MAAM,KAAK,YAAY;gBAAE,OAAO;YACpC,YAAY,GAAG,MAAM,CAAC;YACtB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC,CAAC;IAEH,OAAO;QACL,OAAO;YACL,WAAW,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { Effect, Runner, TextureSource, UniformParams } from "@vysmo/effects";
2
+ import type { EaseFn, Handle } from "./types.js";
3
+ export interface ScrollEffectOptions<P extends UniformParams> {
4
+ /**
5
+ * Section whose scroll-past drives the effect. Progress spans the
6
+ * full viewport sweep, same as `createScrollProgress`.
7
+ */
8
+ section: HTMLElement;
9
+ /** Effects runner; caller owns the canvas + WebGL context. */
10
+ runner: Runner;
11
+ /** Effect to render on every scroll frame. */
12
+ effect: Effect<P>;
13
+ /** Source texture to filter — image, video, canvas. */
14
+ source: TextureSource;
15
+ /**
16
+ * Maps the scroll progress [0, 1] to the effect's uniform params.
17
+ * Keeps the scroll package free of effect-specific knowledge — the
18
+ * caller decides which param to animate and how.
19
+ *
20
+ * paramsAt: (p) => ({ radius: p * 20 })
21
+ */
22
+ paramsAt: (progress: number) => Partial<P>;
23
+ /** Remap the raw [0, 1] progress before it reaches `paramsAt`. */
24
+ ease?: EaseFn;
25
+ }
26
+ /**
27
+ * Bind the scroll position through `section` to a continuous render of
28
+ * `effect` on `runner`, where the effect's params are a function of
29
+ * progress. Typical use: blur / chromatic-aberration / colour-grade
30
+ * intensity ramps up as the user scrolls into a section and back down
31
+ * as they scroll out.
32
+ *
33
+ * Same ownership model as `createScrollTransition`: you pass a Runner,
34
+ * scroll drives its `render()`. No re-render when progress is unchanged.
35
+ */
36
+ export declare function createScrollEffect<P extends UniformParams>(options: ScrollEffectOptions<P>): Handle;
37
+ //# sourceMappingURL=scroll-effect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-effect.d.ts","sourceRoot":"","sources":["../src/scroll-effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,MAAM,EACN,aAAa,EACb,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,aAAa;IAC1D;;;OAGG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,uDAAuD;IACvD,MAAM,EAAE,aAAa,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3C,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,aAAa,EACxD,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC9B,MAAM,CA0BR"}
@@ -0,0 +1,39 @@
1
+ import { sharedScrollObserver } from "./observer.js";
2
+ /**
3
+ * Bind the scroll position through `section` to a continuous render of
4
+ * `effect` on `runner`, where the effect's params are a function of
5
+ * progress. Typical use: blur / chromatic-aberration / colour-grade
6
+ * intensity ramps up as the user scrolls into a section and back down
7
+ * as they scroll out.
8
+ *
9
+ * Same ownership model as `createScrollTransition`: you pass a Runner,
10
+ * scroll drives its `render()`. No re-render when progress is unchanged.
11
+ */
12
+ export function createScrollEffect(options) {
13
+ const observer = sharedScrollObserver();
14
+ let lastProgress = Number.NaN;
15
+ const unsubscribe = observer.subscribe(options.section, {
16
+ onScroll(rect, viewport) {
17
+ const span = viewport.height + rect.height;
18
+ if (span <= 0)
19
+ return;
20
+ const raw = (viewport.height - rect.top) / span;
21
+ const clamped = raw < 0 ? 0 : raw > 1 ? 1 : raw;
22
+ const mapped = options.ease ? options.ease(clamped) : clamped;
23
+ if (mapped === lastProgress)
24
+ return;
25
+ lastProgress = mapped;
26
+ const params = options.paramsAt(mapped);
27
+ options.runner.render(options.effect, {
28
+ source: options.source,
29
+ params,
30
+ });
31
+ },
32
+ });
33
+ return {
34
+ destroy() {
35
+ unsubscribe();
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=scroll-effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-effect.js","sourceRoot":"","sources":["../src/scroll-effect.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AA2BrD;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA+B;IAE/B,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,IAAI,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC;IAE9B,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE;QACtD,QAAQ,CAAC,IAAI,EAAE,QAAQ;YACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3C,IAAI,IAAI,IAAI,CAAC;gBAAE,OAAO;YACtB,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;YAChD,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9D,IAAI,MAAM,KAAK,YAAY;gBAAE,OAAO;YACpC,YAAY,GAAG,MAAM,CAAC;YACtB,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACxC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE;gBACpC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,MAAM;aACP,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;IAEH,OAAO;QACL,OAAO;YACL,WAAW,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { Runner, TextureSource, Transition, UniformParams } from "@vysmo/transitions";
2
+ import type { EaseFn, Handle } from "./types.js";
3
+ export interface ScrollTransitionOptions<P extends UniformParams> {
4
+ /**
5
+ * Section whose scroll-past drives the transition. Progress is 0 when
6
+ * the section's top enters the viewport bottom and 1 when its bottom
7
+ * exits the viewport top — same curve as `createScrollProgress`.
8
+ */
9
+ section: HTMLElement;
10
+ /**
11
+ * Transitions runner. The caller owns the WebGL context and its
12
+ * canvas — that keeps this primitive composable (multiple
13
+ * scroll-transitions can share one runner) and decouples lifecycle.
14
+ */
15
+ runner: Runner;
16
+ /** Transition to render across the scroll range. */
17
+ transition: Transition<P>;
18
+ /** Starting source — shown at progress 0. */
19
+ from: TextureSource;
20
+ /** Ending source — shown at progress 1. */
21
+ to: TextureSource;
22
+ /** Overrides for the transition's uniform params. */
23
+ params?: Partial<P>;
24
+ /** Remap the raw [0, 1] progress. Default: linear. */
25
+ ease?: EaseFn;
26
+ }
27
+ /**
28
+ * Bind the vertical scroll position through `section` to a full transition
29
+ * run on `runner`. As the section sweeps past the viewport, `from` morphs
30
+ * into `to` via the chosen transition. One rAF per scroll frame, no
31
+ * re-render when the clamped progress hasn't changed.
32
+ *
33
+ * The scroll package does not import the transitions runtime — only its
34
+ * types. You pass in your own `Runner`, so the transitions library only
35
+ * lands in bundles that actually call this primitive.
36
+ */
37
+ export declare function createScrollTransition<P extends UniformParams>(options: ScrollTransitionOptions<P>): Handle;
38
+ //# sourceMappingURL=scroll-transition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-transition.d.ts","sourceRoot":"","sources":["../src/scroll-transition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,aAAa;IAC9D;;;;OAIG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B,6CAA6C;IAC7C,IAAI,EAAE,aAAa,CAAC;IACpB,2CAA2C;IAC3C,EAAE,EAAE,aAAa,CAAC;IAClB,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,aAAa,EAC5D,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAClC,MAAM,CAiCR"}
@@ -0,0 +1,42 @@
1
+ import { sharedScrollObserver } from "./observer.js";
2
+ /**
3
+ * Bind the vertical scroll position through `section` to a full transition
4
+ * run on `runner`. As the section sweeps past the viewport, `from` morphs
5
+ * into `to` via the chosen transition. One rAF per scroll frame, no
6
+ * re-render when the clamped progress hasn't changed.
7
+ *
8
+ * The scroll package does not import the transitions runtime — only its
9
+ * types. You pass in your own `Runner`, so the transitions library only
10
+ * lands in bundles that actually call this primitive.
11
+ */
12
+ export function createScrollTransition(options) {
13
+ const observer = sharedScrollObserver();
14
+ let lastProgress = Number.NaN;
15
+ const unsubscribe = observer.subscribe(options.section, {
16
+ onScroll(rect, viewport) {
17
+ const span = viewport.height + rect.height;
18
+ if (span <= 0)
19
+ return;
20
+ const raw = (viewport.height - rect.top) / span;
21
+ const clamped = raw < 0 ? 0 : raw > 1 ? 1 : raw;
22
+ const mapped = options.ease ? options.ease(clamped) : clamped;
23
+ if (mapped === lastProgress)
24
+ return;
25
+ lastProgress = mapped;
26
+ const args = {
27
+ from: options.from,
28
+ to: options.to,
29
+ progress: mapped,
30
+ };
31
+ if (options.params !== undefined)
32
+ args.params = options.params;
33
+ options.runner.render(options.transition, args);
34
+ },
35
+ });
36
+ return {
37
+ destroy() {
38
+ unsubscribe();
39
+ },
40
+ };
41
+ }
42
+ //# sourceMappingURL=scroll-transition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-transition.js","sourceRoot":"","sources":["../src/scroll-transition.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AA4BrD;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAmC;IAEnC,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,IAAI,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC;IAE9B,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE;QACtD,QAAQ,CAAC,IAAI,EAAE,QAAQ;YACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3C,IAAI,IAAI,IAAI,CAAC;gBAAE,OAAO;YACtB,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;YAChD,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9D,IAAI,MAAM,KAAK,YAAY;gBAAE,OAAO;YACpC,YAAY,GAAG,MAAM,CAAC;YACtB,MAAM,IAAI,GAKN;gBACF,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,QAAQ,EAAE,MAAM;aACjB,CAAC;YACF,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;gBAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC/D,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAClD,CAAC;KACF,CAAC,CAAC;IAEH,OAAO;QACL,OAAO;YACL,WAAW,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /** Signature compatible with `@vysmo/easings.EasingFn`, kept local so this package has zero runtime coupling to the easings lib. */
2
+ export type EaseFn = (t: number) => number;
3
+ export interface Handle {
4
+ destroy(): void;
5
+ }
6
+ export interface ScrollProgressOptions {
7
+ /**
8
+ * Element whose bounding box is tracked. Progress is 0 when its top is
9
+ * at the viewport bottom and 1 when its bottom is at the viewport top
10
+ * — i.e. a full sweep across the viewport.
11
+ */
12
+ element: HTMLElement;
13
+ /** Remap the raw [0, 1] progress. Default: linear. */
14
+ ease?: EaseFn;
15
+ /** Invoked on every frame the element's position changes. */
16
+ onProgress: (progress: number) => void;
17
+ }
18
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oIAAoI;AACpI,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAE3C,MAAM,WAAW,MAAM;IACrB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,56 @@
1
+ import type { EaseFn } from "./types.js";
2
+ /**
3
+ * Smoothstep — `t² · (3 − 2t)`. C1-continuous S-curve that matches
4
+ * linear at 0, 0.5, and 1 but eases in and out near the boundaries.
5
+ * Used as the default ramp shape for every zone helper so transitions
6
+ * and effects decelerate smoothly into their plateaus instead of
7
+ * snapping on the derivative discontinuity of a pure linear ramp.
8
+ *
9
+ * Exported so callers can reach for it explicitly, or compose it.
10
+ */
11
+ export declare const smoothstep: EaseFn;
12
+ /**
13
+ * Clamp progress to a sub-range. Outside the range, the output stays flat
14
+ * (0 before `start`, 1 after `end`). Inside, progress is remapped from
15
+ * `[start, end]` to `[0, 1]` through the supplied ease (default:
16
+ * {@link smoothstep}).
17
+ *
18
+ * scrollRange(0.1, 0.5) — transition plays between 10% and 50% of the
19
+ * full viewport sweep; does nothing before, stays at its final state
20
+ * after. Pass `ease: (t) => t` for a linear clamp.
21
+ *
22
+ * Designed for `createScrollTransition` so the transition completes at a
23
+ * point the author chooses — typically well before the section exits the
24
+ * viewport — and then holds its final state while the user keeps scrolling.
25
+ */
26
+ export declare function scrollRange(start: number, end: number, ease?: EaseFn): EaseFn;
27
+ /**
28
+ * Three-zone bathtub envelope for effects.
29
+ *
30
+ * scrollZones(0.25, 0.85) — effect ramps from max at `p = 0` down to
31
+ * zero at `p = 0.25` (smoothly by default), stays at zero through the
32
+ * clear zone (`0.25 ≤ p ≤ 0.85`), then ramps back up to max by
33
+ * `p = 1`.
34
+ *
35
+ * Designed for `createScrollEffect` where "identity" is zero intensity —
36
+ * so the image reads cleanly while the section is visible and the effect
37
+ * only appears as it enters and exits the viewport. Pass `ease: (t) => t`
38
+ * for a linear ramp; default is {@link smoothstep}.
39
+ */
40
+ export declare function scrollZones(clearStart: number, clearEnd: number, ease?: EaseFn): EaseFn;
41
+ /**
42
+ * Three-zone plateau envelope — the inverse of {@link scrollZones}.
43
+ *
44
+ * scrollPlateau(0.3, 0.7) — rises from 0 at `p = 0` up to 1 at
45
+ * `p = 0.3` (smoothly by default), holds at 1 across the clear zone
46
+ * (`0.3 ≤ p ≤ 0.7`), then falls back to 0 by `p = 1`.
47
+ *
48
+ * Designed for `createScrollTransition` where "identity" is the fully
49
+ * transitioned state (progress 1). The transition plays in as the
50
+ * section enters, holds its final frame across the clear zone, then
51
+ * plays back out (reverse) as the section exits. Smoothstep default
52
+ * prevents the harsh snap that a linear ramp produces at the plateau
53
+ * boundaries.
54
+ */
55
+ export declare function scrollPlateau(clearStart: number, clearEnd: number, ease?: EaseFn): EaseFn;
56
+ //# sourceMappingURL=zones.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zones.d.ts","sourceRoot":"","sources":["../src/zones.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,MAAmC,CAAC;AAE7D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAmB,GACxB,MAAM,CAQR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAmB,GACxB,MAAM,CAYR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAmB,GACxB,MAAM,CAYR"}