nodality 1.0.194 → 1.0.196

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 (168) hide show
  1. package/dist/animator.cjs.js +1 -1
  2. package/dist/animator.esm.js +1 -1
  3. package/dist/audionew.cjs.js +1 -1
  4. package/dist/audionew.esm.js +1 -1
  5. package/dist/beta-desktop-bar.cjs.js +1 -1
  6. package/dist/beta-desktop-bar.esm.js +1 -1
  7. package/dist/beta-mobile-bar.cjs.js +1 -1
  8. package/dist/beta-mobile-bar.esm.js +1 -1
  9. package/dist/bundle.umd.js +1 -1
  10. package/dist/button.cjs.js +1 -1
  11. package/dist/button.esm.js +1 -1
  12. package/dist/center.cjs.js +1 -1
  13. package/dist/center.esm.js +1 -1
  14. package/dist/checkbox.cjs.js +1 -1
  15. package/dist/checkbox.esm.js +1 -1
  16. package/dist/code.cjs.js +1 -1
  17. package/dist/code.esm.js +1 -1
  18. package/dist/container.cjs.js +1 -1
  19. package/dist/container.esm.js +1 -1
  20. package/dist/designer.cjs.js +1 -1
  21. package/dist/designer.esm.js +1 -1
  22. package/dist/dropdown.cjs.js +1 -1
  23. package/dist/dropdown.esm.js +1 -1
  24. package/dist/element-mapper.cjs.js +1 -1
  25. package/dist/element-mapper.esm.js +1 -1
  26. package/dist/finalresult.esm.js +1 -1
  27. package/dist/flex-card.cjs.js +1 -1
  28. package/dist/flex-card.esm.js +1 -1
  29. package/dist/flex-grid.cjs.js +1 -1
  30. package/dist/flex-grid.esm.js +1 -1
  31. package/dist/flex-row.cjs.js +1 -1
  32. package/dist/flex-row.esm.js +1 -1
  33. package/dist/floating-input.cjs.js +1 -1
  34. package/dist/floating-input.esm.js +1 -1
  35. package/dist/free.cjs.js +1 -1
  36. package/dist/free.esm.js +1 -1
  37. package/dist/image-picker.cjs.js +1 -1
  38. package/dist/image-picker.esm.js +1 -1
  39. package/dist/image.cjs.js +1 -1
  40. package/dist/image.esm.js +1 -1
  41. package/dist/index.cjs.js +1 -1
  42. package/dist/index.esm.js +1 -1
  43. package/dist/link-getter.cjs.js +1 -1
  44. package/dist/link-getter.esm.js +1 -1
  45. package/dist/link.cjs.js +1 -1
  46. package/dist/link.esm.js +1 -1
  47. package/dist/multiswitcher.cjs.js +1 -1
  48. package/dist/multiswitcher.esm.js +1 -1
  49. package/dist/new-nav-bar.cjs.js +1 -1
  50. package/dist/new-nav-bar.esm.js +1 -1
  51. package/dist/picker.cjs.js +1 -1
  52. package/dist/picker.esm.js +1 -1
  53. package/dist/side-nav-bar.cjs.js +1 -1
  54. package/dist/side-nav-bar.esm.js +1 -1
  55. package/dist/simple-bar.cjs.js +1 -1
  56. package/dist/simple-bar.esm.js +1 -1
  57. package/dist/slider-2025.cjs.js +1 -1
  58. package/dist/slider-2025.esm.js +1 -1
  59. package/dist/table.cjs.js +1 -1
  60. package/dist/table.esm.js +1 -1
  61. package/dist/text-field.cjs.js +1 -1
  62. package/dist/text-field.esm.js +1 -1
  63. package/dist/text.cjs.js +1 -1
  64. package/dist/text.esm.js +1 -1
  65. package/dist/ulist.cjs.js +1 -1
  66. package/dist/ulist.esm.js +1 -1
  67. package/dist/video.cjs.js +1 -1
  68. package/dist/video.esm.js +1 -1
  69. package/dist/zoom-card.cjs.js +1 -1
  70. package/dist/zoom-card.esm.js +1 -1
  71. package/layout/animator.js +1 -1
  72. package/layout/audio.js +1 -1
  73. package/layout/audionew.js +1 -1
  74. package/layout/base-2.js +1 -1
  75. package/layout/base.js +1 -1
  76. package/layout/beta-desktop-bar.js +1 -1
  77. package/layout/beta-mobile-bar.js +1 -1
  78. package/layout/box.js +1 -1
  79. package/layout/button.js +1 -1
  80. package/layout/cards.js +1 -1
  81. package/layout/center.js +1 -1
  82. package/layout/checkbox.js +1 -1
  83. package/layout/circle.js +1 -1
  84. package/layout/clean-row.js +1 -1
  85. package/layout/code.js +1 -1
  86. package/layout/container.js +1 -1
  87. package/layout/custom.js +1 -1
  88. package/layout/div-image.js +1 -1
  89. package/layout/dropdown-2025.js +1 -1
  90. package/layout/dropdown.js +1 -1
  91. package/layout/empty-element.js +1 -1
  92. package/layout/external-stylesheet.js +1 -1
  93. package/layout/flex-card.js +1 -1
  94. package/layout/flex-grid.js +1 -1
  95. package/layout/flex-row.js +1 -1
  96. package/layout/footer.js +1 -1
  97. package/layout/form-components/custom.js +1 -1
  98. package/layout/form-components/data-list.js +1 -1
  99. package/layout/form-components/floating-input.js +1 -1
  100. package/layout/form-components/form-all.js +1 -1
  101. package/layout/form-components/form.js +1 -1
  102. package/layout/form-components/image-picker.js +1 -1
  103. package/layout/form-components/picker.js +1 -1
  104. package/layout/form-components/radio.js +1 -1
  105. package/layout/form-components/radiogroup.js +1 -1
  106. package/layout/form-components/range.js +1 -1
  107. package/layout/free.js +1 -1
  108. package/layout/grid-new.js +1 -1
  109. package/layout/grid-switcher.js +1 -1
  110. package/layout/grid.js +1 -1
  111. package/layout/group.js +1 -1
  112. package/layout/header.js +1 -1
  113. package/layout/horizontal-scroller.js +1 -1
  114. package/layout/image-old.js +1 -1
  115. package/layout/image.js +1 -1
  116. package/layout/index.js +1 -1
  117. package/layout/label.js +1 -1
  118. package/layout/link.js +1 -1
  119. package/layout/list-OLD.js +1 -1
  120. package/layout/list.js +1 -1
  121. package/layout/meta-adder.js +1 -1
  122. package/layout/modal-2025.js +1 -1
  123. package/layout/modernwrap.js +1 -1
  124. package/layout/multiswitcher.js +1 -1
  125. package/layout/multiswitcherBeta.js +1 -1
  126. package/layout/nav-bar.js +1 -1
  127. package/layout/nav-factor/custom-div.js +1 -1
  128. package/layout/navBar-OLD.js +1 -1
  129. package/layout/new-flat-adder.js +1 -1
  130. package/layout/new-nav-bar.js +1 -1
  131. package/layout/offset-container.js +1 -1
  132. package/layout/polygon.js +1 -1
  133. package/layout/prerender-site.js +1 -1
  134. package/layout/prerender.js +1 -1
  135. package/layout/progress.js +1 -1
  136. package/layout/row.js +1 -1
  137. package/layout/saved-new-nav-bar.js +1 -1
  138. package/layout/scroll-video.js +1 -1
  139. package/layout/side-bar.js +1 -1
  140. package/layout/side-nav-bar.js +1 -1
  141. package/layout/simple-bar.js +1 -1
  142. package/layout/slider-2025.js +1 -1
  143. package/layout/spacer.js +1 -1
  144. package/layout/stack.js +1 -1
  145. package/layout/styler.js +1 -1
  146. package/layout/svg.js +1 -1
  147. package/layout/switcher.js +1 -1
  148. package/layout/table.js +1 -1
  149. package/layout/text-field.js +1 -1
  150. package/layout/text.js +1 -1
  151. package/layout/ulist.js +1 -1
  152. package/layout/video.js +1 -1
  153. package/layout/without-new.js +1 -1
  154. package/layout/wrap.js +1 -1
  155. package/layout/zoom-card.js +1 -1
  156. package/lib/card-getter.js +1 -1
  157. package/lib/data.js +1 -1
  158. package/lib/designer.js +1 -1
  159. package/lib/element-mapper.js +1 -1
  160. package/lib/keyframe-animation.js +1 -1
  161. package/lib/link-getter.js +1 -1
  162. package/lib/raster-ops.js +120 -21
  163. package/lib/scroll-video.js +1 -1
  164. package/lib/seo.js +1 -1
  165. package/lib/stacker.js +1 -1
  166. package/lib/theme.js +1 -1
  167. package/lib/transform-anim.js +1 -1
  168. package/package.json +1 -1
