@ev-ry/fx 0.1.0-rc.1 → 0.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/QUICKSTART.fa.md CHANGED
@@ -1,4 +1,4 @@
1
- # EV-RY FX Free 0.1.0-rc.1 — نصب آزمایشی
1
+ # EV-RY FX Free 0.1.0-rc.2 — نصب آزمایشی
2
2
 
3
3
  این نسخهٔ آزمایشی عمومی با مجوز MIT ارائه می‌شود. نام بسته در npm برابر @ev-ry/fx است.
4
4
 
@@ -34,7 +34,7 @@ H1/H2 خودکار انتخاب می‌شوند. برای تصویر `data-thd-i
34
34
  ## نصب با npm
35
35
 
36
36
  ```sh
37
- npm install @ev-ry/fx
37
+ npm install ./thd-free-0.1.0-rc.2.tgz
38
38
  ```
39
39
 
40
40
  برای import از `@ev-ry/fx`، namespace سازگار THREE را خودتان ارائه کنید؛ نسخهٔ همراه script همان r158 است. راهنمای کامل API در docs/GUIDE.md قرار دارد.
package/README.md CHANGED
@@ -1,116 +1,116 @@
1
- <p align="center"><img src="https://raw.githubusercontent.com/kbaghini/evry-fx/main/docs/assets/cover.svg" alt="EV-RY FX — Motion for the text and images already on your page." width="100%"></p>
2
-
3
- <h1 align="center">EV-RY FX</h1>
4
-
5
- <p align="center"><strong>Your HTML. Four particle effects. Native when still.</strong></p>
6
-
7
- <p align="center">Free edition · 0.1.0-rc.1 preview · <a href="https://github.com/kbaghini/evry-fx/blob/main/LICENSE">MIT licensed</a> · JavaScript + TypeScript declarations</p>
8
-
9
- <p align="center">
10
- <a href="#quick-start">Quick start</a> ·
11
- <a href="https://kbaghini.github.io/evry-fx/docs/">Live demo</a> ·
12
- <a href="https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md">Guide</a> ·
13
- <a href="https://github.com/kbaghini/evry-fx/blob/main/QUICKSTART.fa.md">راهنمای فارسی</a>
14
- </p>
15
-
16
- Add WebGL particle motion to existing headings, display text, images and supported SVG icons. EV-RY FX borrows their content and placement from the page, animates textured particles, then returns to native browser rendering.
17
-
18
- Start with automatic scroll reveals, or attach selected elements through a small API. Keep your existing layout and components.
19
-
20
- ## See it move
21
-
22
- ![EV-RY FX motion preview](https://raw.githubusercontent.com/kbaghini/evry-fx/main/docs/assets/thd-preview.gif)
23
-
24
- The README shows an animated preview. Open the [live interactive demo](https://kbaghini.github.io/evry-fx/docs/) to try entry and exit effects on real text and images.
25
-
26
- ## What you get
27
-
28
- | Content | Entry | Exit |
29
- | --- | --- | --- |
30
- | Text | Dust wind | Smoke |
31
- | Images and supported SVG | Drifting snow | Melt |
32
-
33
- - **A small automatic setup.** Reveal `h1`, `h2`, `[data-thd-text]` and `img[data-thd-image]` on intersection. Mark exclusions with `data-thd-ignore`.
34
- - **Manual control when needed.** Attach display text, images or supported static SVG; replay, cancel, refresh and destroy individual attachments. Simple image replacement is included.
35
- - **Your page keeps ownership.** Native DOM elements retain their content and semantics. Native rendering resumes when entry motion settles.
36
- - **One visual engine.** Text and media use textured triangular particles, four fixed presets and a 2000ms effect timeline. Individual particles can finish earlier within that timeline.
37
- - **A practical integration boundary.** Framework-independent JavaScript, typed APIs, a classic script entry and an explicit ES-module loader. A React integration example is included.
38
-
39
- ## Quick start
40
-
41
- **Public preview: 0.1.0-rc.1.** Validate the supported content and layouts in your project before production use.
42
-
43
- Install from your project folder:
44
-
45
- ```sh
46
- npm install @ev-ry/fx
47
- ```
48
-
49
- ### Add a script
50
-
51
- Serve the complete installed `node_modules/@ev-ry/fx` directory at `/thd/`. Preserve its modules and asset folders; this is not a single-file bundle.
52
-
53
- ```html
54
- <head>
55
- <!-- Early mask: load before body content, without async or defer. -->
56
- <script src="/thd/src/dom-reveal-boot.js"></script>
57
- <script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
58
- </head>
59
- <body>
60
- <h1 data-thd-pending>Small details. A better feeling.</h1>
61
- <p data-thd-text data-thd-pending>Made for your existing page.</p>
62
- <img data-thd-image data-thd-pending src="photo.jpg"
63
- width="640" height="400" alt="Describe your photograph">
64
- </body>
65
- ```
66
-
67
- `data-thd-pending` prevents initial native paint before the entry effect; it does **not** select an element for attachment. Use it only on selected targets. The mask preserves space, is absent without JavaScript, and has a failure fallback. [First-paint setup, CSP and troubleshooting →](https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md#first-paint-setup-avoid-an-initial-flash)
68
-
69
- The classic loader uses an existing `window.THREE` or loads the bundled Three.js r158 asset. Serve examples over HTTP, not `file://`. Existing `THDFree`, `data-thd-*` and `src/dom-*` API/file names are retained in this release.
70
-
71
- ### Use a module
72
-
73
- Provide a compatible Three.js namespace; r158 is the tested version. Call the loader in the browser after the host elements exist:
74
-
75
- ```js
76
- import * as THREE from 'three';
77
- import { loadFree } from '@ev-ry/fx/script';
78
-
79
- const api = await loadFree({ THREE, auto: false });
80
- const free = api.create(document, { auto: false });
81
-
82
- const title = free.attachText(document.querySelector('.title'), {
83
- revealOnView: { threshold: 0.5, once: false }
84
- });
85
-
86
- // Later, on an explicit user action:
87
- // title.play('exit');
88
- // title.cancel();
89
-
90
- // On component unmount: free.destroy();
91
- ```
92
-
93
- Importing the loader does not initialize the page. Its first call chooses automatic initialization; repeated calls share that initialization. Use `auto:true` for automatic scanning, or `auto:false` with `api.create()` for manual ownership. Do not attach the same element both ways.
94
-
95
- For direct ownership with injected Three.js, `createFree` is also exported from `@ev-ry/fx`. See the [API and lifecycle guide](https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md).
96
-
97
- ## Examples and guide
98
-
99
- | Start here | What it demonstrates |
100
- | --- | --- |
101
- | [Script example](https://github.com/kbaghini/evry-fx/blob/main/examples/script.html) | Automatic heading entry, including the early mask |
102
- | [Navigation example](https://github.com/kbaghini/evry-fx/blob/main/examples/navigation.html) | Back/forward navigation and page lifecycle |
103
- | [React example](https://github.com/kbaghini/evry-fx/blob/main/examples/AnimatedTitle.jsx) | A string title with effect cleanup and StrictMode handling |
104
- | [Detailed guide](https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md) | SVG, image swaps, selectors, thresholds, canvas routing and troubleshooting |
105
- | [Persian quickstart](https://github.com/kbaghini/evry-fx/blob/main/QUICKSTART.fa.md) | Installation and integration notes in Persian |
106
- | [Release notes](https://github.com/kbaghini/evry-fx/blob/main/docs/RELEASE-NOTES.md) | Changes, tested scope and remaining limits |
107
-
108
- ## A few useful boundaries
109
-
110
- - Repeated scroll reveals hide and rearm after a **complete exit**. Scroll exit does not run the smoke/melt effect; use `play('exit')` for animated disappearance.
111
- - After adding or removing automatic targets, call `refresh()`. Destroy owned attachments on component unmount; see the guide for back/forward-cache handling.
112
- - Ordinary page content uses a shared document-connected canvas. Special layouts can route to a local canvas. Arbitrary CSS stacking, clipping and typography are not fully reproduced.
113
- - SVG support is a static shape/path subset. Unsupported SVG/CSS and images blocked by canvas CORS restrictions can remain native.
114
- - This Free build does not include editable inputs, arbitrary effect customization or a dedicated React/Vue component pack.
115
-
116
- EV-RY FX Free is [MIT licensed](https://github.com/kbaghini/evry-fx/blob/main/LICENSE). Bundled dependencies retain their own licenses and [notices](https://github.com/kbaghini/evry-fx/blob/main/NOTICE.md). This license covers the Free distribution; other editions are separate.
1
+ <p align="center"><img src="https://raw.githubusercontent.com/kbaghini/evry-fx/main/docs/assets/cover.svg" alt="EV-RY FX — Motion for the text and images already on your page." width="100%"></p>
2
+
3
+ <h1 align="center">EV-RY FX</h1>
4
+
5
+ <p align="center"><strong>Your HTML. Four particle effects. Native when still.</strong></p>
6
+
7
+ <p align="center">Free edition · 0.1.0-rc.2 preview · <a href="https://github.com/kbaghini/evry-fx/blob/main/LICENSE">MIT licensed</a> · JavaScript + TypeScript declarations</p>
8
+
9
+ <p align="center">
10
+ <a href="#quick-start">Quick start</a> ·
11
+ <a href="https://kbaghini.github.io/evry-fx/docs/">Live demo</a> ·
12
+ <a href="https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md">Guide</a> ·
13
+ <a href="https://github.com/kbaghini/evry-fx/blob/main/QUICKSTART.fa.md">راهنمای فارسی</a>
14
+ </p>
15
+
16
+ Add WebGL particle motion to existing headings, display text, images and supported SVG icons. EV-RY FX borrows their content and placement from the page, animates textured particles, then returns to native browser rendering.
17
+
18
+ Start with automatic scroll reveals, or attach selected elements through a small API. Keep your existing layout and components.
19
+
20
+ ## See it move
21
+
22
+ ![EV-RY FX motion preview](https://raw.githubusercontent.com/kbaghini/evry-fx/main/docs/assets/thd-preview.gif)
23
+
24
+ The README shows an animated preview. Open the [live interactive demo](https://kbaghini.github.io/evry-fx/docs/) to try entry and exit effects on real text and images.
25
+
26
+ ## What you get
27
+
28
+ | Content | Entry | Exit |
29
+ | --- | --- | --- |
30
+ | Text | Dust wind | Smoke |
31
+ | Images and supported SVG | Drifting snow | Melt |
32
+
33
+ - **A small automatic setup.** Reveal `h1`, `h2`, `[data-thd-text]` and `img[data-thd-image]` on intersection. Mark exclusions with `data-thd-ignore`.
34
+ - **Manual control when needed.** Attach display text, images or supported static SVG; replay, cancel, refresh and destroy individual attachments. Simple image replacement is included.
35
+ - **Your page keeps ownership.** Native DOM elements retain their content and semantics. Native rendering resumes when entry motion settles.
36
+ - **One visual engine.** Text and media use textured triangular particles, four fixed presets and a 2000ms effect timeline. Individual particles can finish earlier within that timeline.
37
+ - **A practical integration boundary.** Framework-independent JavaScript, typed APIs, a classic script entry and an explicit ES-module loader. A React integration example is included.
38
+
39
+ ## Quick start
40
+
41
+ **Public preview: 0.1.0-rc.2.** Validate the supported content and layouts in your project before production use.
42
+
43
+ Install from your project folder:
44
+
45
+ ```sh
46
+ npm install @ev-ry/fx
47
+ ```
48
+
49
+ ### Add a script
50
+
51
+ Serve the complete installed `node_modules/@ev-ry/fx` directory at `/thd/`. Preserve its modules and asset folders; this is not a single-file bundle.
52
+
53
+ ```html
54
+ <head>
55
+ <!-- Early mask: load before body content, without async or defer. -->
56
+ <script src="/thd/src/dom-reveal-boot.js"></script>
57
+ <script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
58
+ </head>
59
+ <body>
60
+ <h1 data-thd-pending>Small details. A better feeling.</h1>
61
+ <p data-thd-text data-thd-pending>Made for your existing page.</p>
62
+ <img data-thd-image data-thd-pending src="photo.jpg"
63
+ width="640" height="400" alt="Describe your photograph">
64
+ </body>
65
+ ```
66
+
67
+ `data-thd-pending` prevents initial native paint before the entry effect; it does **not** select an element for attachment. Use it only on selected targets. The mask preserves space, is absent without JavaScript, and has a failure fallback. [First-paint setup, CSP and troubleshooting →](https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md#first-paint-setup-avoid-an-initial-flash)
68
+
69
+ The classic loader uses an existing `window.THREE` or loads the bundled Three.js r158 asset. Serve examples over HTTP, not `file://`. Existing `THDFree`, `data-thd-*` and `src/dom-*` API/file names are retained in this release.
70
+
71
+ ### Use a module
72
+
73
+ Provide a compatible Three.js namespace; r158 is the tested version. Call the loader in the browser after the host elements exist:
74
+
75
+ ```js
76
+ import * as THREE from 'three';
77
+ import { loadFree } from '@ev-ry/fx/script';
78
+
79
+ const api = await loadFree({ THREE, auto: false });
80
+ const free = api.create(document, { auto: false });
81
+
82
+ const title = free.attachText(document.querySelector('.title'), {
83
+ revealOnView: { threshold: 0.5, once: false }
84
+ });
85
+
86
+ // Later, on an explicit user action:
87
+ // title.play('exit');
88
+ // title.cancel();
89
+
90
+ // On component unmount: free.destroy();
91
+ ```
92
+
93
+ Importing the loader does not initialize the page. Its first call chooses automatic initialization; repeated calls share that initialization. Use `auto:true` for automatic scanning, or `auto:false` with `api.create()` for manual ownership. Do not attach the same element both ways.
94
+
95
+ For direct ownership with injected Three.js, `createFree` is also exported from `@ev-ry/fx`. See the [API and lifecycle guide](https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md).
96
+
97
+ ## Examples and guide
98
+
99
+ | Start here | What it demonstrates |
100
+ | --- | --- |
101
+ | [Script example](https://github.com/kbaghini/evry-fx/blob/main/examples/script.html) | Automatic heading entry, including the early mask |
102
+ | [Navigation example](https://github.com/kbaghini/evry-fx/blob/main/examples/navigation.html) | Back/forward navigation and page lifecycle |
103
+ | [React example](https://github.com/kbaghini/evry-fx/blob/main/examples/AnimatedTitle.jsx) | A string title with effect cleanup and StrictMode handling |
104
+ | [Detailed guide](https://github.com/kbaghini/evry-fx/blob/main/docs/GUIDE.md) | SVG, image swaps, selectors, thresholds, canvas routing and troubleshooting |
105
+ | [Persian quickstart](https://github.com/kbaghini/evry-fx/blob/main/QUICKSTART.fa.md) | Installation and integration notes in Persian |
106
+ | [Release notes](https://github.com/kbaghini/evry-fx/blob/main/docs/RELEASE-NOTES.md) | Changes, tested scope and remaining limits |
107
+
108
+ ## A few useful boundaries
109
+
110
+ - Repeated scroll reveals hide and rearm after a **complete exit**. Scroll exit does not run the smoke/melt effect; use `play('exit')` for animated disappearance.
111
+ - After adding or removing automatic targets, call `refresh()`. Destroy owned attachments on component unmount; see the guide for back/forward-cache handling.
112
+ - Ordinary page content uses a shared document-connected canvas. Special layouts can route to a local canvas. Arbitrary CSS stacking, clipping and typography are not fully reproduced.
113
+ - SVG support is a static shape/path subset. Unsupported SVG/CSS and images blocked by canvas CORS restrictions can remain native.
114
+ - This Free build does not include editable inputs, arbitrary effect customization or a dedicated React/Vue component pack.
115
+
116
+ EV-RY FX Free is [MIT licensed](https://github.com/kbaghini/evry-fx/blob/main/LICENSE). Bundled dependencies retain their own licenses and [notices](https://github.com/kbaghini/evry-fx/blob/main/NOTICE.md). This license covers the Free distribution; other editions are separate.
package/build-report.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0-rc.1",
2
+ "version": "0.1.0-rc.2",
3
3
  "effects": [
4
4
  "dust-wind",
5
5
  "smoke",
@@ -39,8 +39,8 @@
39
39
  },
40
40
  {
41
41
  "file": "src/image-surface.js",
42
- "bytes": 11834,
43
- "gzip": 4227
42
+ "bytes": 11984,
43
+ "gzip": 4278
44
44
  },
45
45
  {
46
46
  "file": "src/motion-envelope.js",
@@ -144,8 +144,8 @@
144
144
  },
145
145
  {
146
146
  "file": "src/dom-image-raster.js",
147
- "bytes": 1916,
148
- "gzip": 870
147
+ "bytes": 3273,
148
+ "gzip": 1287
149
149
  },
150
150
  {
151
151
  "file": "src/image-preparation-queue.js",
@@ -179,8 +179,8 @@
179
179
  },
180
180
  {
181
181
  "file": "src/dom-text-surface.js",
182
- "bytes": 18914,
183
- "gzip": 6011
182
+ "bytes": 20197,
183
+ "gzip": 6360
184
184
  },
185
185
  {
186
186
  "file": "src/hybrid-text-flow.js",
@@ -194,8 +194,8 @@
194
194
  },
195
195
  {
196
196
  "file": "src/font-rasterizer.js",
197
- "bytes": 3539,
198
- "gzip": 1322
197
+ "bytes": 4384,
198
+ "gzip": 1544
199
199
  },
200
200
  {
201
201
  "file": "src/native-run-shaping.js",
@@ -204,8 +204,8 @@
204
204
  },
205
205
  {
206
206
  "file": "src/text-scene.js",
207
- "bytes": 13688,
208
- "gzip": 3998
207
+ "bytes": 13840,
208
+ "gzip": 4007
209
209
  },
210
210
  {
211
211
  "file": "src/motion.js",
@@ -234,8 +234,8 @@
234
234
  },
235
235
  {
236
236
  "file": "src/dom-surface-font.js",
237
- "bytes": 3246,
238
- "gzip": 1251
237
+ "bytes": 3475,
238
+ "gzip": 1315
239
239
  },
240
240
  {
241
241
  "file": "src/dom-raster-cache.js",
@@ -244,8 +244,8 @@
244
244
  },
245
245
  {
246
246
  "file": "src/dom-rich-text.js",
247
- "bytes": 11269,
248
- "gzip": 4185
247
+ "bytes": 12881,
248
+ "gzip": 4613
249
249
  },
250
250
  {
251
251
  "file": "src/dom-text-runs.js",
@@ -254,8 +254,8 @@
254
254
  },
255
255
  {
256
256
  "file": "src/dom-text-fingerprint.js",
257
- "bytes": 2060,
258
- "gzip": 1005
257
+ "bytes": 2103,
258
+ "gzip": 1021
259
259
  },
260
260
  {
261
261
  "file": "src/dom-auto-route.js",
@@ -323,6 +323,6 @@
323
323
  "gzip": 1971
324
324
  }
325
325
  ],
326
- "bytes": 954714,
327
- "gzipSum": 274468
326
+ "bytes": 960385,
327
+ "gzipSum": 276024
328
328
  }
package/docs/GUIDE.md CHANGED
@@ -1,124 +1,128 @@
1
1
  <!-- Generated by scripts/prepare-free-release.cjs from FREE-PACKAGE.md. -->
2
- # EV-RY FX Free — integration guide
3
-
4
- Four fixed behaviors: text enters with wind and exits as smoke; images enter as snow and exit with melt. Triangle particles, 2000ms, native resting. Existing native semantics remain with the host. SVG/icon attachment and simple image swaps are included with the image presets. Editable controls and other effect recipes remain outside this build. Shared text shaping and rendering utilities remain where required by display text.
5
-
6
- ## Script
7
-
8
- Serve this directory intact at `/thd/`:
9
-
10
- ```html
11
- <script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
12
- ```
13
-
14
- The loader uses existing window.THREE or loads the accompanying r158 asset. After the script executes, await `THDFree.ready`; its `instance` is the automatic installation. Without data-thd-auto, call `(await THDFree.ready).create(document, options)` explicitly. This is a script entry with accompanying modules/assets, not a single-file bundle.
15
-
16
- ## Modules
17
-
18
- ```js
19
- import {createFree} from '@ev-ry/fx';
20
- const free = createFree(THREE, document, {auto: false});
21
- const text = free.attachText(document.querySelector('.title'));
22
- await text.ready;
23
- text.play('exit');
24
- // free.destroy() on page/component unmount.
25
- ```
26
-
27
- Provide a compatible THREE namespace; r158 is the version tested here. The package import itself has no page side effects. Copy the assets directory with its relative paths if your bundler does not preserve new URL assets.
28
-
29
- For explicit initialization from an ES module, use the separate loader entry:
30
-
31
- ```js
32
- import * as THREE from 'three'; // r158 is the tested version.
33
- import {loadFree} from '@ev-ry/fx/script';
34
- const api = await loadFree({THREE, auto: true});
35
- // api.instance is the automatic installation.
36
- ```
37
-
38
- Importing this entry is inert, including during server-side rendering. Call `loadFree()` only in the browser after mounting the document. Without an injected THREE namespace it reuses `window.THREE` or loads the accompanying asset; keep that asset reachable when bundling. Repeated calls reuse the first `THDFree.ready`, so the first initialization chooses `auto`. For manual ownership, use `auto:false` then `api.create(root, options)`. Loaders do not silently start a second automatic installation.
39
-
40
- The classic `<script src="…/src/dom-free-script.js">` URL remains unchanged. `@ev-ry/fx/classic-script` resolves that classic asset; it is not an ES-module import entry. Do not use `type="module"` with the classic asset: use `loadFree` instead.
41
-
42
- Auto selects h1/h2/[data-thd-text] and img[data-thd-image]; data-thd-ignore and interactive/navigation/dialog regions are excluded. Options: auto, textSelector, imageSelector, presentation, threshold, once, intersectionRoot. Call refresh after route/DOM changes. Manual attachment accepts presentation and revealOnView. play/cancel/refresh/stats/destroy are exposed; arbitrary effects are not. Scroll exit hides repeated targets rather than running an exit animation.
43
-
44
- Preparation is lazy for offscreen targets. Native content may paint before a late script initializes; use the optional early mask below for initial-page entry effects. Unsupported content falls back to native presentation. Cross-origin image canvas restrictions and unsupported rich CSS still apply. This does not claim general CSS reproduction or new physical-device validation.
45
-
46
- For an offscreen or zero-area image, `ready` can resolve with temporary native reason `Image not visible`; it does not promise a prepared GPU mesh outside the viewport. Image preparation begins near the viewport (roughly half a viewport height ahead). The native image's network loading policy is unchanged. Resize work is coalesced: the current raster follows the new box briefly, then the final crop and rounded corners are rebuilt after about 50ms of quiet, with a roughly 150ms bound during continuous resizing. Existing motion keeps its timeline.
47
-
48
- Unchanged text reuses validated layout and document-scoped raster pixels. The retained shared pixel cache is bounded at 16MiB; active geometry, textures and referenced pixels are separate, so this is not a total memory limit. Font loading invalidates cached pixels. Unsupported text is shown natively without repeated preparation retries; correct its layout and call `refresh()` to retry. Always destroy attachments on unmount.
49
-
50
- ## First-paint setup: avoid an initial flash
51
-
52
- Load the small boot script synchronously in the head, before body content (no async/defer). Keep the main loader deferred:
53
-
54
- ```html
55
- <head>
56
- <script src="/thd/src/dom-reveal-boot.js"></script>
57
- <script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
58
- </head>
59
- <body>
60
- <h1 data-thd-pending>Hello EV-RY FX</h1>
61
- <img data-thd-image data-thd-pending src="photo.jpg"
62
- width="640" height="400" alt="A descriptive caption">
63
- </body>
64
- ```
65
-
66
- `data-thd-pending` only requests initial masking; it does not select or attach an element. Use it on targets that will actually receive entry effects. The mask preserves layout and transfers to the reveal controller during attachment. Never edit the controller-owned `data-thd-reveal` attribute. Specify image dimensions or aspect-ratio to prevent loading-related layout shifts.
67
-
68
- Without JavaScript the boot mask is never installed. If the engine fails to load, `thd:error` releases the boot mask; an eight-second timer from boot execution also releases unclaimed masks. This timer is a failure fallback, not an animation delay. Late initialization after that timeout cannot guarantee flash-free entry. Once attached, the reveal controller owns its own preparation/error handling. A late-loaded boot script cannot undo an earlier paint. Sites with a restrictive CSP must allow the boot script and its injected style; otherwise omit pre-masking or integrate an equivalent permitted policy.
69
-
70
- ## Reveal, replay and component lifecycle
71
-
72
- Automatic reveal defaults to `threshold:0` and `once:true`. Threshold is visible area ratio: `0` means a positive intersection, `.5` half, `1` full visibility; an element larger than the viewport may never reach `1`.
73
-
74
- ```js
75
- const free = (await THDFree.ready).create(document, {
76
- once: false, threshold: 0.5
77
- }); // Load the script WITHOUT data-thd-auto for this manual setup.
78
- ```
79
-
80
- With `once:false`, a full exit rearms the target and hides it immediately; it does not play the exit effect. Small scroll changes while still intersecting do not rearm. Reentry during unfinished entry resumes the timeline; reentry after completion starts a new entry. Explicit `surface.play('enter')` is a replay request. Use `surface.play('exit')` for animated disappearance.
81
-
82
- Do not attach manually to an automatically selected element. Use `auto:false` for fully manual ownership, or exclude manual targets from the automatic selectors. Call `free.refresh()` after adding/removing targets; discovery is not a blanket DOM mutation watcher. Destroy the surface or installation on component unmount. In React, initialize after the host exists and return cleanup from the effect; see `examples/AnimatedTitle.jsx`. Its children are a string, not an arbitrary React subtree adapter.
83
-
84
- Automatic loader installations survive a persisted `pagehide`/`pageshow` round trip (the browser's back/forward cache). Return refreshes the same owner and preserves completed once-only entries; a non-persisted page exit releases it. A manually destroyed installation is never revived. For installations created with `api.create()` or `createFree()`, lifecycle belongs to the host: avoid destroying solely for `pagehide` when `event.persisted` is true, refresh on persisted return, and still destroy on actual component unmount.
85
-
86
- ## Troubleshooting
87
-
88
- Plain headings that wrap across lines use the existing DOM-measured multi-line text path automatically. A line break caused by available width alone does not require nested spans or a smaller font to animate. Unsupported typography can still retain native fallback.
89
-
90
- Normal/nowrap HTML whitespace is collapsed for raster text while DOM source offsets are preserved. NBSP and preformatted spacing are not globally trimmed. An initially empty manually attached target can reveal after content arrives; a recoverable unsupported layout can reveal after correction and refresh. Automatic scanning still requires `free.refresh()` to discover a previously empty, unattached target. Recovery does not replay an already completed once-only entry.
91
-
92
- | Symptom | Check |
93
- | --- | --- |
94
- | Initial flash | Boot script precedes body paint; target has `data-thd-pending`; no async/defer on boot. |
95
- | Target stays hidden until fallback | Target matches an attachment selector and is not inside an excluded region. |
96
- | No entry at full visibility threshold | Target fits inside the intersection root; try a smaller threshold. |
97
- | Image stays native | Inspect CORS restrictions, image loading and supported CSS; native fallback is intentional. |
98
- | Effect appears above an unrelated layer | Review zIndex and presentation routing; try local presentation for that target. |
99
- | Duplicate work after navigation | Destroy old owners, avoid simultaneous auto/manual attachment, then refresh new targets. |
100
-
101
- See [the Persian quickstart](../QUICKSTART.fa.md), [script example](../examples/script.html) and [phone/history check](../examples/navigation.html). Serve examples over HTTP. The boot helper applies to manual reveal attachment as well; the main loader is not required when using the module API.
102
-
103
- Build specialization strips unused attachment entry points and effect graphs, with assertions that fail on incompatible source changes. It is not source protection: browser code is inspectable. This Free distribution is MIT licensed; see ../LICENSE and ../NOTICE.md. Third-party notices retain their original terms. The private full-product source and unpublished Pro code are outside this distribution.
2
+ # EV-RY FX Free — integration guide
104
3
 
105
-
106
- ## Media additions
107
-
108
- attachSVG(svg, options) uses snow/melt and native resting, supporting the existing static path/shape SVG subset. Unsupported filters, masks, use, embedded text/images and external paint references retain native fallback. This is not arbitrary SVG support.
109
-
110
- swapImage(previous, next, {exit:true, waitForExit:false, presentation:'global'}) returns {finished,cancel}. Both images must be unattached. Entry uses snow; optional exit uses melt. waitForExit sequences them. Arbitrary effect selection stays unavailable. The next image takes the previous image's placement on success; previous is hidden. These APIs reuse the main engine.
4
+ Four fixed behaviors: text enters with wind and exits as smoke; images enter as snow and exit with melt. Triangle particles, 2000ms, native resting. Existing native semantics remain with the host. SVG/icon attachment and simple image swaps are included with the image presets. Editable controls and other effect recipes remain outside this build. Shared text shaping and rendering utilities remain where required by display text.
5
+
6
+ ## Script
7
+
8
+ Serve this directory intact at `/thd/`:
9
+
10
+ ```html
11
+ <script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
12
+ ```
13
+
14
+ The loader uses existing window.THREE or loads the accompanying r158 asset. After the script executes, await `THDFree.ready`; its `instance` is the automatic installation. Without data-thd-auto, call `(await THDFree.ready).create(document, options)` explicitly. This is a script entry with accompanying modules/assets, not a single-file bundle.
15
+
16
+ ## Modules
17
+
18
+ ```js
19
+ import {createFree} from '@ev-ry/fx';
20
+ const free = createFree(THREE, document, {auto: false});
21
+ const text = free.attachText(document.querySelector('.title'));
22
+ await text.ready;
23
+ text.play('exit');
24
+ // free.destroy() on page/component unmount.
25
+ ```
26
+
27
+ Provide a compatible THREE namespace; r158 is the version tested here. The package import itself has no page side effects. Copy the assets directory with its relative paths if your bundler does not preserve new URL assets.
28
+
29
+ For explicit initialization from an ES module, use the separate loader entry:
30
+
31
+ ```js
32
+ import * as THREE from 'three'; // r158 is the tested version.
33
+ import {loadFree} from '@ev-ry/fx/script';
34
+ const api = await loadFree({THREE, auto: true});
35
+ // api.instance is the automatic installation.
36
+ ```
37
+
38
+ Importing this entry is inert, including during server-side rendering. Call `loadFree()` only in the browser after mounting the document. Without an injected THREE namespace it reuses `window.THREE` or loads the accompanying asset; keep that asset reachable when bundling. Repeated calls reuse the first `THDFree.ready`, so the first initialization chooses `auto`. For manual ownership, use `auto:false` then `api.create(root, options)`. Loaders do not silently start a second automatic installation.
39
+
40
+ The classic `<script src="…/src/dom-free-script.js">` URL remains unchanged. `@ev-ry/fx/classic-script` resolves that classic asset; it is not an ES-module import entry. Do not use `type="module"` with the classic asset: use `loadFree` instead.
41
+
42
+ Auto selects h1/h2/[data-thd-text] and img[data-thd-image]; data-thd-ignore and interactive/navigation/dialog regions are excluded. Options: auto, textSelector, imageSelector, presentation, threshold, once, intersectionRoot. Call refresh after route/DOM changes. Manual attachment accepts presentation and revealOnView. play/cancel/refresh/stats/destroy are exposed; arbitrary effects are not. Scroll exit hides repeated targets rather than running an exit animation.
43
+
44
+ Preparation is lazy for offscreen targets. Native content may paint before a late script initializes; use the optional early mask below for initial-page entry effects. Unsupported content falls back to native presentation. Cross-origin image canvas restrictions and unsupported rich CSS still apply. This does not claim general CSS reproduction or new physical-device validation.
45
+
46
+ For an offscreen or zero-area image, `ready` can resolve with temporary native reason `Image not visible`; it does not promise a prepared GPU mesh outside the viewport. Image preparation begins near the viewport (roughly half a viewport height ahead). The native image's network loading policy is unchanged. Resize work is coalesced: the current raster follows the new box briefly, then the final crop and rounded corners are rebuilt after about 50ms of quiet, with a roughly 150ms bound during continuous resizing. Existing motion keeps its timeline.
47
+
48
+ Unchanged text reuses validated layout and document-scoped raster pixels. The retained shared pixel cache is bounded at 16MiB; active geometry, textures and referenced pixels are separate, so this is not a total memory limit. Font loading invalidates cached pixels. Unsupported text is shown natively without repeated preparation retries; correct its layout and call `refresh()` to retry. Always destroy attachments on unmount.
49
+
50
+ ## First-paint setup: avoid an initial flash
51
+
52
+ Load the small boot script synchronously in the head, before body content (no async/defer). Keep the main loader deferred:
53
+
54
+ ```html
55
+ <head>
56
+ <script src="/thd/src/dom-reveal-boot.js"></script>
57
+ <script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
58
+ </head>
59
+ <body>
60
+ <h1 data-thd-pending>Hello EV-RY FX</h1>
61
+ <img data-thd-image data-thd-pending src="photo.jpg"
62
+ width="640" height="400" alt="A descriptive caption">
63
+ </body>
64
+ ```
65
+
66
+ `data-thd-pending` only requests initial masking; it does not select or attach an element. Use it on targets that will actually receive entry effects. The mask preserves layout and transfers to the reveal controller during attachment. Never edit the controller-owned `data-thd-reveal` attribute. Specify image dimensions or aspect-ratio to prevent loading-related layout shifts.
67
+
68
+ Without JavaScript the boot mask is never installed. If the engine fails to load, `thd:error` releases the boot mask; an eight-second timer from boot execution also releases unclaimed masks. This timer is a failure fallback, not an animation delay. Late initialization after that timeout cannot guarantee flash-free entry. Once attached, the reveal controller owns its own preparation/error handling. A late-loaded boot script cannot undo an earlier paint. Sites with a restrictive CSP must allow the boot script and its injected style; otherwise omit pre-masking or integrate an equivalent permitted policy.
69
+
70
+ ## Reveal, replay and component lifecycle
71
+
72
+ Automatic reveal defaults to `threshold:0` and `once:true`. Threshold is visible area ratio: `0` means a positive intersection, `.5` half, `1` full visibility; an element larger than the viewport may never reach `1`.
73
+
74
+ ```js
75
+ const free = (await THDFree.ready).create(document, {
76
+ once: false, threshold: 0.5
77
+ }); // Load the script WITHOUT data-thd-auto for this manual setup.
78
+ ```
79
+
80
+ With `once:false`, a full exit rearms the target and hides it immediately; it does not play the exit effect. Small scroll changes while still intersecting do not rearm. Reentry during unfinished entry resumes the timeline; reentry after completion starts a new entry. Explicit `surface.play('enter')` is a replay request. Use `surface.play('exit')` for animated disappearance.
81
+
82
+ Do not attach manually to an automatically selected element. Use `auto:false` for fully manual ownership, or exclude manual targets from the automatic selectors. Call `free.refresh()` after adding/removing targets; discovery is not a blanket DOM mutation watcher. Destroy the surface or installation on component unmount. In React, initialize after the host exists and return cleanup from the effect; see `examples/AnimatedTitle.jsx`. Its children are a string, not an arbitrary React subtree adapter.
83
+
84
+ Automatic loader installations survive a persisted `pagehide`/`pageshow` round trip (the browser's back/forward cache). Return refreshes the same owner and preserves completed once-only entries; a non-persisted page exit releases it. A manually destroyed installation is never revived. For installations created with `api.create()` or `createFree()`, lifecycle belongs to the host: avoid destroying solely for `pagehide` when `event.persisted` is true, refresh on persisted return, and still destroy on actual component unmount.
85
+
86
+ ## Troubleshooting
87
+
88
+ Plain headings that wrap across lines use the existing DOM-measured multi-line text path automatically. A line break caused by available width alone does not require nested spans or a smaller font to animate. Unsupported typography can still retain native fallback.
89
+
90
+ Normal/nowrap HTML whitespace is collapsed for raster text while DOM source offsets are preserved. NBSP and preformatted spacing are not globally trimmed. An initially empty manually attached target can reveal after content arrives; a recoverable unsupported layout can reveal after correction and refresh. Automatic scanning still requires `free.refresh()` to discover a previously empty, unattached target. Recovery does not replay an already completed once-only entry.
91
+
92
+ | Symptom | Check |
93
+ | --- | --- |
94
+ | Initial flash | Boot script precedes body paint; target has `data-thd-pending`; no async/defer on boot. |
95
+ | Target stays hidden until fallback | Target matches an attachment selector and is not inside an excluded region. |
96
+ | No entry at full visibility threshold | Target fits inside the intersection root; try a smaller threshold. |
97
+ | Image stays native | Inspect CORS restrictions, image loading and supported CSS; native fallback is intentional. |
98
+ | Effect appears above an unrelated layer | Review zIndex and presentation routing; try local presentation for that target. |
99
+ | Duplicate work after navigation | Destroy old owners, avoid simultaneous auto/manual attachment, then refresh new targets. |
100
+
101
+ See [the Persian quickstart](../QUICKSTART.fa.md), [script example](../examples/script.html) and [phone/history check](../examples/navigation.html). Serve examples over HTTP. The boot helper applies to manual reveal attachment as well; the main loader is not required when using the module API.
102
+
103
+ Build specialization strips unused attachment entry points and effect graphs, with assertions that fail on incompatible source changes. It is not source protection: browser code is inspectable. This Free distribution is MIT licensed; see ../LICENSE and ../NOTICE.md. Third-party notices retain their original terms. The private full-product source and unpublished Pro code are outside this distribution.
104
+
105
+
106
+ ## Media additions
107
+
108
+ attachSVG(svg, options) uses snow/melt and native resting, supporting the existing static path/shape SVG subset. Unsupported filters, masks, use, embedded text/images and external paint references retain native fallback. This is not arbitrary SVG support.
109
+
110
+ swapImage(previous, next, {exit:true, waitForExit:false, presentation:'global'}) returns {finished,cancel}. Both images must be unattached. Entry uses snow; optional exit uses melt. waitForExit sequences them. Arbitrary effect selection stays unavailable. The next image takes the previous image's placement on success; previous is hidden. These APIs reuse the main engine.
111
+
112
+
113
+ ### Native image shape and layering
114
+
115
+ Free global canvas defaults to zIndex:1; createFree accepts zIndex to fit the host's layer convention. A header at z-index 20 now stays above the effect. This is an explicit shared-layer policy, not automatic reconstruction of arbitrary stacking contexts.
111
116
 
112
-
113
- ### Native image shape and layering
114
-
115
- Free global canvas defaults to zIndex:1; createFree accepts zIndex to fit the host's layer convention. A header at z-index 20 now stays above the effect. This is an explicit shared-layer policy, not automatic reconstruction of arbitrary stacking contexts.
116
-
117
117
  DOM images are rasterized from their displayed box before meshing, applying supported centered object-fit fill/contain/cover and elliptical/percentage corner radii. The alpha mask travels with particles instead of appearing only on native handoff. Changes in size/fit/radii rebuild that raster. This adds preparation work. Tainted canvas/unsupported layouts retain native fallback. Borders, shadows, arbitrary CSS clipping and complete screenshot equivalence are not claimed.
118
-
119
- ### Default shared canvas (2026-09-14)
120
-
118
+
119
+ ### Default shared canvas (2026-09-14)
120
+
121
121
  createFree defaults to auto routing with documentCanvas:true. Ordinary page effects use the bounded document-connected shared canvas; nested scroll containers, fixed/sticky content and explicit positioned stacking contexts route conservatively to local presentation. Force local with presentation:'local'; select legacy fixed shared rendering with presentation:'global',documentCanvas:false. experimentalDocumentCanvas is retained as a compatibility alias. Dialog-root rendering is unchanged. This is not general CSS stacking equivalence.
122
+
123
+ ## Native text handoff
124
+
125
+ Display text returns to native paint with a 350ms transition. Native text begins fading in 175ms before the entry timeline ends; mesh fade-out starts at the end. Allow preparation plus the 2000ms effect plus 350ms when observing completion; do not destroy a surface on a fixed 2000ms timer. Reduced-motion users bypass the handoff. Unsupported typography still falls back to native immediately.
122
126
 
123
127
  ## Package identity
124
128