nodality 1.0.186 → 1.0.187

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 +121 -13
  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.186
2
+ * nodality v1.0.187
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
@@ -955,9 +955,38 @@ ${defaultSeam}
955
955
 
956
956
  // ── Snapshot backend: DOM subtree -> SVG foreignObject -> texture ────
957
957
 
958
+ // Viewport-relative CSS inside a foreignObject resolves against the SVG
959
+ // element's own size, not the browser viewport — so `font-size: calc(
960
+ // 1.625rem + 5.075vw)` renders at a different size in the snapshot than
961
+ // it does on the page. The glyphs then sit somewhere the DOM text does
962
+ // not, which is what makes the selection highlight look offset.
963
+ //
964
+ // Freeze those declarations to the pixel values the element actually
965
+ // computes, on a clone, so the snapshot matches the live layout.
966
+ const VIEWPORT_UNIT = /\d(?:vw|vh|vmin|vmax|dvw|dvh|svw|svh|lvw|lvh)\b/i;
967
+
968
+ function freezeViewportUnits(original, clone) {
969
+ const origs = [original, ...original.querySelectorAll("*")];
970
+ const clones = [clone, ...clone.querySelectorAll("*")];
971
+ for (let i = 0; i < origs.length && i < clones.length; i++) {
972
+ const inline = clones[i].style;
973
+ if (!inline || inline.length === 0) continue;
974
+ let computed = null;
975
+ // Iterate a snapshot of the names: writing to style mutates the list.
976
+ for (const prop of Array.from(inline)) {
977
+ if (!VIEWPORT_UNIT.test(inline.getPropertyValue(prop))) continue;
978
+ computed = computed || window.getComputedStyle(origs[i]);
979
+ const px = computed.getPropertyValue(prop);
980
+ if (px) inline.setProperty(prop, px, inline.getPropertyPriority(prop));
981
+ }
982
+ }
983
+ return clone;
984
+ }
985
+
958
986
  function snapshotToImage(el, w, h, dpr) {
959
987
  return new Promise((resolve, reject) => {
960
- const serialized = new XMLSerializer().serializeToString(el);
988
+ const serialized = new XMLSerializer()
989
+ .serializeToString(freezeViewportUnits(el, el.cloneNode(true)));
961
990
  const svg =
962
991
  `<svg xmlns="http://www.w3.org/2000/svg" width="${w * dpr}" height="${h * dpr}" viewBox="0 0 ${w} ${h}">` +
963
992
  `<foreignObject width="100%" height="100%">` +
@@ -1067,6 +1096,16 @@ function applyRasterPipeline(el, rasterNodes) {
1067
1096
  // chrome://flags/#canvas-draw-element). layoutsubtree was already
1068
1097
  // set before context creation above.
1069
1098
  const wrap = document.createElement("div");
1099
+ // The wrapper MUST fill the canvas box. Left to itself it
1100
+ // shrink-wraps its content — a headline wraps to the text width,
1101
+ // not the element width — and texElementImage2D then captures
1102
+ // that narrower box while the shader maps the texture across the
1103
+ // whole canvas. Everything comes out stretched horizontally by
1104
+ // canvasWidth/contentWidth, so the drawn glyphs no longer sit
1105
+ // where the DOM text does and selection lands on the wrong
1106
+ // characters.
1107
+ wrap.style.cssText =
1108
+ `display:block;width:${rect.width}px;height:${rect.height}px;`;
1070
1109
  while (el.firstChild) wrap.appendChild(el.firstChild);
1071
1110
  canvas.appendChild(wrap);
1072
1111
  sourceEl = wrap;
@@ -1075,13 +1114,24 @@ function applyRasterPipeline(el, rasterNodes) {
1075
1114
  canvas.style.cssText =
1076
1115
  `display:block;width:${rect.width}px;height:${rect.height}px;`;
1077
1116
  } else {
1078
- // Snapshot mode: purely visual overlay; the real DOM stays put
1079
- // underneath for accessibility, selection and events.
1117
+ // Snapshot mode: purely visual overlay.
1080
1118
  canvas.style.cssText =
1081
1119
  "position:absolute;top:0;left:0;" +
1082
1120
  `width:${rect.width}px;height:${rect.height}px;` +
1083
1121
  "pointer-events:none;z-index:2147483000;";
1084
- canvas.setAttribute("aria-hidden", "true");
1122
+ if (pureOverlay) {
1123
+ // The real content is still visible underneath and keeps its
1124
+ // own semantics, so the canvas is pure decoration.
1125
+ canvas.setAttribute("aria-hidden", "true");
1126
+ } else {
1127
+ // In-place: the host gets hidden below, which would take its
1128
+ // text out of the accessibility tree, out of find-in-page and
1129
+ // out of innerText. Carry the label onto the canvas so the
1130
+ // element is still announced rather than silently vanishing.
1131
+ canvas.setAttribute("role", "img");
1132
+ const label = (el.textContent || "").replace(/\s+/g, " ").trim();
1133
+ if (label) canvas.setAttribute("aria-label", label.slice(0, 300));
1134
+ }
1085
1135
  }
1086
1136
  el.appendChild(canvas);
1087
1137
 
@@ -1335,6 +1385,65 @@ function applyRasterPipeline(el, rasterNodes) {
1335
1385
  // texture is live, the host's own painting is hidden via the
1336
1386
  // visibility trick (the canvas child re-shows itself) so transparent
1337
1387
  // hosts — e.g. a bare headline — don't ghost-double under the effect.
1388
+ // Keeping the host selectable.
1389
+ // --------------------------------------------------------------
1390
+ // The host must NOT be visibility:hidden. Hidden text cannot be
1391
+ // selected, so dragging over the effect either selects nothing or
1392
+ // catches neighbouring content — which is what makes the highlight
1393
+ // look offset and unreliable. Instead the host stays in the flow
1394
+ // with its ink made transparent, and the canvas is painted BEHIND
1395
+ // it. The glyphs are invisible but still hit-testable, and the
1396
+ // browser paints the selection highlight above the canvas, aligned
1397
+ // with the real text, because that is where the text actually is.
1398
+ const INK = {
1399
+ "color": "transparent",
1400
+ "-webkit-text-fill-color": "transparent",
1401
+ "-webkit-text-stroke-color": "transparent",
1402
+ "background-color": "transparent",
1403
+ "background-image": "none",
1404
+ "border-color": "transparent",
1405
+ "box-shadow": "none",
1406
+ "text-shadow": "none",
1407
+ };
1408
+ const REPLACED = "img,svg,video,picture,iframe";
1409
+ let inkSaved = null;
1410
+
1411
+ const hideHostInk = () => {
1412
+ if (inkSaved) return;
1413
+ inkSaved = [];
1414
+ for (const node of [el, ...el.querySelectorAll("*")]) {
1415
+ if (node === canvas || node.tagName === "CANVAS") continue;
1416
+ const saved = { op: node.style.getPropertyValue("opacity") };
1417
+ for (const p in INK) saved[p] = node.style.getPropertyValue(p);
1418
+ inkSaved.push([node, saved]);
1419
+ for (const p in INK) node.style.setProperty(p, INK[p], "important");
1420
+ // Replaced content has no "ink" property to neutralise.
1421
+ if (node !== el && node.matches && node.matches(REPLACED)) {
1422
+ node.style.setProperty("opacity", "0", "important");
1423
+ }
1424
+ }
1425
+ // Contain the negative-z canvas so it stays behind this element's
1426
+ // text but never slips behind an ancestor's background.
1427
+ el.style.isolation = "isolate";
1428
+ canvas.style.zIndex = "-1";
1429
+ };
1430
+
1431
+ const showHostInk = () => {
1432
+ el.style.visibility = "";
1433
+ if (!inkSaved) return;
1434
+ for (const [node, saved] of inkSaved) {
1435
+ for (const p in INK) {
1436
+ if (saved[p]) node.style.setProperty(p, saved[p]);
1437
+ else node.style.removeProperty(p);
1438
+ }
1439
+ if (saved.op) node.style.setProperty("opacity", saved.op);
1440
+ else node.style.removeProperty("opacity");
1441
+ }
1442
+ inkSaved = null;
1443
+ el.style.isolation = "";
1444
+ canvas.style.zIndex = "";
1445
+ };
1446
+
1338
1447
  const snapshotCapture = () => {
1339
1448
  // Overlay + image content: use the image directly as the texture.
1340
1449
  if (isOverlay) {
@@ -1359,8 +1468,7 @@ function applyRasterPipeline(el, rasterNodes) {
1359
1468
  }
1360
1469
  }
1361
1470
  canvas.style.display = "none";
1362
- const prevVisibility = el.style.visibility;
1363
- el.style.visibility = "";
1471
+ showHostInk();
1364
1472
  const p = snapshotToImage(el, rect.width, rect.height, dpr)
1365
1473
  .then((img) => {
1366
1474
  if (destroyed) return;
@@ -1368,15 +1476,14 @@ function applyRasterPipeline(el, rasterNodes) {
1368
1476
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
1369
1477
  finishUpload();
1370
1478
  textureReady = true;
1371
- // In-place transforms hide the host (the canvas replaces
1372
- // its look). A pure-overlay chain keeps it visible so the
1373
- // real content shows through the bubble; a mixed chain
1374
- // (in-place + overlay) hides it — the canvas carries all.
1375
- el.style.visibility = pureOverlay ? "" : "hidden";
1479
+ // A pure-overlay chain leaves the host alone; the lens
1480
+ // floats over untouched content. Otherwise the canvas
1481
+ // carries the look and the host's own ink is suppressed.
1482
+ if (!pureOverlay) hideHostInk();
1376
1483
  canvas.style.visibility = "visible";
1377
1484
  })
1378
1485
  .catch((e) => {
1379
- el.style.visibility = prevVisibility;
1486
+ showHostInk();
1380
1487
  console.warn("[nodality] raster snapshot failed:", e);
1381
1488
  });
1382
1489
  canvas.style.display = "";
@@ -1671,6 +1778,7 @@ function applyRasterPipeline(el, rasterNodes) {
1671
1778
  }
1672
1779
  solverOps.length = 0;
1673
1780
  while (sourceEl !== el && sourceEl.firstChild) el.appendChild(sourceEl.firstChild);
1781
+ showHostInk();
1674
1782
  el.style.visibility = "";
1675
1783
  canvas.remove();
1676
1784
  },
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.186
2
+ * nodality v1.0.187
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.186
2
+ * nodality v1.0.187
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.186
2
+ * nodality v1.0.187
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.186
2
+ * nodality v1.0.187
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.0.186
2
+ * nodality v1.0.187
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.186",
3
+ "version": "1.0.187",
4
4
  "description": "A lightweight library for declarative UI elements.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",