package/lib/raster-ops.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.194
2
+ * nodality v1.0.196
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
@@ -170,16 +170,17 @@ const REGISTRY = {
170
170
  // samples the finished field. See the `field` descriptor below and
171
171
  // the field-simulation block in applyRasterPipeline().
172
172
  //
173
- // Formulation (deliberately not the usual Stable-Fluids solver):
174
- // the classic GPU fluid pipeline is advect + vorticity confinement +
175
- // a divergence/pressure Jacobi solve (Stam 1999; Harris, GPU Gems
176
- // 2003) roughly ten passes. This op instead advects momentum along
177
- // its own flow (semi-Lagrangian backtrace, the one genuinely
178
- // universal piece) and then ROTATES each momentum vector by the
179
- // local curl angle. Rotation preserves magnitude, so eddies keep
180
- // spinning instead of being pushed around, and because it cannot
181
- // inject divergence energy the field stays stable with no pressure
182
- // projection at all the whole simulation is a single pass.
173
+ // Formulation: the classic GPU Stable-Fluids pipeline (Stam 1999;
174
+ // Harris, GPU Gems 2003) splat, curl, vorticity confinement,
175
+ // divergence, a Jacobi pressure solve, gradient subtraction, then
176
+ // semi-Lagrangian advection of velocity and dye. See the provenance
177
+ // note on the `stir` field descriptor below for the citations and
178
+ // for what this op inherits from existing implementations.
179
+ //
180
+ // An earlier version of this comment claimed the op was a single
181
+ // pass with no pressure projection. It never was the solver has
182
+ // always run the full projection, which is what makes the flow roll
183
+ // into persistent vortices rather than smear.
183
184
  //
184
185
  // { op: "stir", strength: 26, curl: 2.4, decay: 0.985 }
185
186
  //
@@ -544,8 +545,31 @@ void main() {
544
545
  // Stirred liquid: an incompressible fluid whose velocity field warps
545
546
  // the content and whose dye field colours it.
546
547
  //
547
- // Implemented from the published algorithm, not from any particular
548
- // existing codebase:
548
+ // Provenance. The solver was written from the published papers, and
549
+ // no code was copied from an existing implementation — the neighbour
550
+ // taps are computed in-fragment from gl_FragCoord rather than passed
551
+ // down as vL/vR/vT/vB varyings, advection samples directly instead of
552
+ // through a hand-rolled bilerp(), and the rainbow hue comes from the
553
+ // stroke direction rather than a random HSV pick. But it is not
554
+ // independent of that lineage either, and two things are inherited
555
+ // rather than derived from the papers:
556
+ //
557
+ // - The option vocabulary and several defaults (curl, pressure,
558
+ // pressureIterations, radius, force, intensity, rainbow,
559
+ // dyeResolution; resolution 128, dyeResolution 512, pressure 0.8)
560
+ // follow the conventions of Pavel Dobryakov's
561
+ // WebGL-Fluid-Simulation (MIT) and its many descendants.
562
+ // - The free-slip boundary handling — four inline conditionals in
563
+ // the divergence shader that mirror the centre velocity at the
564
+ // walls — is that codebase's approach. Stam and Harris both use a
565
+ // separate boundary pass over border geometry instead.
566
+ //
567
+ // Everything below that looks identical to other implementations
568
+ // beyond those two points is identical because the mathematics admits
569
+ // one spelling: (r+l+t+b-d)*0.25 is the Jacobi iteration as printed
570
+ // in GPU Gems 38, and the divergence, curl and confinement kernels
571
+ // are the standard central differences.
572
+ //
549
573
  // J. Stam, "Stable Fluids", SIGGRAPH '99, 121-128 - the
550
574
  // unconditionally stable operator splitting used here: add
551
575
  // forces, advect by a backward particle trace, then project the
@@ -1425,11 +1449,18 @@ function freezeViewportUnits(original, clone) {
1425
1449
  */
1426
1450
  function soleImageOf(el) {
1427
1451
  if (!el) return null;
1428
- const img = el.tagName === "IMG"
1429
- ? el
1430
- : (el.children && el.children.length === 1 && el.children[0].tagName === "IMG"
1431
- ? el.children[0]
1432
- : null);
1452
+ let img = null;
1453
+ if (el.tagName === "IMG") {
1454
+ img = el;
1455
+ } else if (el.children) {
1456
+ // Ignore the pipeline's own canvas: by the time a capture runs it has
1457
+ // already been appended to the host, so a strict one-child test never
1458
+ // matches and a wrapped photo falls back to the foreignObject path —
1459
+ // which cannot fetch the image and captures blank.
1460
+ const content = Array.prototype.filter.call(
1461
+ el.children, (n) => !n.hasAttribute || !n.hasAttribute("data-nodality-raster"));
1462
+ if (content.length === 1 && content[0].tagName === "IMG") img = content[0];
1463
+ }
1433
1464
  if (!img || !img.complete || !img.naturalWidth) return null;
1434
1465
  try {
1435
1466
  // Relative and same-origin absolute srcs both resolve clean here.
@@ -1442,9 +1473,70 @@ function soleImageOf(el) {
1442
1473
  return img;
1443
1474
  }
1444
1475
 
1476
+ /**
1477
+ * Draw `img` into an offscreen canvas at the host's box, honouring the
1478
+ * element's own `object-fit` / `object-position`.
1479
+ *
1480
+ * The pipeline samples its texture as `fragCoord / u_res`, i.e. stretched
1481
+ * across the whole quad. That is right for a DOM snapshot, which is captured
1482
+ * at the canvas aspect — but the direct-image path uploads the source at its
1483
+ * NATURAL aspect, so any host whose box is a different shape rendered the
1484
+ * photo distorted, and `object-fit: cover` no longer had any say because the
1485
+ * image was no longer being laid out by CSS at all.
1486
+ *
1487
+ * Baking the fit in here keeps it out of the shader and means the texture
1488
+ * always arrives pre-cropped to the box the quad expects.
1489
+ */
1490
+ function fitImageToBox(img, w, h, dpr) {
1491
+ const cw = Math.max(1, Math.round(w * dpr));
1492
+ const ch = Math.max(1, Math.round(h * dpr));
1493
+ const iw = img.naturalWidth, ih = img.naturalHeight;
1494
+ if (!iw || !ih) return img;
1495
+
1496
+ let fit = "fill", pos = "50% 50%";
1497
+ if (typeof getComputedStyle === "function") {
1498
+ const cs = getComputedStyle(img);
1499
+ fit = cs.objectFit || "fill";
1500
+ pos = cs.objectPosition || "50% 50%";
1501
+ }
1502
+ // `fill` is the CSS default and matches the old stretch behaviour, so
1503
+ // leave those callers on the cheap path.
1504
+ if (fit === "fill") return img;
1505
+
1506
+ const sx = cw / iw, sy = ch / ih;
1507
+ const scale = fit === "cover" ? Math.max(sx, sy)
1508
+ : fit === "contain" ? Math.min(sx, sy)
1509
+ : fit === "none" ? 1
1510
+ : fit === "scale-down" ? Math.min(1, Math.min(sx, sy))
1511
+ : Math.max(sx, sy);
1512
+
1513
+ const dw = iw * scale, dh = ih * scale;
1514
+ // object-position: percentages position the overflow, so 0% pins the
1515
+ // near edge and 100% the far edge.
1516
+ const parts = String(pos).trim().split(/\s+/);
1517
+ const frac = (v, i) => {
1518
+ if (v == null) return 0.5;
1519
+ if (v.endsWith("%")) return parseFloat(v) / 100;
1520
+ if (v === "left" || v === "top") return 0;
1521
+ if (v === "right" || v === "bottom") return 1;
1522
+ if (v === "center") return 0.5;
1523
+ const px = parseFloat(v);
1524
+ return isNaN(px) ? 0.5 : px / (i === 0 ? Math.max(cw - dw, 1) : Math.max(ch - dh, 1));
1525
+ };
1526
+ const fx = frac(parts[0], 0);
1527
+ const fy = frac(parts.length > 1 ? parts[1] : parts[0], 1);
1528
+
1529
+ const c = document.createElement("canvas");
1530
+ c.width = cw; c.height = ch;
1531
+ const ctx = c.getContext("2d");
1532
+ if (!ctx) return img;
1533
+ ctx.drawImage(img, (cw - dw) * fx, (ch - dh) * fy, dw, dh);
1534
+ return c;
1535
+ }
1536
+
1445
1537
  function snapshotToImage(el, w, h, dpr) {
1446
1538
  const direct = soleImageOf(el);
1447
- if (direct) return Promise.resolve(direct);
1539
+ if (direct) return Promise.resolve(fitImageToBox(direct, w, h, dpr));
1448
1540
 
1449
1541
  return new Promise((resolve, reject) => {
1450
1542
  const serialized = new XMLSerializer()
@@ -1622,11 +1714,18 @@ function applyRasterPipeline(el, rasterNodes) {
1622
1714
  // Content-box measurement: some elements (fluid-sized Text) keep a
1623
1715
  // collapsed layout box while their glyphs overflow it, so fall back
1624
1716
  // to scroll dimensions when the rect is degenerate.
1717
+ //
1718
+ // The fallback is per-axis and only for a genuinely collapsed box. It
1719
+ // used to be an unconditional max(rect, scroll), which cannot shrink:
1720
+ // the canvas is a CHILD of the measured element, so once it has been
1721
+ // sized up its own width holds scrollWidth at the old value, the max
1722
+ // never comes down, and a host that is animating smaller (a hero
1723
+ // collapsing as you scroll back up) leaves the canvas stuck wide.
1625
1724
  const measure = () => {
1626
1725
  const r = el.getBoundingClientRect();
1627
1726
  return {
1628
- width: Math.max(r.width, el.scrollWidth || 0),
1629
- height: Math.max(r.height, el.scrollHeight || 0),
1727
+ width: r.width >= 2 ? r.width : (el.scrollWidth || 0),
1728
+ height: r.height >= 2 ? r.height : (el.scrollHeight || 0),
1630
1729
  };
1631
1730
  };
1632
1731
  const rect = measure();
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.194
2
+ * nodality v1.0.196
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
package/lib/seo.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.194
2
+ * nodality v1.0.196
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
package/lib/stacker.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.194
2
+ * nodality v1.0.196
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
package/lib/theme.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.194
2
+ * nodality v1.0.196
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.194
2
+ * nodality v1.0.196
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodality",
3
- "version": "1.0.194",
3
+ "version": "1.0.196",
4
4
  "description": "A lightweight library for declarative UI elements.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",