nodality 1.3.17 → 1.3.19
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/dist/animator.cjs.js +1 -1
- package/dist/animator.cjs.js.LICENSE.txt +1 -1
- package/dist/animator.esm.js.LICENSE.txt +1 -1
- package/dist/audionew.cjs.js +1 -1
- package/dist/audionew.cjs.js.LICENSE.txt +1 -1
- package/dist/audionew.esm.js.LICENSE.txt +1 -1
- package/dist/base.cjs.js +1 -1
- package/dist/base.cjs.js.LICENSE.txt +1 -1
- package/dist/base.esm.js.LICENSE.txt +1 -1
- package/dist/beta-desktop-bar.cjs.js +1 -1
- package/dist/beta-desktop-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-desktop-bar.esm.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.cjs.js +1 -1
- package/dist/beta-mobile-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.esm.js.LICENSE.txt +1 -1
- package/dist/bundle.umd.js +1 -1
- package/dist/bundle.umd.js.LICENSE.txt +1 -1
- package/dist/button.cjs.js +1 -1
- package/dist/button.cjs.js.LICENSE.txt +1 -1
- package/dist/button.esm.js.LICENSE.txt +1 -1
- package/dist/card-getter.cjs.js.LICENSE.txt +1 -1
- package/dist/card-getter.esm.js.LICENSE.txt +1 -1
- package/dist/center.cjs.js +1 -1
- package/dist/center.cjs.js.LICENSE.txt +1 -1
- package/dist/center.esm.js.LICENSE.txt +1 -1
- package/dist/checkbox.cjs.js +1 -1
- package/dist/checkbox.cjs.js.LICENSE.txt +1 -1
- package/dist/checkbox.esm.js.LICENSE.txt +1 -1
- package/dist/code.cjs.js +1 -1
- package/dist/code.cjs.js.LICENSE.txt +1 -1
- package/dist/code.esm.js.LICENSE.txt +1 -1
- package/dist/container.cjs.js +1 -1
- package/dist/container.cjs.js.LICENSE.txt +1 -1
- package/dist/container.esm.js.LICENSE.txt +1 -1
- package/dist/data-list.cjs.js +1 -1
- package/dist/data-list.cjs.js.LICENSE.txt +1 -1
- package/dist/data-list.esm.js.LICENSE.txt +1 -1
- package/dist/designer.cjs.js +1 -1
- package/dist/designer.cjs.js.LICENSE.txt +1 -1
- package/dist/designer.esm.js +1 -1
- package/dist/designer.esm.js.LICENSE.txt +1 -1
- package/dist/element-mapper.cjs.js +1 -1
- package/dist/element-mapper.cjs.js.LICENSE.txt +1 -1
- package/dist/element-mapper.esm.js +1 -1
- package/dist/element-mapper.esm.js.LICENSE.txt +1 -1
- package/dist/finalresult.esm.js +1 -1
- package/dist/finalresult.esm.js.LICENSE.txt +1 -1
- package/dist/flex-card.cjs.js +1 -1
- package/dist/flex-card.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-card.esm.js.LICENSE.txt +1 -1
- package/dist/flex-grid.cjs.js +1 -1
- package/dist/flex-grid.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-grid.esm.js.LICENSE.txt +1 -1
- package/dist/flex-row.cjs.js +1 -1
- package/dist/flex-row.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-row.esm.js.LICENSE.txt +1 -1
- package/dist/floating-input.cjs.js +1 -1
- package/dist/floating-input.cjs.js.LICENSE.txt +1 -1
- package/dist/floating-input.esm.js.LICENSE.txt +1 -1
- package/dist/free.cjs.js +1 -1
- package/dist/free.cjs.js.LICENSE.txt +1 -1
- package/dist/free.esm.js.LICENSE.txt +1 -1
- package/dist/horizontal-scroller.cjs.js.LICENSE.txt +1 -1
- package/dist/horizontal-scroller.esm.js.LICENSE.txt +1 -1
- package/dist/image-picker.cjs.js +1 -1
- package/dist/image-picker.cjs.js.LICENSE.txt +1 -1
- package/dist/image-picker.esm.js.LICENSE.txt +1 -1
- package/dist/image.cjs.js +1 -1
- package/dist/image.cjs.js.LICENSE.txt +1 -1
- package/dist/image.esm.js +1 -1
- package/dist/image.esm.js.LICENSE.txt +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.LICENSE.txt +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.LICENSE.txt +1 -1
- package/dist/keyframe-animation.cjs.js.LICENSE.txt +1 -1
- package/dist/keyframe-animation.esm.js.LICENSE.txt +1 -1
- package/dist/link-getter.cjs.js +1 -1
- package/dist/link-getter.cjs.js.LICENSE.txt +1 -1
- package/dist/link-getter.esm.js.LICENSE.txt +1 -1
- package/dist/link.cjs.js +1 -1
- package/dist/link.cjs.js.LICENSE.txt +1 -1
- package/dist/link.esm.js.LICENSE.txt +1 -1
- package/dist/meta-adder.cjs.js +1 -1
- package/dist/meta-adder.cjs.js.LICENSE.txt +1 -1
- package/dist/meta-adder.esm.js.LICENSE.txt +1 -1
- package/dist/modal-2025.cjs.js +1 -1
- package/dist/modal-2025.cjs.js.LICENSE.txt +1 -1
- package/dist/modal-2025.esm.js.LICENSE.txt +1 -1
- package/dist/multiswitcher.cjs.js +1 -1
- package/dist/multiswitcher.cjs.js.LICENSE.txt +1 -1
- package/dist/multiswitcher.esm.js.LICENSE.txt +1 -1
- package/dist/new-nav-bar.cjs.js +1 -1
- package/dist/new-nav-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/new-nav-bar.esm.js.LICENSE.txt +1 -1
- package/dist/picker.cjs.js +1 -1
- package/dist/picker.cjs.js.LICENSE.txt +1 -1
- package/dist/picker.esm.js.LICENSE.txt +1 -1
- package/dist/progress.cjs.js +1 -1
- package/dist/progress.cjs.js.LICENSE.txt +1 -1
- package/dist/progress.esm.js.LICENSE.txt +1 -1
- package/dist/radio.cjs.js +1 -1
- package/dist/radio.cjs.js.LICENSE.txt +1 -1
- package/dist/radio.esm.js.LICENSE.txt +1 -1
- package/dist/range.cjs.js +1 -1
- package/dist/range.cjs.js.LICENSE.txt +1 -1
- package/dist/range.esm.js.LICENSE.txt +1 -1
- package/dist/scroll-video.cjs.js.LICENSE.txt +1 -1
- package/dist/scroll-video.esm.js.LICENSE.txt +1 -1
- package/dist/side-bar.cjs.js +1 -1
- package/dist/side-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/side-bar.esm.js.LICENSE.txt +1 -1
- package/dist/side-nav-bar.cjs.js +1 -1
- package/dist/side-nav-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/side-nav-bar.esm.js.LICENSE.txt +1 -1
- package/dist/simple-bar.cjs.js +1 -1
- package/dist/simple-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/simple-bar.esm.js.LICENSE.txt +1 -1
- package/dist/slider-2025.cjs.js +1 -1
- package/dist/slider-2025.cjs.js.LICENSE.txt +1 -1
- package/dist/slider-2025.esm.js.LICENSE.txt +1 -1
- package/dist/spacer.cjs.js +1 -1
- package/dist/spacer.cjs.js.LICENSE.txt +1 -1
- package/dist/spacer.esm.js.LICENSE.txt +1 -1
- package/dist/stack.cjs.js +1 -1
- package/dist/stack.cjs.js.LICENSE.txt +1 -1
- package/dist/stack.esm.js.LICENSE.txt +1 -1
- package/dist/stacker.cjs.js.LICENSE.txt +1 -1
- package/dist/stacker.esm.js.LICENSE.txt +1 -1
- package/dist/table.cjs.js +1 -1
- package/dist/table.cjs.js.LICENSE.txt +1 -1
- package/dist/table.esm.js.LICENSE.txt +1 -1
- package/dist/text-field.cjs.js +1 -1
- package/dist/text-field.cjs.js.LICENSE.txt +1 -1
- package/dist/text-field.esm.js.LICENSE.txt +1 -1
- package/dist/text.cjs.js +1 -1
- package/dist/text.cjs.js.LICENSE.txt +1 -1
- package/dist/text.esm.js.LICENSE.txt +1 -1
- package/dist/theme.cjs.js.LICENSE.txt +1 -1
- package/dist/theme.esm.js.LICENSE.txt +1 -1
- package/dist/transform-anim.cjs.js.LICENSE.txt +1 -1
- package/dist/transform-anim.esm.js.LICENSE.txt +1 -1
- package/dist/ulist.cjs.js +1 -1
- package/dist/ulist.cjs.js.LICENSE.txt +1 -1
- package/dist/ulist.esm.js.LICENSE.txt +1 -1
- package/dist/video.cjs.js +1 -1
- package/dist/video.cjs.js.LICENSE.txt +1 -1
- package/dist/video.esm.js.LICENSE.txt +1 -1
- package/dist/wrap.cjs.js +1 -1
- package/dist/wrap.cjs.js.LICENSE.txt +1 -1
- package/dist/wrap.esm.js.LICENSE.txt +1 -1
- package/dist/zoom-card.cjs.js +1 -1
- package/dist/zoom-card.cjs.js.LICENSE.txt +1 -1
- package/dist/zoom-card.esm.js.LICENSE.txt +1 -1
- package/layout/animator.js +1 -1
- package/layout/audio.js +1 -1
- package/layout/audionew.js +1 -1
- package/layout/base.js +1 -1
- package/layout/beta-desktop-bar.js +1 -1
- package/layout/beta-mobile-bar.js +1 -1
- package/layout/button.js +1 -1
- package/layout/center.js +1 -1
- package/layout/checkbox.js +1 -1
- package/layout/circle.js +1 -1
- package/layout/code.js +1 -1
- package/layout/container.js +1 -1
- package/layout/dropdown-2025.js +1 -1
- package/layout/flex-card.js +1 -1
- package/layout/flex-grid.js +1 -1
- package/layout/flex-row.js +1 -1
- package/layout/form-components/custom.js +1 -1
- package/layout/form-components/data-list.js +1 -1
- package/layout/form-components/floating-input.js +1 -1
- package/layout/form-components/form-all.js +1 -1
- package/layout/form-components/form.js +1 -1
- package/layout/form-components/image-picker.js +1 -1
- package/layout/form-components/picker.js +1 -1
- package/layout/form-components/radio.js +1 -1
- package/layout/form-components/radiogroup.js +1 -1
- package/layout/form-components/range.js +1 -1
- package/layout/free.js +1 -1
- package/layout/grid-overlay.js +1 -1
- package/layout/grid-switcher.js +1 -1
- package/layout/grid.js +1 -1
- package/layout/horizontal-scroller.js +1 -1
- package/layout/image.js +22 -3
- package/layout/index.js +1 -1
- package/layout/link.js +1 -1
- package/layout/list.js +1 -1
- package/layout/meta-adder.js +1 -1
- package/layout/modal-2025.js +1 -1
- package/layout/morph.js +1 -1
- package/layout/multiswitcher.js +1 -1
- package/layout/nav-bar.js +1 -1
- package/layout/nav-factor/custom-div.js +1 -1
- package/layout/new-nav-bar.js +1 -1
- package/layout/polygon.js +1 -1
- package/layout/prerender-site.js +1 -1
- package/layout/prerender.js +1 -1
- package/layout/progress.js +1 -1
- package/layout/row.js +1 -1
- package/layout/scroll-video.js +1 -1
- package/layout/shop.js +8 -1
- package/layout/side-bar.js +1 -1
- package/layout/side-nav-bar.js +1 -1
- package/layout/simple-bar.js +1 -1
- package/layout/slider-2025.js +1 -1
- package/layout/spacer.js +1 -1
- package/layout/stack.js +1 -1
- package/layout/svg.js +1 -1
- package/layout/switcher.js +1 -1
- package/layout/table.js +1 -1
- package/layout/text-field.js +1 -1
- package/layout/text.js +1 -1
- package/layout/ulist.js +1 -1
- package/layout/video.js +1 -1
- package/layout/wrap.js +1 -1
- package/layout/zoom-card.js +1 -1
- package/lib/agent-surface.js +1 -1
- package/lib/card-getter.js +1 -1
- package/lib/check-page.js +58 -1
- package/lib/codegen.js +1 -1
- package/lib/data.js +1 -1
- package/lib/designer.js +1 -1
- package/lib/element-mapper.js +1 -1
- package/lib/element-params.generated.js +1 -1
- package/lib/grid-geometry.js +1 -1
- package/lib/keyframe-animation.js +1 -1
- package/lib/known-inert.js +1 -1
- package/lib/link-getter.js +1 -1
- package/lib/morph-node.js +1 -1
- package/lib/normalize-spec.js +1 -1
- package/lib/parse-html.js +1 -1
- package/lib/raster-inspect.js +1 -1
- package/lib/raster-ops.js +43 -6
- package/lib/raster-presets.js +1 -1
- package/lib/resolve-refs.js +1 -1
- package/lib/scroll-video.js +1 -1
- package/lib/seo.js +1 -1
- package/lib/stacker.js +1 -1
- package/lib/suggest.js +1 -1
- package/lib/theme.js +1 -1
- package/lib/transform-anim.js +1 -1
- package/lib/transition.js +1 -1
- package/lib/validate-nodes.js +29 -2
- package/lib/webmcp-adapter.js +1 -1
- package/package.json +1 -1
package/lib/check-page.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.19
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -274,6 +274,53 @@ const IN_PAGE = () => {
|
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
|
+
|
|
278
|
+
// A shop product's loading state against its loaded state. Until the
|
|
279
|
+
// shop answers, a product shows its static copy (`-static` ids) in the
|
|
280
|
+
// placeholder; then Shopify stamps the <template> and hides the copy. A
|
|
281
|
+
// page can look right in both and still jump between them — or, as on
|
|
282
|
+
// the suitcase site before 1.3.18, be wrong only while loading (price
|
|
283
|
+
// and options run into one line). Nothing above measures that, because
|
|
284
|
+
// nothing overflows. So the stamp is done here, the way Shopify does it,
|
|
285
|
+
// each live element is measured against its static copy, and the stamp
|
|
286
|
+
// is undone. The shop script itself is blocked (see checkPage), so this
|
|
287
|
+
// is deterministic and needs no store.
|
|
288
|
+
for (const ctx of document.querySelectorAll("shopify-context")) {
|
|
289
|
+
const tpl = [...ctx.children].find((c) => c.tagName === "TEMPLATE");
|
|
290
|
+
const ph = [...ctx.children].find((c) => c.hasAttribute("shopify-loading-placeholder"));
|
|
291
|
+
if (!tpl || !ph || ph.hidden || !ph.getClientRects) continue;
|
|
292
|
+
const box = ctx.getBoundingClientRect();
|
|
293
|
+
const at = (el) => {
|
|
294
|
+
const r = el.getBoundingClientRect();
|
|
295
|
+
return { x: r.left - box.left, y: r.top - box.top, w: r.width, h: r.height };
|
|
296
|
+
};
|
|
297
|
+
const before = new Map();
|
|
298
|
+
for (const el of ph.querySelectorAll("[id$='-static']")) {
|
|
299
|
+
if (el.getClientRects().length) before.set(el.id.replace(/-static$/, ""), at(el));
|
|
300
|
+
}
|
|
301
|
+
const stamped = [...tpl.content.cloneNode(true).childNodes];
|
|
302
|
+
stamped.forEach((n) => ctx.appendChild(n));
|
|
303
|
+
ph.hidden = true;
|
|
304
|
+
const prev = ph.style.display;
|
|
305
|
+
ph.style.display = "none";
|
|
306
|
+
let worst = null;
|
|
307
|
+
for (const [id, a] of before) {
|
|
308
|
+
const live = ctx.querySelector(`[id="${CSS.escape(id)}"]`);
|
|
309
|
+
if (!live || !live.getClientRects().length) continue;
|
|
310
|
+
const b = at(live);
|
|
311
|
+
const move = Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y));
|
|
312
|
+
const resize = Math.max(Math.abs(a.w - b.w), Math.abs(a.h - b.h));
|
|
313
|
+
const d = Math.max(move, resize);
|
|
314
|
+
if (d > 8 && (!worst || d > worst.d)) worst = { id, d: Math.round(d), move: Math.round(move), resize: Math.round(resize) };
|
|
315
|
+
}
|
|
316
|
+
stamped.forEach((n) => n.parentNode && n.parentNode.removeChild(n));
|
|
317
|
+
ph.style.display = prev;
|
|
318
|
+
ph.hidden = false;
|
|
319
|
+
if (worst) {
|
|
320
|
+
add("LOADING_LAYOUT_SHIFT", ctx, `#${worst.id}: ${worst.d}px`,
|
|
321
|
+
`the loading copy and the loaded element differ by ${worst.move}px in position and ${worst.resize}px in size — the product jumps when the shop answers, and reads differently until it does`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
277
324
|
return out;
|
|
278
325
|
};
|
|
279
326
|
|
|
@@ -335,6 +382,11 @@ const REPAIRS = {
|
|
|
335
382
|
suggestions: [`maxWidth: "100%" on whichever element the other findings name`],
|
|
336
383
|
valid: ["the document may not be wider than the viewport"],
|
|
337
384
|
},
|
|
385
|
+
LOADING_LAYOUT_SHIFT: {
|
|
386
|
+
suggestions: [`give the static copy the same text and layout keys as the live element`,
|
|
387
|
+
`upgrade nodality: since 1.3.18 the placeholder lays out in the product's own grid`],
|
|
388
|
+
valid: ["a product's loading copy sits where its loaded element will, within 8px"],
|
|
389
|
+
},
|
|
338
390
|
CONTENT_CLIPPED: {
|
|
339
391
|
suggestions: [`height: "auto"`, `overflow: "visible"`],
|
|
340
392
|
valid: ["a box with `overflow: hidden` must be tall enough for its text"],
|
|
@@ -385,6 +437,11 @@ export async function checkPage(html, opts = {}) {
|
|
|
385
437
|
for (const vp of viewports) {
|
|
386
438
|
const page = await browser.newPage({ viewport: { width: vp.width, height: vp.height } });
|
|
387
439
|
try {
|
|
440
|
+
// The shop script never loads here: a product is checked in
|
|
441
|
+
// the state every visitor sees first, the same on every run
|
|
442
|
+
// and without a store. LOADING_LAYOUT_SHIFT compares it with
|
|
443
|
+
// the loaded state by stamping the template itself.
|
|
444
|
+
await page.route(/cdn\.shopify\.com\/storefront\//, (r) => r.abort());
|
|
388
445
|
await page.setContent(doc, { waitUntil });
|
|
389
446
|
const found = await page.evaluate(IN_PAGE);
|
|
390
447
|
for (const f of found) {
|
package/lib/codegen.js
CHANGED
package/lib/data.js
CHANGED
package/lib/designer.js
CHANGED
package/lib/element-mapper.js
CHANGED
package/lib/grid-geometry.js
CHANGED
package/lib/known-inert.js
CHANGED
package/lib/link-getter.js
CHANGED
package/lib/morph-node.js
CHANGED
package/lib/normalize-spec.js
CHANGED
package/lib/parse-html.js
CHANGED
package/lib/raster-inspect.js
CHANGED
package/lib/raster-ops.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.19
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -21,8 +21,12 @@
|
|
|
21
21
|
// snapshot (default) — XMLSerializer -> SVG foreignObject -> Image ->
|
|
22
22
|
// texture. Works in every browser. Nodality output is inline-styled,
|
|
23
23
|
// so fidelity is high. Static: recaptured on resize (or via
|
|
24
|
-
// handle.refresh()). External images
|
|
25
|
-
//
|
|
24
|
+
// handle.refresh()). External images do not load inside the SVG
|
|
25
|
+
// image context (data: URIs do). Web fonts DO arrive: the faces the
|
|
26
|
+
// subtree uses are fetched and embedded as data: URIs (see "Web fonts
|
|
27
|
+
// inside the snapshot"); only a font host that refuses CORS falls back
|
|
28
|
+
// to a local typeface. (This line said web fonts never load until
|
|
29
|
+
// October 2026, long after embedding shipped.)
|
|
26
30
|
// live (the DEFAULT where it applies; opt OUT with `live: false` on any
|
|
27
31
|
// node) — the emerging HTML-in-Canvas API (WICG, Chrome origin trial):
|
|
28
32
|
// the mount subtree moves inside the effect <canvas layoutsubtree> and
|
|
@@ -108,12 +112,26 @@ const DRIVERS = {
|
|
|
108
112
|
};
|
|
109
113
|
const DRIVER_NAMES = Object.keys(DRIVERS);
|
|
110
114
|
|
|
115
|
+
// What each driver does, for describeOps(). The resting behaviour is the part
|
|
116
|
+
// an agent cannot infer from a name: whether the effect is visible before
|
|
117
|
+
// anyone interacts, which decides whether a page looks the same at rest.
|
|
118
|
+
const DRIVER_DOCS = {
|
|
119
|
+
static: "The element's centre at full strength, always on. The default for ops that need a focus.",
|
|
120
|
+
mouse: "Follows the pointer at full strength, and stays on where the pointer last was — visible at rest.",
|
|
121
|
+
hover: "Follows the pointer, faded in and out with hover — nothing shows at rest or without a pointer (a phone).",
|
|
122
|
+
scroll: "A band that travels up the element as it crosses the viewport, at full strength.",
|
|
123
|
+
time: "Hands-free: the focus drifts on a Lissajous path, always on.",
|
|
124
|
+
};
|
|
125
|
+
|
|
111
126
|
const REGISTRY = {
|
|
112
127
|
hexalize: {
|
|
113
128
|
doc: {
|
|
114
129
|
summary: "Hexagonal cell grid. Snaps sampling to a hex lattice and "
|
|
115
130
|
+ "draws cell borders; with `lift`, cells near the driver swell "
|
|
116
|
-
+ "toward the viewer and their content magnifies to match."
|
|
131
|
+
+ "toward the viewer and their content magnifies to match. Away "
|
|
132
|
+
+ "from the focus a lifted chain is untouched — no magnification, "
|
|
133
|
+
+ "no border — so with `by: \"hover\"` the element is "
|
|
134
|
+
+ "pixel-identical at rest and the cells appear only under the pointer.",
|
|
117
135
|
params: {
|
|
118
136
|
size: { default: 24, unit: "px", summary: "lattice pitch — the width of one cell" },
|
|
119
137
|
lift: {
|
|
@@ -442,6 +460,10 @@ ${NB.map(([x, y]) => probe(x, y)).join("")}
|
|
|
442
460
|
},
|
|
443
461
|
},
|
|
444
462
|
stage: "color",
|
|
463
|
+
// It colours `edge`, which only a cell-stage op writes. Declared so the
|
|
464
|
+
// validator can say so (INERT_RASTER_OP) instead of the chain
|
|
465
|
+
// validating clean and drawing nothing.
|
|
466
|
+
needs: "cell",
|
|
445
467
|
decl: (p) => `uniform vec3 ${p}color; uniform float ${p}strength;`,
|
|
446
468
|
code: (p) => `
|
|
447
469
|
{
|
|
@@ -4094,11 +4116,26 @@ function applyRasterPipeline(el, rasterNodes, opts) {
|
|
|
4094
4116
|
|
|
4095
4117
|
if (live) {
|
|
4096
4118
|
canvas.addEventListener("paint", onPaint);
|
|
4097
|
-
|
|
4119
|
+
// The 1500ms is a budget for a page someone is looking at. A hidden
|
|
4120
|
+
// document — a tab opened in the background — paints nothing at all,
|
|
4121
|
+
// so timing it from load sent every background-opened page to the
|
|
4122
|
+
// snapshot for good, even in a browser with the live API. The clock
|
|
4123
|
+
// starts when the page is first shown.
|
|
4124
|
+
const startPaintClock = () => setTimeout(() => {
|
|
4098
4125
|
if (!destroyed && mode === "live" && !textureReady) {
|
|
4099
4126
|
fallbackToSnapshot("no paint event within 1500ms");
|
|
4100
4127
|
}
|
|
4101
4128
|
}, 1500);
|
|
4129
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
4130
|
+
const onShown = () => {
|
|
4131
|
+
if (document.visibilityState === "hidden") return;
|
|
4132
|
+
document.removeEventListener("visibilitychange", onShown);
|
|
4133
|
+
if (!destroyed) startPaintClock();
|
|
4134
|
+
};
|
|
4135
|
+
document.addEventListener("visibilitychange", onShown);
|
|
4136
|
+
} else {
|
|
4137
|
+
startPaintClock();
|
|
4138
|
+
}
|
|
4102
4139
|
}
|
|
4103
4140
|
|
|
4104
4141
|
// Pointer -> u_mouse (listen on the host so the overlay stays
|
|
@@ -4963,7 +5000,7 @@ function applyRasterPipeline(el, rasterNodes, opts) {
|
|
|
4963
5000
|
|
|
4964
5001
|
export {
|
|
4965
5002
|
applyRasterPipeline, registerRasterOp, RASTER_OP_NAMES,
|
|
4966
|
-
isHTMLInCanvasAvailable, DRIVER_NAMES,
|
|
5003
|
+
isHTMLInCanvasAvailable, DRIVER_NAMES, DRIVER_DOCS,
|
|
4967
5004
|
// Phase H3. The registry is already effectively public — registerRasterOp
|
|
4968
5005
|
// mutates it — and a dev tool needs to read an op's stage and params to
|
|
4969
5006
|
// show them. `isStructuralChange` is exported so the inspector can label
|
package/lib/raster-presets.js
CHANGED
package/lib/resolve-refs.js
CHANGED
package/lib/scroll-video.js
CHANGED
package/lib/seo.js
CHANGED
package/lib/stacker.js
CHANGED
package/lib/suggest.js
CHANGED
package/lib/theme.js
CHANGED
package/lib/transform-anim.js
CHANGED
package/lib/transition.js
CHANGED
package/lib/validate-nodes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.19
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import {
|
|
29
|
-
REGISTRY, RASTER_OP_NAMES, DRIVER_NAMES, RASTER_UNITS,
|
|
29
|
+
REGISTRY, RASTER_OP_NAMES, DRIVER_NAMES, DRIVER_DOCS, RASTER_UNITS,
|
|
30
30
|
FRAMEWORK_DOC, EASING_NAMES,
|
|
31
31
|
} from "./raster-ops.js";
|
|
32
32
|
import { presetNames, presetInfo } from "./raster-presets.js";
|
|
@@ -1026,6 +1026,28 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
1026
1026
|
}
|
|
1027
1027
|
});
|
|
1028
1028
|
|
|
1029
|
+
// An op that only colours what an earlier stage produced draws nothing
|
|
1030
|
+
// without it: `edges` reads the cell borders a cell op (hexalize) writes.
|
|
1031
|
+
// The chain is per target — raster nodes on one element compile into one
|
|
1032
|
+
// pass, in array order — so the producer must come earlier on the same
|
|
1033
|
+
// target. Declared on the op (`needs`), not listed here.
|
|
1034
|
+
const stagesOf = (d) => [].concat((d && d.stage) || []);
|
|
1035
|
+
const seen = new Map(); // target key -> stages written so far
|
|
1036
|
+
nodes.forEach((node, i) => {
|
|
1037
|
+
if (!node || typeof node.op !== "string") return;
|
|
1038
|
+
const d = REGISTRY[node.op];
|
|
1039
|
+
if (!d) return;
|
|
1040
|
+
const key = Array.isArray(node.target) ? node.target.map((t) => String(t).replace(/^#/, "")).sort().join(",") : "";
|
|
1041
|
+
const have = seen.get(key) || new Set();
|
|
1042
|
+
if (d.needs && !have.has(d.needs)) {
|
|
1043
|
+
const producers = RASTER_OP_NAMES.filter((n) => stagesOf(REGISTRY[n]).includes(d.needs));
|
|
1044
|
+
warn("INERT_RASTER_OP", `nodes[${i}].op`, node.op, producers, producers,
|
|
1045
|
+
`"${node.op}" draws nothing unless a ${d.needs}-stage op (${producers.join(", ")}) comes before it on the same target.`);
|
|
1046
|
+
}
|
|
1047
|
+
stagesOf(d).forEach((s) => have.add(s));
|
|
1048
|
+
seen.set(key, have);
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1029
1051
|
return { ok: errors.length === 0, errors, warnings };
|
|
1030
1052
|
}
|
|
1031
1053
|
|
|
@@ -1043,6 +1065,8 @@ export function describeOps() {
|
|
|
1043
1065
|
op: name,
|
|
1044
1066
|
stage: Array.isArray(def.stage) ? def.stage : [def.stage].filter(Boolean),
|
|
1045
1067
|
summary: doc.summary || "",
|
|
1068
|
+
// What must come before it on the same target for it to draw.
|
|
1069
|
+
...(def.needs ? { needs: def.needs } : {}),
|
|
1046
1070
|
params: Object.keys(params).map((p) => ({
|
|
1047
1071
|
name: p,
|
|
1048
1072
|
default: params[p].default,
|
|
@@ -1071,6 +1095,9 @@ export function describeOps() {
|
|
|
1071
1095
|
summary: FRAMEWORK_DOC[k].summary || "",
|
|
1072
1096
|
})),
|
|
1073
1097
|
drivers: DRIVER_NAMES,
|
|
1098
|
+
// Names stay a plain list above for existing readers; this says what
|
|
1099
|
+
// each does, including whether anything shows at rest.
|
|
1100
|
+
driverDocs: DRIVER_NAMES.map((name) => ({ name, summary: DRIVER_DOCS[name] || "" })),
|
|
1074
1101
|
easings: EASING_NAMES,
|
|
1075
1102
|
units: RASTER_UNITS,
|
|
1076
1103
|
};
|
package/lib/webmcp-adapter.js
CHANGED