morphicons 1.4.0 → 1.4.1

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/README.md CHANGED
@@ -13,7 +13,14 @@
13
13
  <a href="https://www.morphicons.com"><strong>morphicons.com</strong></a> — live playground
14
14
  </p>
15
15
 
16
- Universal morphing for stroke-based icons (Lucide, Tabler, Heroicons, Iconoir, or your own paths): **any icon morphs into any other** with spring physics. Rotations are never declared by hand — they emerge from the math (2D Procrustes + polar interpolation). Zero runtime dependencies.
16
+ <p align="center">
17
+ <a href="https://github.com/sponsors/guillermolg00">
18
+ <img src="https://img.shields.io/badge/%E2%98%95%20Buy%20me%20a%20coffee-EA4AAA" alt="Buy me a coffee — sponsor morphicons on GitHub Sponsors">
19
+ </a>
20
+ </p>
21
+
22
+ Universal morphing for stroke-based icons (Lucide, Tab
23
+ ler, Heroicons, Iconoir, or your own paths): **any icon morphs into any other** with spring physics. Rotations are never declared by hand — they emerge from the math (2D Procrustes + polar interpolation). Zero runtime dependencies.
17
24
 
18
25
  ```tsx
19
26
  import { MorphIcon } from "morphicons/react";
@@ -312,7 +319,7 @@ c_k(t) = c_{A,k} + t·d_k + (σ*ᵗ·R(t·θ*) − I)·(c_{A,k} − g_A)
312
319
 
313
320
  ### 6. Serialization and canonical snap
314
321
 
315
- In flight, each subpath is emitted as `M x y L x y …` (+ `Z` if the pair is closed↔closed; closed→open flies open, the loop opening at the cut chosen by the circular correspondence) with 2 decimals — invisible at 24px, and the frame's only allocation. On settle, the driver **snaps to the canonical `d`** of the target icon: exact fidelity at rest (real curves, not polylines), subpath count reset after duplications, and the DOM ends up identical to a static icon's. The jump is < 0.02px — imperceptible.
322
+ In flight, each subpath is emitted as `M x y L x y …` (+ `Z` if the pair is closed↔closed; closed→open flies open, the loop opening at the cut chosen by the circular correspondence) with 2 decimals — invisible at 24px, and the frame's only allocation. On settle, the driver **snaps to the canonical `d`** of the target icon: exact fidelity at rest (real curves, not polylines), subpath count reset after duplications, and the DOM ends up identical to a static icon's. The jump is < 0.02px — imperceptible. The canonical `d` itself is emitted with 4 decimals, and that is a correctness choice, not a size one: arc→cubic conversion goes through trig whose last ulp differs across JS engines, and SSR hydration compares the server's bytes against the browser's — quantized emission keeps them identical, where full precision leaked each engine's ulp into the markup (a Next hydration mismatch on icons with non-cardinal arcs).
316
323
 
317
324
  ### 7. The spring
318
325
 
package/dist/dom.d.ts CHANGED
@@ -27,8 +27,9 @@ interface Morph {
27
27
  destroy(): void;
28
28
  }
29
29
  /** Canonical `d` of an icon: the input string verbatim, or the real cubics
30
- * at full precision (the at-rest snap). Exported because it is what a
31
- * binding renders at SSR/rest before any runtime exists. */
30
+ * quantized to 4 decimals (the at-rest snap; engine-stable bytes so SSR
31
+ * hydration matches, see fmtCanon in core/serialize). Exported because it
32
+ * is what a binding renders at SSR/rest before any runtime exists. */
32
33
  declare function canonicalD(icon: IconInput): string;
33
34
  /** Creates the morph instance over a `<path>` and paints the initial icon. */
34
35
  declare function createMorph(el: PathEl, icon: IconInput): Morph;
package/dist/dom.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as buildPlan, c as cubicsToPathD, f as interpPolar, l as serialize, n as Spring, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-BTcHiHR5.js";
1
+ import { a as buildPlan, c as cubicsToPathD, f as interpPolar, l as serialize, n as Spring, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-CBynweFS.js";
2
2
  //#region src/dom/index.ts
3
3
  const tickers = /* @__PURE__ */ new Set();
4
4
  let rafId = 0;
@@ -41,8 +41,9 @@ function sampledOf(icon) {
41
41
  return s;
42
42
  }
43
43
  /** Canonical `d` of an icon: the input string verbatim, or the real cubics
44
- * at full precision (the at-rest snap). Exported because it is what a
45
- * binding renders at SSR/rest before any runtime exists. */
44
+ * quantized to 4 decimals (the at-rest snap; engine-stable bytes so SSR
45
+ * hydration matches, see fmtCanon in core/serialize). Exported because it
46
+ * is what a binding renders at SSR/rest before any runtime exists. */
46
47
  function canonicalD(icon) {
47
48
  if (typeof icon === "string") return icon;
48
49
  let d = canon.get(icon);
package/dist/index.d.ts CHANGED
@@ -72,7 +72,8 @@ declare function resampleIcon(input: IconInput, N?: number): Sampled[];
72
72
  /** Sampled subpaths → polyline `d` attribute. `closed[k]` appends Z to
73
73
  * subpath k (closed loops in flight); without flags everything is open. */
74
74
  declare function serialize(subs: readonly Float64Array[], closed?: readonly boolean[]): string;
75
- /** Cubic subpaths → canonical `d` at full precision (round-trip). */
75
+ /** Cubic subpaths → canonical `d`, quantized to 4 decimals (engine-stable
76
+ * bytes; see fmtCanon). */
76
77
  declare function cubicsToPathD(paths: readonly CubicPath[]): string;
77
78
  //#endregion
78
79
  export { type CubicPath, type IconInput, type IconNode, type IconNodeAttrs, type MorphPlan, type PlanItem, SPRING_PRESETS, type Sampled, Spring, type SpringPreset, type ViewBox, allocOutputs, buildPlan, cubicsToPathD, fitIcon, iconToCubics, interpLinear, interpPolar, resampleIcon, resamplePath, serialize };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as buildPlan, c as cubicsToPathD, d as interpLinear, f as interpPolar, i as resamplePath, l as serialize, n as Spring, o as fitIcon, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-BTcHiHR5.js";
1
+ import { a as buildPlan, c as cubicsToPathD, d as interpLinear, f as interpPolar, i as resamplePath, l as serialize, n as Spring, o as fitIcon, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-CBynweFS.js";
2
2
  export { SPRING_PRESETS, Spring, allocOutputs, buildPlan, cubicsToPathD, fitIcon, iconToCubics, interpLinear, interpPolar, resampleIcon, resamplePath, serialize };
@@ -1,4 +1,4 @@
1
- import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-BTcHiHR5.js";
1
+ import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
2
2
  import { canonicalD, createMorph } from "./dom.js";
3
3
  import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-BTcHiHR5.js";
1
+ import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
2
2
  import { canonicalD, createMorph } from "./dom.js";
3
3
  import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -305,12 +305,16 @@ function serialize(subs, closed) {
305
305
  }
306
306
  return d;
307
307
  }
308
- /** Cubic subpaths → canonical `d` at full precision (round-trip). */
308
+ function fmtCanon(v) {
309
+ return String(Math.round(v * 1e4) / 1e4);
310
+ }
311
+ /** Cubic subpaths → canonical `d`, quantized to 4 decimals (engine-stable
312
+ * bytes; see fmtCanon). */
309
313
  function cubicsToPathD(paths) {
310
314
  let d = "";
311
315
  for (const { pts, closed } of paths) {
312
- d += `M${pts[0]} ${pts[1]}`;
313
- for (let i = 2; i < pts.length; i += 6) d += `C${pts[i]} ${pts[i + 1]} ${pts[i + 2]} ${pts[i + 3]} ${pts[i + 4]} ${pts[i + 5]}`;
316
+ d += `M${fmtCanon(pts[0])} ${fmtCanon(pts[1])}`;
317
+ for (let i = 2; i < pts.length; i += 6) d += `C${fmtCanon(pts[i])} ${fmtCanon(pts[i + 1])} ${fmtCanon(pts[i + 2])} ${fmtCanon(pts[i + 3])} ${fmtCanon(pts[i + 4])} ${fmtCanon(pts[i + 5])}`;
314
318
  if (closed) d += "Z";
315
319
  }
316
320
  return d;
@@ -1,4 +1,4 @@
1
- import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-BTcHiHR5.js";
1
+ import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
2
2
  import { canonicalD, createMorph } from "./dom.js";
3
3
  //#region src/svelte/shared.ts
4
4
  /** Frozen shape of the from→to pair at t, using the pure core (SSR-safe).
package/dist/vue.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-BTcHiHR5.js";
1
+ import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
2
2
  import { canonicalD, createMorph } from "./dom.js";
3
3
  import { defineComponent, h, onBeforeUnmount, onMounted, shallowRef, watch } from "vue";
4
4
  //#region src/vue/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morphicons",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Universal morphing for stroke-based icons with spring physics — zero runtime dependencies",
5
5
  "license": "MIT",
6
6
  "author": "Guillermo (https://guillermolg.com)",