@liiift-studio/magnettype 1.1.6 → 1.2.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
@@ -30,7 +30,7 @@ Per-word cursor-proximity weight variation driven by a continuous rAF loop.
30
30
  import { MagnetTypeText } from '@liiift-studio/magnettype'
31
31
 
32
32
  <MagnetTypeText
33
- mode="field"
33
+ mode="word"
34
34
  axes={{ wght: [300, 700] }}
35
35
  radius={150}
36
36
  falloff="quadratic"
@@ -40,43 +40,43 @@ import { MagnetTypeText } from '@liiift-studio/magnettype'
40
40
  </MagnetTypeText>
41
41
  ```
42
42
 
43
- ### React — block mode (`MagnetBlock`)
43
+ ### React — block mode (`MagnetChar`)
44
44
 
45
45
  Per-character cursor-proximity weight variation. Works with mixed content (inline elements, links, `<code>`, etc.) inside any block element. Characters are wrapped as React elements — no DOM mutation.
46
46
 
47
47
  ```tsx
48
- import { MagnetBlock } from '@liiift-studio/magnettype'
48
+ import { MagnetChar } from '@liiift-studio/magnettype'
49
49
 
50
50
  // Per-character spread — each character responds to cursor distance
51
- <MagnetBlock
51
+ <MagnetChar
52
52
  spreadRadius={200}
53
53
  minWeight={300}
54
54
  maxWeight={700}
55
55
  >
56
56
  Typography that responds to presence.
57
- </MagnetBlock>
57
+ </MagnetChar>
58
58
 
59
59
  // Whole-element gate — the effect only activates when the cursor is within proximityRadius of the element edge
60
- <MagnetBlock
60
+ <MagnetChar
61
61
  proximityRadius={120}
62
62
  minWeight={300}
63
63
  maxWeight={700}
64
64
  >
65
65
  Weight rises when the cursor enters.
66
- </MagnetBlock>
66
+ </MagnetChar>
67
67
 
68
68
  // Both combined — proximity gates the spread effect
69
- <MagnetBlock
69
+ <MagnetChar
70
70
  proximityRadius={200}
71
71
  spreadRadius={120}
72
72
  minWeight={300}
73
73
  maxWeight={700}
74
74
  >
75
75
  Only spreads when the cursor is close.
76
- </MagnetBlock>
76
+ </MagnetChar>
77
77
  ```
78
78
 
79
- **`MagnetBlock` props:**
79
+ **`MagnetChar` props:**
80
80
 
81
81
  | Prop | Default | Description |
82
82
  |------|---------|-------------|
@@ -97,7 +97,7 @@ import { MagnetBlock } from '@liiift-studio/magnettype'
97
97
  ```tsx
98
98
  import { useMagnetType } from '@liiift-studio/magnettype'
99
99
 
100
- const ref = useMagnetType({ mode: 'field', axes: { wght: [300, 700] }, radius: 150 })
100
+ const ref = useMagnetType({ mode: 'word', axes: { wght: [300, 700] }, radius: 150 })
101
101
  return <p ref={ref}>{children}</p>
102
102
  ```
103
103
 
@@ -120,7 +120,7 @@ import { startMagnetType, removeMagnetType, getCleanHTML } from '@liiift-studio/
120
120
 
121
121
  const el = document.querySelector('p')
122
122
  const original = getCleanHTML(el)
123
- const opts = { mode: 'field', axes: { wght: [300, 700] }, radius: 150 }
123
+ const opts = { mode: 'word', axes: { wght: [300, 700] }, radius: 150 }
124
124
 
125
125
  let stop
126
126
 
@@ -163,10 +163,10 @@ ro.observe(el)
163
163
  ### TypeScript
164
164
 
165
165
  ```ts
166
- import type { MagnetTypeOptions, FalloffType, MagnetModeType, MagnetBlockProps } from '@liiift-studio/magnettype'
166
+ import type { MagnetTypeOptions, FalloffType, MagnetModeType, MagnetCharProps } from '@liiift-studio/magnettype'
167
167
 
168
168
  const fieldOpts: MagnetTypeOptions = {
169
- mode: 'field',
169
+ mode: 'word',
170
170
  axes: { wght: [300, 700], wdth: [90, 110] },
171
171
  radius: 120,
172
172
  falloff: 'quadratic' as FalloffType,
@@ -185,7 +185,7 @@ const legibilityOpts: MagnetTypeOptions = {
185
185
 
186
186
  | Option | Default | Description |
187
187
  |--------|---------|-------------|
188
- | `mode` | `'field'` | `'field'` — cursor proximity drives per-word `font-variation-settings` via a continuous rAF loop. `'legibility'` — static per-character `wdth` boost on visually confusable characters |
188
+ | `mode` | `'word'` | `'word'` — cursor proximity drives per-word `font-variation-settings` via a continuous rAF loop. `'legibility'` — static per-character `wdth` boost on visually confusable characters |
189
189
  | `axes` | `{ wght: [300, 500] }` | *(field mode)* Map of axis tag → `[restValue, peakValue]` |
190
190
  | `radius` | `120` | *(field mode)* Pixel radius over which the field effect fades from each word's centre |
191
191
  | `falloff` | `'quadratic'` | *(field mode)* `'linear'` or `'quadratic'` falloff curve |
@@ -209,9 +209,9 @@ strength = normalised² (quadratic) or normalised (linear)
209
209
 
210
210
  Each word's `font-variation-settings` interpolates between `restValue` and `peakValue` at that strength. Reads are batched before writes on every frame to avoid layout thrashing. When the cursor leaves, one final frame resets all words to `restValue`.
211
211
 
212
- ### Block mode (`MagnetBlock`)
212
+ ### Block mode (`MagnetChar`)
213
213
 
214
- `MagnetBlock` splits string children into per-character `<span>` elements during the React render pass using `useMemo` — no DOM mutation. Callback refs collect each span element. On `mousemove` (and on `scroll`, using the stored last position), the component reads each span's `getBoundingClientRect`, computes cursor-to-character-centre distance, and sets `font-variation-settings` directly on the span's style. This is passive and batched per frame via the event handler.
214
+ `MagnetChar` splits string children into per-character `<span>` elements during the React render pass using `useMemo` — no DOM mutation. Callback refs collect each span element. On `mousemove` (and on `scroll`, using the stored last position), the component reads each span's `getBoundingClientRect`, computes cursor-to-character-centre distance, and sets `font-variation-settings` directly on the span's style. This is passive and batched per frame via the event handler.
215
215
 
216
216
  `proximityRadius` measures cursor distance to the element **edge** (not its centre) — useful as an outer gate so the effect only fires when the cursor is actually near the block. `spreadRadius` measures cursor distance to each **character centre** — controls how wide the weight gradient spreads around the cursor within the block. Both are independent and combinable.
217
217
 
@@ -247,4 +247,4 @@ The package itself has zero runtime dependencies. Do not remove this entry.
247
247
 
248
248
  ---
249
249
 
250
- Current version: 1.1.6
250
+ Current version: 1.2.0
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react"),W=require("react/jsx-runtime"),rt={i:3,l:3,1:3,I:3,r:2,n:1,m:1,0:2,O:2,o:1,b:1,d:1,p:1,q:1,c:1,e:1},z={word:"mt-word",char:"mt-char",probe:"mt-probe"},K={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function ot(t,n=[]){return t.nodeType===Node.TEXT_NODE?n.push(t):t.childNodes.forEach(o=>ot(o,n)),n}function tt(t,n,o){if(!t||t==="normal")return`"${n}" ${o}`;const E=new RegExp(`(["'])${n}\\1\\s+[\\d.eE+-]+`),y=`"${n}" ${o}`;return E.test(t)?t.replace(E,y):`${t}, ${y}`}function nt(t,n){let o=t;for(const[E,y]of Object.entries(n))o=tt(o,E,y);return o}function st(t,n,o){if(n.opacity!==void 0){const[E,y]=n.opacity;t.style.opacity=String(E+(y-E)*o)}n.italic===!0&&(t.style.fontStyle=o>.5?"italic":"")}function et(t,n){n.opacity!==void 0&&(t.style.opacity=String(n.opacity[0])),n.italic===!0&&(t.style.fontStyle="")}function ct(t){const n=t.cloneNode(!0),o=n.querySelectorAll(`.${z.word}, .${z.char}`);return Array.from(o).reverse().forEach(y=>{const d=y.parentNode;if(d){for(;y.firstChild;)d.insertBefore(y.firstChild,y);d.removeChild(y)}}),n.innerHTML}function mt(t,n){t.innerHTML=n}function it(t,n,o={}){var p,r;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const E=o.wdthBoost??K.wdthBoost,y=o.radius??K.radius,d=o.falloff??K.falloff,L=o.scope??K.scope,F=o.props,m=o.transitionMs??0,S=o.cachePositions??!0,V=window.scrollY;t.innerHTML=n;const j=getComputedStyle(t).fontVariationSettings,N=j.match(/"wdth"\s+([\d.eE+-]+)/),k=N?parseFloat(N[1]):100,T=ot(t),g=[];for(const a of T){const C=a.textContent??"";if(!C||!C.split("").some(v=>v in rt))continue;const e=document.createDocumentFragment();for(const v of C){const i=rt[v];if(i===void 0){const u=e.lastChild;u&&u.nodeType===Node.TEXT_NODE?u.textContent+=v:e.appendChild(document.createTextNode(v))}else{const u=document.createElement("span");u.className=z.char,u.style.fontVariationSettings=tt(j,"wdth",k),u.textContent=v,e.appendChild(u),g.push({span:u,riskLevel:i})}}a.parentNode.replaceChild(e,a)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-V)>2&&window.scrollTo({top:V,behavior:"instant"})}),g.length===0)return()=>{};F&&g.forEach(({span:a})=>et(a,F));let A=[],H=!1;function _(){const a=window.scrollX,C=window.scrollY;A=g.map(({span:c})=>{const e=c.getBoundingClientRect();return{cx:(e.left+e.right)/2+a,cy:(e.top+e.bottom)/2+C}}),H=!0}let R=null;S&&(_(),R=new ResizeObserver(()=>{H=!1}),R.observe(t),(r=(p=document.fonts)==null?void 0:p.ready)==null||r.then(()=>{H=!1}));let q=-9999,B=-9999,$=!1,w=0,P=!0,b=null;function D(){if(!P)return;if(!$){g.forEach(({span:e})=>{m>0&&(e.style.transition=`font-variation-settings ${m}ms ease`),e.style.fontVariationSettings=tt(j,"wdth",k),F&&et(e,F)}),m>0&&(b!==null&&clearTimeout(b),b=setTimeout(()=>{g.forEach(({span:e})=>{e.style.transition=""}),b=null},m)),w=0;return}S&&!H&&_();const a=S?q+window.scrollX:q,C=S?B+window.scrollY:B,c=S?null:g.map(({span:e})=>e.getBoundingClientRect());g.forEach(({span:e,riskLevel:v},i)=>{e.style.transition="";let u,O;if(S)({cx:u,cy:O}=A[i]);else{const I=c[i];u=I.left+I.width/2,O=I.top+I.height/2}const M=Math.sqrt((a-u)**2+(C-O)**2),f=Math.max(0,1-M/y),x=d==="quadratic"?f*f:f,Y=E*(v/3)*x;e.style.fontVariationSettings=tt(j,"wdth",k+Y),F&&st(e,F,x)}),w=requestAnimationFrame(D)}function X(a){q=a.clientX,B=a.clientY,$||($=!0),w===0&&(w=requestAnimationFrame(D))}function J(){$=!1,w===0&&(w=requestAnimationFrame(D))}function Q(a){a.touches.length!==0&&(q=a.touches[0].clientX,B=a.touches[0].clientY,$||($=!0),w===0&&(w=requestAnimationFrame(D)))}function l(){$=!1,w===0&&(w=requestAnimationFrame(D))}const s=L==="document"?document:t;return s.addEventListener("mousemove",X),s.addEventListener("mouseleave",J),s.addEventListener("touchmove",Q,{passive:!0}),s.addEventListener("touchend",l),()=>{P=!1,cancelAnimationFrame(w),b!==null&&clearTimeout(b),R&&R.disconnect(),s.removeEventListener("mousemove",X),s.removeEventListener("mouseleave",J),s.removeEventListener("touchmove",Q),s.removeEventListener("touchend",l),t.innerHTML=n}}function lt(t,n,o={}){var a,C;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const E=o.axes??K.axes,y=o.radius??K.radius,d=o.falloff??K.falloff,L=o.magnetMode??K.magnetMode,F=o.scope??K.scope,m=o.props,S=o.transitionMs??0,V=o.cachePositions??!0,j=o.stabilizeLayout??!0,N=window.scrollY;t.innerHTML=n;const k=ot(t),T=[];for(const c of k){const e=c.textContent??"";if(!e.trim())continue;const v=e.split(/(\S+)/),i=document.createDocumentFragment();for(let u=0;u<v.length;u+=2){const O=v[u],M=v[u+1];if(!M)continue;const x=v[u+3]===void 0?v[u+2]??"":"",Y=document.createElement("span");Y.className=z.word,Y.textContent=O+M+x,i.appendChild(Y),T.push(Y)}c.parentNode.replaceChild(i,c)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-N)>2&&window.scrollTo({top:N,behavior:"instant"})}),T.length===0)return()=>{};const g=getComputedStyle(t).fontVariationSettings,A=nt(g,Object.fromEntries(Object.entries(E).map(([c,[e]])=>[c,e])));let H=0;if(j){const c=nt(g,Object.fromEntries(Object.entries(E).map(([f,[,x]])=>[f,x]))),e=t.style.fontVariationSettings,v=t.style.whiteSpace,i=t.style.overflow;t.style.whiteSpace="nowrap",t.style.overflow="visible",t.style.fontVariationSettings=c;const u=t.scrollWidth;t.style.fontVariationSettings=A;const O=t.scrollWidth;t.style.fontVariationSettings=e,t.style.whiteSpace=v,t.style.overflow=i;const M=T.reduce((f,x)=>{var Y;return f+(((Y=x.textContent)==null?void 0:Y.replace(/\s+/g,"").length)??0)},0);M>0&&u>O&&(H=(u-O)/M)}T.forEach(c=>{c.style.fontVariationSettings=A,m&&et(c,m)});let _=[],R=!1;function q(){const c=window.scrollX,e=window.scrollY;_=T.map(v=>{const i=v.getBoundingClientRect();return{cx:(i.left+i.right)/2+c,cy:(i.top+i.bottom)/2+e}}),R=!0}let B=null;V&&(q(),B=new ResizeObserver(()=>{R=!1}),B.observe(t),(C=(a=document.fonts)==null?void 0:a.ready)==null||C.then(()=>{R=!1}));let $=-9999,w=-9999,P=!1,b=0,D=!0,X=null;function J(){if(!D)return;if(!P){T.forEach(i=>{S>0&&(i.style.transition=`font-variation-settings ${S}ms ease`),i.style.fontVariationSettings=A,j&&(i.style.letterSpacing=""),m&&et(i,m)}),S>0&&(X!==null&&clearTimeout(X),X=setTimeout(()=>{T.forEach(i=>{i.style.transition=""}),X=null},S)),b=0;return}V&&!R&&q();const c=V?$+window.scrollX:$,e=V?w+window.scrollY:w,v=V?null:T.map(i=>i.getBoundingClientRect());T.forEach((i,u)=>{i.style.transition="";let O,M;if(V)({cx:O,cy:M}=_[u]);else{const G=v[u];O=G.left+G.width/2,M=G.top+G.height/2}const f=Math.sqrt((c-O)**2+(e-M)**2),x=Math.max(0,1-f/y),Y=d==="quadratic"?x*x:x,I=L==="repel"?1-Y:Y,Z={};for(const G of Object.keys(E)){const[U,dt]=E[G]??[300,500];Z[G]=U+(dt-U)*I}i.style.fontVariationSettings=nt(g,Z),j&&H!==0&&(i.style.letterSpacing=`${(-H*I).toFixed(3)}px`),m&&st(i,m,Y)}),b=requestAnimationFrame(J)}function Q(c){$=c.clientX,w=c.clientY,P||(P=!0),b===0&&(b=requestAnimationFrame(J))}function l(){P=!1,b===0&&(b=requestAnimationFrame(J))}function s(c){c.touches.length!==0&&($=c.touches[0].clientX,w=c.touches[0].clientY,P||(P=!0),b===0&&(b=requestAnimationFrame(J)))}function p(){P=!1,b===0&&(b=requestAnimationFrame(J))}const r=F==="document"?document:t;return r.addEventListener("mousemove",Q),r.addEventListener("mouseleave",l),r.addEventListener("touchmove",s,{passive:!0}),r.addEventListener("touchend",p),()=>{D=!1,cancelAnimationFrame(b),X!==null&&clearTimeout(X),B&&B.disconnect(),r.removeEventListener("mousemove",Q),r.removeEventListener("mouseleave",l),r.removeEventListener("touchmove",s),r.removeEventListener("touchend",p),t.innerHTML=n}}function at(t){const n=h.useRef(null),o=h.useRef(null),E=h.useRef(t);E.current=t;const y=h.useRef(null),d=t.mode??"field",{axes:L,radius:F,falloff:m,magnetMode:S,wdthBoost:V,scope:j}=t,N=L?JSON.stringify(L):void 0,k=t.props?JSON.stringify(t.props):void 0,T=h.useCallback(()=>{const g=n.current;if(!g)return;o.current===null&&(o.current=ct(g)),y.current&&(y.current(),y.current=null),(E.current.mode??"field")==="field"?y.current=lt(g,o.current,E.current):y.current=it(g,o.current,E.current)},[d,N,F,m,S,V,j,k]);return h.useLayoutEffect(()=>(T(),()=>{y.current&&(y.current(),y.current=null)}),[T]),h.useEffect(()=>{var g,A;(A=(g=document.fonts)==null?void 0:g.ready)==null||A.then(T)},[T]),n}const ut=h.forwardRef(function({children:n,as:o="p",className:E,style:y,...d},L){const F=at(d),m=h.useCallback(S=>{F.current=S,typeof L=="function"?L(S):L&&(L.current=S)},[L]);return W.jsx(o,{ref:m,className:E,style:y,children:n})});ut.displayName="MagnetTypeText";const ft=h.forwardRef(function({children:n,as:o="p",className:E,style:y,minWeight:d=300,maxWeight:L=600,proximityRadius:F,spreadRadius:m,fixedAxes:S={},cachePositions:V=!0,rafThrottle:j=!0,stabilizeLayout:N=!0},k){const T=h.useRef(null),g=h.useRef(null),A=h.useRef([]),H=h.useRef([]),_=h.useRef(null),R=h.useRef(!1),q=h.useRef(null),B=h.useRef(0),$=h.useCallback(l=>{T.current=l,typeof k=="function"?k(l):k&&(k.current=l)},[k]);function w(l){const s=[`'wght' ${l.toFixed(0)}`];for(const[p,r]of Object.entries(S))s.push(`'${p}' ${r}`);return s.join(", ")}function P(){const l=T.current;if(!l)return;const s=window.scrollX,p=window.scrollY,r=l.getBoundingClientRect();_.current={top:r.top+p,left:r.left+s,right:r.right+s,bottom:r.bottom+p},H.current=A.current.map(a=>{if(!a)return{cx:0,cy:0};const C=a.getBoundingClientRect();return{cx:(C.left+C.right)/2+s,cy:(C.top+C.bottom)/2+p}}),R.current=!0}function b(l,s){const p=T.current;if(!p)return;V&&!R.current&&P();let r,a,C,c,e,v;if(V&&_.current)e=l+window.scrollX,v=s+window.scrollY,{top:r,left:a,right:C,bottom:c}=_.current;else{const M=p.getBoundingClientRect();e=l,v=s,r=M.top,a=M.left,C=M.right,c=M.bottom}const i=Math.max(a-e,0,e-C),u=Math.max(r-v,0,v-c),O=Math.sqrt(i*i+u*u);if(F!==void 0&&!m){const f=1-(1-Math.max(0,1-O/F))**2;p.style.fontVariationSettings=w(d+(L-d)*f);return}if(m){if(F!==void 0&&O>F){p.style.fontVariationSettings=w(d);for(const f of A.current)f&&(f.style.fontVariationSettings=w(d),N&&(f.style.letterSpacing=""));return}if(O>m){for(const f of A.current)f&&(f.style.fontVariationSettings=w(d),N&&(f.style.letterSpacing=""));return}const M=A.current;if(V&&H.current.length===M.length)for(let f=0;f<M.length;f++){const x=M[f];if(!x)continue;const{cx:Y,cy:I}=H.current[f],Z=Math.sqrt((e-Y)**2+(v-I)**2),U=1-(1-Math.max(0,1-Z/m))**2;x.style.fontVariationSettings=w(d+(L-d)*U),N&&B.current!==0&&(x.style.letterSpacing=`${(-B.current*U).toFixed(3)}px`)}else for(const f of M){if(!f)continue;const x=f.getBoundingClientRect(),Y=(x.left+x.right)/2,I=(x.top+x.bottom)/2,Z=Math.sqrt((l-Y)**2+(s-I)**2),U=1-(1-Math.max(0,1-Z/m))**2;f.style.fontVariationSettings=w(d+(L-d)*U),N&&B.current!==0&&(f.style.letterSpacing=`${(-B.current*U).toFixed(3)}px`)}}}const D=h.useCallback(l=>{g.current={x:l.clientX,y:l.clientY},j?q.current===null&&(q.current=requestAnimationFrame(()=>{q.current=null,g.current&&b(g.current.x,g.current.y)})):b(l.clientX,l.clientY)},[d,L,F,m,V,j,N,JSON.stringify(S)]),X=h.useCallback(()=>{g.current&&b(g.current.x,g.current.y)},[d,L,F,m,V,N,JSON.stringify(S)]),J=h.useCallback(()=>{g.current=null,q.current!==null&&(cancelAnimationFrame(q.current),q.current=null);const l=T.current;l&&(l.style.fontVariationSettings=w(d));for(const s of A.current)s&&(s.style.fontVariationSettings=w(d),N&&(s.style.letterSpacing=""))},[d,N,JSON.stringify(S)]);h.useEffect(()=>(window.addEventListener("mousemove",D,{passive:!0}),window.addEventListener("scroll",X,{passive:!0,capture:!0}),document.documentElement.addEventListener("mouseleave",J),()=>{window.removeEventListener("mousemove",D),window.removeEventListener("scroll",X,{capture:!0}),document.documentElement.removeEventListener("mouseleave",J),q.current!==null&&(cancelAnimationFrame(q.current),q.current=null)}),[D,X,J]),h.useEffect(()=>{var p,r;if(!V||!m)return;R.current=!1;const l=T.current;if(!l)return;const s=new ResizeObserver(()=>{R.current=!1});return s.observe(l),(r=(p=document.fonts)==null?void 0:p.ready)==null||r.then(()=>{R.current=!1}),()=>s.disconnect()},[V,m]),h.useEffect(()=>{R.current=!1},[n,m]),h.useEffect(()=>{var s,p;if(!N||!m){B.current=0;return}function l(){const r=T.current;if(!r)return;const a=r.textContent??"",C=a.replace(/\s/g,"").length;if(C===0)return;const c=getComputedStyle(r),e=document.createElement("span");e.style.cssText="position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;",e.style.fontFamily=c.fontFamily,e.style.fontSize=c.fontSize,e.style.fontWeight=c.fontWeight,e.style.lineHeight=c.lineHeight,e.style.letterSpacing=c.letterSpacing,e.textContent=a,document.body.appendChild(e);const v=O=>{const M=[`'wght' ${O.toFixed(0)}`];for(const[f,x]of Object.entries(S))M.push(`'${f}' ${x}`);return M.join(", ")};e.style.fontVariationSettings=v(L);const i=e.scrollWidth;e.style.fontVariationSettings=v(d);const u=e.scrollWidth;document.body.removeChild(e),B.current=i>u?(i-u)/C:0}l(),(p=(s=document.fonts)==null?void 0:s.ready)==null||p.then(l)},[N,m,d,L,n,JSON.stringify(S)]);const Q=h.useMemo(()=>{if(!m)return n;A.current=[];let l=0;function s(p){if(typeof p=="string")return[...p].map(r=>{if(/\s/.test(r))return r;const a=l++;return W.jsx("span",{ref:C=>{A.current[a]=C},style:{fontVariationSettings:w(d)},children:r},a)});if(Array.isArray(p))return p.map((r,a)=>W.jsx(h.Fragment,{children:s(r)},a));if(h.isValidElement(p)){const r=p;if(r.props.children!==void 0)return h.cloneElement(r,{},s(r.props.children))}return p}return s(n)},[n,m,d,JSON.stringify(S)]);return W.jsx(o,{ref:$,className:E,style:{fontVariationSettings:w(d),...y},children:Q})});ft.displayName="MagnetBlock";exports.MAGNET_TYPE_CLASSES=z;exports.MagnetBlock=ft;exports.MagnetTypeText=ut;exports.applyMagnetType=it;exports.getCleanHTML=ct;exports.removeMagnetType=mt;exports.startMagnetType=lt;exports.useMagnetType=at;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react"),tt=require("react/jsx-runtime"),it={i:3,l:3,1:3,I:3,r:2,n:1,m:1,0:2,O:2,o:1,b:1,d:1,p:1,q:1,c:1,e:1},W={word:"mt-word",char:"mt-char",probe:"mt-probe"},U={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function rt(t,e=[]){return t.nodeType===Node.TEXT_NODE?e.push(t):t.childNodes.forEach(n=>rt(n,e)),e}function et(t,e,n){if(!t||t==="normal")return`"${e}" ${n}`;const E=new RegExp(`(["'])${e}\\1\\s+[\\d.eE+-]+`),y=`"${e}" ${n}`;return E.test(t)?t.replace(E,y):`${t}, ${y}`}function ot(t,e){let n=t;for(const[E,y]of Object.entries(e))n=et(n,E,y);return n}function lt(t,e,n){if(e.opacity!==void 0){const[E,y]=e.opacity;t.style.opacity=String(E+(y-E)*n)}e.italic===!0&&(t.style.fontStyle=n>.5?"italic":"")}function nt(t,e){e.opacity!==void 0&&(t.style.opacity=String(e.opacity[0])),e.italic===!0&&(t.style.fontStyle="")}function at(t){const e=t.cloneNode(!0),n=e.querySelectorAll(`.${W.word}, .${W.char}`);return Array.from(n).reverse().forEach(y=>{const f=y.parentNode;if(f){for(;y.firstChild;)f.insertBefore(y.firstChild,y);f.removeChild(y)}}),e.innerHTML}function ht(t,e){t.innerHTML=e}function ut(t,e,n={}){var m,r,L,N;if(typeof window>"u")return()=>{};if((r=(m=window.matchMedia)==null?void 0:m.call(window,"(prefers-reduced-motion: reduce)"))!=null&&r.matches)return t.innerHTML=e,()=>{};const E=n.wdthBoost??U.wdthBoost,y=n.radius??U.radius,f=n.falloff??U.falloff,b=n.scope??U.scope,F=n.props,d=n.transitionMs??0,M=n.cachePositions??!0,V=window.scrollY;t.innerHTML=e;const k=getComputedStyle(t).fontVariationSettings,A=k.match(/"wdth"\s+([\d.eE+-]+)/),H=A?parseFloat(A[1]):100,C=rt(t),g=[];for(const v of C){const p=v.textContent??"";if(!p||!p.split("").some(S=>S in it))continue;const s=document.createDocumentFragment();for(const S of p){const l=it[S];if(l===void 0){const o=s.lastChild;o&&o.nodeType===Node.TEXT_NODE?o.textContent+=S:s.appendChild(document.createTextNode(S))}else{const o=document.createElement("span");o.className=W.char,o.style.fontVariationSettings=et(k,"wdth",H),o.textContent=S,s.appendChild(o),g.push({span:o,riskLevel:l})}}v.parentNode.replaceChild(s,v)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-V)>2&&window.scrollTo({top:V,behavior:"instant"})}),g.length===0)return()=>{};F&&g.forEach(({span:v})=>nt(v,F));let O=[],D=!1;function G(){const v=window.scrollX,p=window.scrollY;O=g.map(({span:i})=>{const s=i.getBoundingClientRect();return{cx:(s.left+s.right)/2+v,cy:(s.top+s.bottom)/2+p}}),D=!0}let R=null;M&&(G(),R=new ResizeObserver(()=>{D=!1}),R.observe(t),(N=(L=document.fonts)==null?void 0:L.ready)==null||N.then(()=>{D=!1}));let q=-9999,$=-9999,j=!1,w=0,J=!0,T=null;function I(){if(!J)return;if(!j){g.forEach(({span:s})=>{d>0&&(s.style.transition=`font-variation-settings ${d}ms ease`),s.style.fontVariationSettings=et(k,"wdth",H),F&&nt(s,F)}),d>0&&(T!==null&&clearTimeout(T),T=setTimeout(()=>{g.forEach(({span:s})=>{s.style.transition=""}),T=null},d)),w=0;return}M&&!D&&G();const v=M?q+window.scrollX:q,p=M?$+window.scrollY:$,i=M?null:g.map(({span:s})=>s.getBoundingClientRect());g.forEach(({span:s,riskLevel:S},l)=>{s.style.transition="";let o,u;if(M)({cx:o,cy:u}=O[l]);else{const K=i[l];o=K.left+K.width/2,u=K.top+K.height/2}const x=Math.sqrt((v-o)**2+(p-u)**2),X=Math.max(0,1-x/y),B=f==="quadratic"?X*X:X,Y=E*(S/3)*B;s.style.fontVariationSettings=et(k,"wdth",H+Y),F&&lt(s,F,B)}),w=requestAnimationFrame(I)}function P(v){q=v.clientX,$=v.clientY,j||(j=!0),w===0&&(w=requestAnimationFrame(I))}function _(){j=!1,w===0&&(w=requestAnimationFrame(I))}function Z(v){v.touches.length!==0&&(q=v.touches[0].clientX,$=v.touches[0].clientY,j||(j=!0),w===0&&(w=requestAnimationFrame(I)))}function a(){j=!1,w===0&&(w=requestAnimationFrame(I))}const c=b==="document"?document:t;return c.addEventListener("mousemove",P),c.addEventListener("mouseleave",_),c.addEventListener("touchmove",Z,{passive:!0}),c.addEventListener("touchend",a),()=>{J=!1,cancelAnimationFrame(w),T!==null&&clearTimeout(T),R&&R.disconnect(),c.removeEventListener("mousemove",P),c.removeEventListener("mouseleave",_),c.removeEventListener("touchmove",Z),c.removeEventListener("touchend",a),t.innerHTML=e}}function ft(t,e,n={}){var L,N,v,p;if(typeof window>"u")return()=>{};if((N=(L=window.matchMedia)==null?void 0:L.call(window,"(prefers-reduced-motion: reduce)"))!=null&&N.matches)return t.innerHTML=e,()=>{};const E=n.axes??U.axes,y=n.radius??U.radius,f=n.falloff??U.falloff,b=n.magnetMode??U.magnetMode,F=n.scope??U.scope,d=n.props,M=n.transitionMs??0,V=n.cachePositions??!0,k=n.stabilizeLayout??!0,A=window.scrollY;t.innerHTML=e;const H=rt(t),C=[];for(const i of H){const s=i.textContent??"";if(!s.trim())continue;const S=s.split(/(\S+)/),l=document.createDocumentFragment();for(let o=0;o<S.length;o+=2){const u=S[o],x=S[o+1];if(!x)continue;const B=S[o+3]===void 0?S[o+2]??"":"",Y=document.createElement("span");Y.className=W.word,Y.textContent=u+x+B,l.appendChild(Y),C.push(Y)}i.parentNode.replaceChild(l,i)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-A)>2&&window.scrollTo({top:A,behavior:"instant"})}),C.length===0)return()=>{};const g=getComputedStyle(t).fontVariationSettings,O=ot(g,Object.fromEntries(Object.entries(E).map(([i,[s]])=>[i,s])));let D=0;if(k){const i=ot(g,Object.fromEntries(Object.entries(E).map(([X,[,B]])=>[X,B]))),s=t.style.fontVariationSettings,S=t.style.whiteSpace,l=t.style.overflow;t.style.whiteSpace="nowrap",t.style.overflow="visible",t.style.fontVariationSettings=i;const o=t.scrollWidth;t.style.fontVariationSettings=O;const u=t.scrollWidth;t.style.fontVariationSettings=s,t.style.whiteSpace=S,t.style.overflow=l;const x=C.reduce((X,B)=>{var Y;return X+(((Y=B.textContent)==null?void 0:Y.replace(/\s+/g,"").length)??0)},0);x>0&&o>u&&(D=(o-u)/x)}C.forEach(i=>{i.style.fontVariationSettings=O,d&&nt(i,d)});let G=[],R=!1;function q(){const i=window.scrollX,s=window.scrollY;G=C.map(S=>{const l=S.getBoundingClientRect();return{cx:(l.left+l.right)/2+i,cy:(l.top+l.bottom)/2+s}}),R=!0}let $=null;V&&(q(),$=new ResizeObserver(()=>{R=!1}),$.observe(t),(p=(v=document.fonts)==null?void 0:v.ready)==null||p.then(()=>{R=!1}));let j=-9999,w=-9999,J=!1,T=0,I=!0,P=null;function _(){if(!I)return;if(!J){C.forEach(l=>{M>0&&(l.style.transition=`font-variation-settings ${M}ms ease`),l.style.fontVariationSettings=O,k&&(l.style.letterSpacing=""),d&&nt(l,d)}),M>0&&(P!==null&&clearTimeout(P),P=setTimeout(()=>{C.forEach(l=>{l.style.transition=""}),P=null},M)),T=0;return}V&&!R&&q();const i=V?j+window.scrollX:j,s=V?w+window.scrollY:w,S=V?null:C.map(l=>l.getBoundingClientRect());C.forEach((l,o)=>{l.style.transition="";let u,x;if(V)({cx:u,cy:x}=G[o]);else{const z=S[o];u=z.left+z.width/2,x=z.top+z.height/2}const X=Math.sqrt((i-u)**2+(s-x)**2),B=Math.max(0,1-X/y),Y=f==="quadratic"?B*B:B,K=b==="repel"?1-Y:Y,Q={};for(const z of Object.keys(E)){const[ct,pt]=E[z]??[300,500];Q[z]=ct+(pt-ct)*K}l.style.fontVariationSettings=ot(g,Q),k&&D!==0&&(l.style.letterSpacing=`${(-D*K).toFixed(3)}px`),d&&lt(l,d,Y)}),T=requestAnimationFrame(_)}function Z(i){j=i.clientX,w=i.clientY,J||(J=!0),T===0&&(T=requestAnimationFrame(_))}function a(){J=!1,T===0&&(T=requestAnimationFrame(_))}function c(i){i.touches.length!==0&&(j=i.touches[0].clientX,w=i.touches[0].clientY,J||(J=!0),T===0&&(T=requestAnimationFrame(_)))}function m(){J=!1,T===0&&(T=requestAnimationFrame(_))}const r=F==="document"?document:t;return r.addEventListener("mousemove",Z),r.addEventListener("mouseleave",a),r.addEventListener("touchmove",c,{passive:!0}),r.addEventListener("touchend",m),()=>{I=!1,cancelAnimationFrame(T),P!==null&&clearTimeout(P),$&&$.disconnect(),r.removeEventListener("mousemove",Z),r.removeEventListener("mouseleave",a),r.removeEventListener("touchmove",c),r.removeEventListener("touchend",m),t.innerHTML=e}}function dt(t){const e=h.useRef(null),n=h.useRef(null),E=h.useRef(t);E.current=t;const y=h.useRef(null),f=t.mode??"word",{axes:b,radius:F,falloff:d,magnetMode:M,wdthBoost:V,scope:k}=t,A=b?JSON.stringify(b):void 0,H=t.props?JSON.stringify(t.props):void 0,C=h.useCallback(()=>{const g=e.current;if(!g)return;n.current===null&&(n.current=at(g)),y.current&&(y.current(),y.current=null),(E.current.mode??"word")==="word"?y.current=ft(g,n.current,E.current):y.current=ut(g,n.current,E.current)},[f,A,F,d,M,V,k,H]);return h.useLayoutEffect(()=>(C(),()=>{y.current&&(y.current(),y.current=null)}),[C]),h.useEffect(()=>{var g,O;(O=(g=document.fonts)==null?void 0:g.ready)==null||O.then(C)},[C]),e}const mt=h.forwardRef(function({children:e,as:n="p",className:E,style:y,...f},b){const F=dt(f),d=h.useCallback(M=>{F.current=M,typeof b=="function"?b(M):b&&(b.current=M)},[b]);return tt.jsx(n,{ref:d,className:E,style:y,children:e})});mt.displayName="MagnetTypeText";const st=h.forwardRef(function({children:e,as:n="p",className:E,style:y,minWeight:f=300,maxWeight:b=600,proximityRadius:F,spreadRadius:d,fixedAxes:M={},cachePositions:V=!0,rafThrottle:k=!0,stabilizeLayout:A=!0},H){const C=h.useRef(null),g=h.useRef(null),O=h.useRef([]),D=h.useRef([]),G=h.useRef(null),R=h.useRef(!1),q=h.useRef(null),$=h.useRef(0),j=h.useCallback(a=>{C.current=a,typeof H=="function"?H(a):H&&(H.current=a)},[H]);function w(a){const c=[`'wght' ${a.toFixed(0)}`];for(const[m,r]of Object.entries(M))c.push(`'${m}' ${r}`);return c.join(", ")}function J(){const a=C.current;if(!a)return;const c=window.scrollX,m=window.scrollY,r=a.getBoundingClientRect();G.current={top:r.top+m,left:r.left+c,right:r.right+c,bottom:r.bottom+m},D.current=O.current.map(L=>{if(!L)return{cx:0,cy:0};const N=L.getBoundingClientRect();return{cx:(N.left+N.right)/2+c,cy:(N.top+N.bottom)/2+m}}),R.current=!0}function T(a,c){const m=C.current;if(!m)return;V&&!R.current&&J();let r,L,N,v,p,i;if(V&&G.current)p=a+window.scrollX,i=c+window.scrollY,{top:r,left:L,right:N,bottom:v}=G.current;else{const o=m.getBoundingClientRect();p=a,i=c,r=o.top,L=o.left,N=o.right,v=o.bottom}const s=Math.max(L-p,0,p-N),S=Math.max(r-i,0,i-v),l=Math.sqrt(s*s+S*S);if(F!==void 0&&!d){const u=1-(1-Math.max(0,1-l/F))**2;m.style.fontVariationSettings=w(f+(b-f)*u);return}if(d){if(F!==void 0&&l>F){m.style.fontVariationSettings=w(f);for(const u of O.current)u&&(u.style.fontVariationSettings=w(f),A&&(u.style.letterSpacing=""));return}if(l>d){for(const u of O.current)u&&(u.style.fontVariationSettings=w(f),A&&(u.style.letterSpacing=""));return}const o=O.current;if(V&&D.current.length===o.length)for(let u=0;u<o.length;u++){const x=o[u];if(!x)continue;const{cx:X,cy:B}=D.current[u],Y=Math.sqrt((p-X)**2+(i-B)**2),Q=1-(1-Math.max(0,1-Y/d))**2;x.style.fontVariationSettings=w(f+(b-f)*Q),A&&$.current!==0&&(x.style.letterSpacing=`${(-$.current*Q).toFixed(3)}px`)}else for(const u of o){if(!u)continue;const x=u.getBoundingClientRect(),X=(x.left+x.right)/2,B=(x.top+x.bottom)/2,Y=Math.sqrt((a-X)**2+(c-B)**2),Q=1-(1-Math.max(0,1-Y/d))**2;u.style.fontVariationSettings=w(f+(b-f)*Q),A&&$.current!==0&&(u.style.letterSpacing=`${(-$.current*Q).toFixed(3)}px`)}}}const I=h.useCallback(a=>{g.current={x:a.clientX,y:a.clientY},k?q.current===null&&(q.current=requestAnimationFrame(()=>{q.current=null,g.current&&T(g.current.x,g.current.y)})):T(a.clientX,a.clientY)},[f,b,F,d,V,k,A,JSON.stringify(M)]),P=h.useCallback(()=>{g.current&&T(g.current.x,g.current.y)},[f,b,F,d,V,A,JSON.stringify(M)]),_=h.useCallback(()=>{g.current=null,q.current!==null&&(cancelAnimationFrame(q.current),q.current=null);const a=C.current;a&&(a.style.fontVariationSettings=w(f));for(const c of O.current)c&&(c.style.fontVariationSettings=w(f),A&&(c.style.letterSpacing=""))},[f,A,JSON.stringify(M)]);h.useEffect(()=>(window.addEventListener("mousemove",I,{passive:!0}),window.addEventListener("scroll",P,{passive:!0,capture:!0}),document.documentElement.addEventListener("mouseleave",_),()=>{window.removeEventListener("mousemove",I),window.removeEventListener("scroll",P,{capture:!0}),document.documentElement.removeEventListener("mouseleave",_),q.current!==null&&(cancelAnimationFrame(q.current),q.current=null)}),[I,P,_]),h.useEffect(()=>{var m,r;if(!V||!d)return;R.current=!1;const a=C.current;if(!a)return;const c=new ResizeObserver(()=>{R.current=!1});return c.observe(a),(r=(m=document.fonts)==null?void 0:m.ready)==null||r.then(()=>{R.current=!1}),()=>c.disconnect()},[V,d]),h.useEffect(()=>{R.current=!1},[e,d]),h.useEffect(()=>{var c,m;if(!A||!d){$.current=0;return}function a(){const r=C.current;if(!r)return;const L=r.textContent??"",N=L.replace(/\s/g,"").length;if(N===0)return;const v=getComputedStyle(r),p=document.createElement("span");p.style.cssText="position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;",p.style.fontFamily=v.fontFamily,p.style.fontSize=v.fontSize,p.style.fontWeight=v.fontWeight,p.style.lineHeight=v.lineHeight,p.style.letterSpacing=v.letterSpacing,p.textContent=L,document.body.appendChild(p);const i=l=>{const o=[`'wght' ${l.toFixed(0)}`];for(const[u,x]of Object.entries(M))o.push(`'${u}' ${x}`);return o.join(", ")};p.style.fontVariationSettings=i(b);const s=p.scrollWidth;p.style.fontVariationSettings=i(f);const S=p.scrollWidth;document.body.removeChild(p),$.current=s>S?(s-S)/N:0}a(),(m=(c=document.fonts)==null?void 0:c.ready)==null||m.then(a)},[A,d,f,b,e,JSON.stringify(M)]);const Z=h.useMemo(()=>{if(!d)return e;O.current=[];let a=0;function c(m){if(typeof m=="string")return[...m].map(r=>{if(/\s/.test(r))return r;const L=a++;return tt.jsx("span",{ref:N=>{O.current[L]=N},style:{fontVariationSettings:w(f)},children:r},L)});if(Array.isArray(m))return m.map((r,L)=>tt.jsx(h.Fragment,{children:c(r)},L));if(h.isValidElement(m)){const r=m;if(r.props.children!==void 0)return h.cloneElement(r,{},c(r.props.children))}return m}return c(e)},[e,d,f,JSON.stringify(M)]);return tt.jsx(n,{ref:j,className:E,style:{fontVariationSettings:w(f),...y},children:Z})});st.displayName="MagnetChar";const yt=st;exports.MAGNET_TYPE_CLASSES=W;exports.MagnetBlock=yt;exports.MagnetChar=st;exports.MagnetTypeText=mt;exports.applyMagnetType=ut;exports.getCleanHTML=at;exports.removeMagnetType=ht;exports.startMagnetType=ft;exports.useMagnetType=dt;
package/dist/index.d.ts CHANGED
@@ -38,9 +38,15 @@ export declare const MAGNET_TYPE_CLASSES: {
38
38
  readonly probe: "mt-probe";
39
39
  };
40
40
 
41
- export declare const MagnetBlock: default_2.ForwardRefExoticComponent<MagnetBlockProps & default_2.RefAttributes<HTMLElement>>;
41
+ /** @deprecated Use MagnetChar instead */
42
+ export declare const MagnetBlock: default_2.ForwardRefExoticComponent<MagnetCharProps & default_2.RefAttributes<HTMLElement>>;
42
43
 
43
- export declare interface MagnetBlockProps {
44
+ /** @deprecated Use MagnetCharProps instead */
45
+ export declare type MagnetBlockProps = MagnetCharProps;
46
+
47
+ export declare const MagnetChar: default_2.ForwardRefExoticComponent<MagnetCharProps & default_2.RefAttributes<HTMLElement>>;
48
+
49
+ export declare interface MagnetCharProps {
44
50
  children: default_2.ReactNode;
45
51
  /** HTML element to render. Default: 'p' */
46
52
  as?: default_2.ElementType;
@@ -87,7 +93,7 @@ export declare interface MagnetBlockProps {
87
93
  export declare type MagnetModeType = 'attract' | 'repel';
88
94
 
89
95
  /** Which mode magnetType operates in */
90
- export declare type MagnetTypeModeType = 'field' | 'legibility';
96
+ export declare type MagnetTypeModeType = 'word' | 'legibility';
91
97
 
92
98
  /**
93
99
  * Options for the magnetType effect.
@@ -98,9 +104,9 @@ export declare type MagnetTypeModeType = 'field' | 'legibility';
98
104
  */
99
105
  export declare interface MagnetTypeOptions {
100
106
  /**
101
- * Operating mode. Default: 'field'
107
+ * Operating mode. Default: 'word'
102
108
  *
103
- * - **'field'** — cursor proximity drives per-word font-variation-settings.
109
+ * - **'word'** — cursor proximity drives per-word font-variation-settings.
104
110
  * - **'legibility'** — cursor-driven wdth boost for confusable characters; both return a stop function.
105
111
  */
106
112
  mode?: MagnetTypeModeType;
@@ -249,13 +255,13 @@ export declare function startMagnetType(element: HTMLElement, originalHTML: stri
249
255
  /**
250
256
  * React hook that applies the magnetType effect to a ref'd element.
251
257
  *
252
- * For mode: 'field' — starts the cursor proximity rAF loop via startMagnetType.
258
+ * For mode: 'word' — starts the cursor proximity rAF loop via startMagnetType.
253
259
  * For mode: 'legibility' — starts the cursor-driven wdth boost via applyMagnetType.
254
260
  *
255
261
  * Both modes return a stop function on mount and restart when options change.
256
262
  * No ResizeObserver needed — the rAF loop reads live getBoundingClientRect each frame.
257
263
  *
258
- * Defaults to 'field' mode if mode is undefined.
264
+ * Defaults to 'word' mode if mode is undefined.
259
265
  */
260
266
  export declare function useMagnetType(options: MagnetTypeOptions): RefObject<HTMLElement | null>;
261
267
 
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import rt, { useRef as I, useCallback as z, useLayoutEffect as ft, useEffect as W, forwardRef as lt, useMemo as dt } from "react";
3
- import { jsx as tt } from "react/jsx-runtime";
4
- const it = {
2
+ import st, { useRef as _, useCallback as W, useLayoutEffect as pt, useEffect as tt, forwardRef as ut, useMemo as ht } from "react";
3
+ import { jsx as et } from "react/jsx-runtime";
4
+ const at = {
5
5
  i: 3,
6
6
  l: 3,
7
7
  1: 3,
@@ -23,14 +23,14 @@ const it = {
23
23
  c: 1,
24
24
  e: 1
25
25
  // similar bowls
26
- }, nt = {
26
+ }, ot = {
27
27
  /** Applied to each word span in field mode */
28
28
  word: "mt-word",
29
29
  /** Applied to each character span in legibility mode */
30
30
  char: "mt-char",
31
31
  /** Applied to measurement probe spans (never in final output) */
32
32
  probe: "mt-probe"
33
- }, U = {
33
+ }, G = {
34
34
  axes: { wght: [300, 500] },
35
35
  radius: 120,
36
36
  falloff: "quadratic",
@@ -38,486 +38,488 @@ const it = {
38
38
  wdthBoost: 6,
39
39
  scope: "document"
40
40
  };
41
- function ct(t, n = []) {
42
- return t.nodeType === Node.TEXT_NODE ? n.push(t) : t.childNodes.forEach((o) => ct(o, n)), n;
41
+ function it(t, e = []) {
42
+ return t.nodeType === Node.TEXT_NODE ? e.push(t) : t.childNodes.forEach((n) => it(n, e)), e;
43
43
  }
44
- function et(t, n, o) {
45
- if (!t || t === "normal") return `"${n}" ${o}`;
46
- const M = new RegExp(`(["'])${n}\\1\\s+[\\d.eE+-]+`), h = `"${n}" ${o}`;
44
+ function nt(t, e, n) {
45
+ if (!t || t === "normal") return `"${e}" ${n}`;
46
+ const M = new RegExp(`(["'])${e}\\1\\s+[\\d.eE+-]+`), h = `"${e}" ${n}`;
47
47
  return M.test(t) ? t.replace(M, h) : `${t}, ${h}`;
48
48
  }
49
- function st(t, n) {
50
- let o = t;
51
- for (const [M, h] of Object.entries(n))
52
- o = et(o, M, h);
53
- return o;
49
+ function ct(t, e) {
50
+ let n = t;
51
+ for (const [M, h] of Object.entries(e))
52
+ n = nt(n, M, h);
53
+ return n;
54
54
  }
55
- function at(t, n, o) {
56
- if (n.opacity !== void 0) {
57
- const [M, h] = n.opacity;
58
- t.style.opacity = String(M + (h - M) * o);
55
+ function ft(t, e, n) {
56
+ if (e.opacity !== void 0) {
57
+ const [M, h] = e.opacity;
58
+ t.style.opacity = String(M + (h - M) * n);
59
59
  }
60
- n.italic === !0 && (t.style.fontStyle = o > 0.5 ? "italic" : "");
60
+ e.italic === !0 && (t.style.fontStyle = n > 0.5 ? "italic" : "");
61
61
  }
62
- function ot(t, n) {
63
- n.opacity !== void 0 && (t.style.opacity = String(n.opacity[0])), n.italic === !0 && (t.style.fontStyle = "");
62
+ function rt(t, e) {
63
+ e.opacity !== void 0 && (t.style.opacity = String(e.opacity[0])), e.italic === !0 && (t.style.fontStyle = "");
64
64
  }
65
- function mt(t) {
66
- const n = t.cloneNode(!0), o = n.querySelectorAll(
67
- `.${nt.word}, .${nt.char}`
65
+ function yt(t) {
66
+ const e = t.cloneNode(!0), n = e.querySelectorAll(
67
+ `.${ot.word}, .${ot.char}`
68
68
  );
69
- return Array.from(o).reverse().forEach((h) => {
70
- const d = h.parentNode;
71
- if (d) {
72
- for (; h.firstChild; ) d.insertBefore(h.firstChild, h);
73
- d.removeChild(h);
69
+ return Array.from(n).reverse().forEach((h) => {
70
+ const f = h.parentNode;
71
+ if (f) {
72
+ for (; h.firstChild; ) f.insertBefore(h.firstChild, h);
73
+ f.removeChild(h);
74
74
  }
75
- }), n.innerHTML;
75
+ }), e.innerHTML;
76
76
  }
77
- function Mt(t, n) {
78
- t.innerHTML = n;
77
+ function Ct(t, e) {
78
+ t.innerHTML = e;
79
79
  }
80
- function pt(t, n, o = {}) {
81
- var p, r;
80
+ function gt(t, e, n = {}) {
81
+ var m, r, b, V;
82
82
  if (typeof window > "u") return () => {
83
83
  };
84
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
85
- return t.innerHTML = n, () => {
84
+ if ((r = (m = window.matchMedia) == null ? void 0 : m.call(window, "(prefers-reduced-motion: reduce)")) != null && r.matches)
85
+ return t.innerHTML = e, () => {
86
86
  };
87
- const M = o.wdthBoost ?? U.wdthBoost, h = o.radius ?? U.radius, d = o.falloff ?? U.falloff, b = o.scope ?? U.scope, F = o.props, m = o.transitionMs ?? 0, w = o.cachePositions ?? !0, L = window.scrollY;
88
- t.innerHTML = n;
89
- const X = getComputedStyle(t).fontVariationSettings, V = X.match(/"wdth"\s+([\d.eE+-]+)/), j = V ? parseFloat(V[1]) : 100, x = ct(t), y = [];
90
- for (const a of x) {
91
- const E = a.textContent ?? "";
92
- if (!E || !E.split("").some((g) => g in it)) continue;
93
- const e = document.createDocumentFragment();
94
- for (const g of E) {
95
- const i = it[g];
96
- if (i === void 0) {
97
- const u = e.lastChild;
98
- u && u.nodeType === Node.TEXT_NODE ? u.textContent += g : e.appendChild(document.createTextNode(g));
87
+ const M = n.wdthBoost ?? G.wdthBoost, h = n.radius ?? G.radius, f = n.falloff ?? G.falloff, T = n.scope ?? G.scope, F = n.props, d = n.transitionMs ?? 0, S = n.cachePositions ?? !0, L = window.scrollY;
88
+ t.innerHTML = e;
89
+ const k = getComputedStyle(t).fontVariationSettings, N = k.match(/"wdth"\s+([\d.eE+-]+)/), H = N ? parseFloat(N[1]) : 100, E = it(t), y = [];
90
+ for (const g of E) {
91
+ const p = g.textContent ?? "";
92
+ if (!p || !p.split("").some((w) => w in at)) continue;
93
+ const s = document.createDocumentFragment();
94
+ for (const w of p) {
95
+ const l = at[w];
96
+ if (l === void 0) {
97
+ const o = s.lastChild;
98
+ o && o.nodeType === Node.TEXT_NODE ? o.textContent += w : s.appendChild(document.createTextNode(w));
99
99
  } else {
100
- const u = document.createElement("span");
101
- u.className = nt.char, u.style.fontVariationSettings = et(X, "wdth", j), u.textContent = g, e.appendChild(u), y.push({ span: u, riskLevel: i });
100
+ const o = document.createElement("span");
101
+ o.className = ot.char, o.style.fontVariationSettings = nt(k, "wdth", H), o.textContent = w, s.appendChild(o), y.push({ span: o, riskLevel: l });
102
102
  }
103
103
  }
104
- a.parentNode.replaceChild(e, a);
104
+ g.parentNode.replaceChild(s, g);
105
105
  }
106
106
  if (requestAnimationFrame(() => {
107
107
  typeof window < "u" && Math.abs(window.scrollY - L) > 2 && window.scrollTo({ top: L, behavior: "instant" });
108
108
  }), y.length === 0) return () => {
109
109
  };
110
- F && y.forEach(({ span: a }) => ot(a, F));
111
- let N = [], H = !1;
112
- function _() {
113
- const a = window.scrollX, E = window.scrollY;
114
- N = y.map(({ span: c }) => {
115
- const e = c.getBoundingClientRect();
116
- return { cx: (e.left + e.right) / 2 + a, cy: (e.top + e.bottom) / 2 + E };
117
- }), H = !0;
110
+ F && y.forEach(({ span: g }) => rt(g, F));
111
+ let O = [], D = !1;
112
+ function K() {
113
+ const g = window.scrollX, p = window.scrollY;
114
+ O = y.map(({ span: i }) => {
115
+ const s = i.getBoundingClientRect();
116
+ return { cx: (s.left + s.right) / 2 + g, cy: (s.top + s.bottom) / 2 + p };
117
+ }), D = !0;
118
118
  }
119
119
  let Y = null;
120
- w && (_(), Y = new ResizeObserver(() => {
121
- H = !1;
122
- }), Y.observe(t), (r = (p = document.fonts) == null ? void 0 : p.ready) == null || r.then(() => {
123
- H = !1;
120
+ S && (K(), Y = new ResizeObserver(() => {
121
+ D = !1;
122
+ }), Y.observe(t), (V = (b = document.fonts) == null ? void 0 : b.ready) == null || V.then(() => {
123
+ D = !1;
124
124
  }));
125
- let A = -9999, $ = -9999, B = !1, v = 0, R = !0, T = null;
126
- function D() {
127
- if (!R) return;
128
- if (!B) {
129
- y.forEach(({ span: e }) => {
130
- m > 0 && (e.style.transition = `font-variation-settings ${m}ms ease`), e.style.fontVariationSettings = et(X, "wdth", j), F && ot(e, F);
131
- }), m > 0 && (T !== null && clearTimeout(T), T = setTimeout(() => {
132
- y.forEach(({ span: e }) => {
133
- e.style.transition = "";
134
- }), T = null;
135
- }, m)), v = 0;
125
+ let A = -9999, $ = -9999, X = !1, v = 0, J = !0, C = null;
126
+ function I() {
127
+ if (!J) return;
128
+ if (!X) {
129
+ y.forEach(({ span: s }) => {
130
+ d > 0 && (s.style.transition = `font-variation-settings ${d}ms ease`), s.style.fontVariationSettings = nt(k, "wdth", H), F && rt(s, F);
131
+ }), d > 0 && (C !== null && clearTimeout(C), C = setTimeout(() => {
132
+ y.forEach(({ span: s }) => {
133
+ s.style.transition = "";
134
+ }), C = null;
135
+ }, d)), v = 0;
136
136
  return;
137
137
  }
138
- w && !H && _();
139
- const a = w ? A + window.scrollX : A, E = w ? $ + window.scrollY : $, c = w ? null : y.map(({ span: e }) => e.getBoundingClientRect());
140
- y.forEach(({ span: e, riskLevel: g }, i) => {
141
- e.style.transition = "";
142
- let u, O;
143
- if (w)
144
- ({ cx: u, cy: O } = N[i]);
138
+ S && !D && K();
139
+ const g = S ? A + window.scrollX : A, p = S ? $ + window.scrollY : $, i = S ? null : y.map(({ span: s }) => s.getBoundingClientRect());
140
+ y.forEach(({ span: s, riskLevel: w }, l) => {
141
+ s.style.transition = "";
142
+ let o, u;
143
+ if (S)
144
+ ({ cx: o, cy: u } = O[l]);
145
145
  else {
146
- const P = c[i];
147
- u = P.left + P.width / 2, O = P.top + P.height / 2;
146
+ const U = i[l];
147
+ o = U.left + U.width / 2, u = U.top + U.height / 2;
148
148
  }
149
- const S = Math.sqrt((a - u) ** 2 + (E - O) ** 2), f = Math.max(0, 1 - S / h), C = d === "quadratic" ? f * f : f, q = M * (g / 3) * C;
150
- e.style.fontVariationSettings = et(X, "wdth", j + q), F && at(e, F, C);
151
- }), v = requestAnimationFrame(D);
149
+ const x = Math.sqrt((g - o) ** 2 + (p - u) ** 2), j = Math.max(0, 1 - x / h), B = f === "quadratic" ? j * j : j, q = M * (w / 3) * B;
150
+ s.style.fontVariationSettings = nt(k, "wdth", H + q), F && ft(s, F, B);
151
+ }), v = requestAnimationFrame(I);
152
152
  }
153
- function k(a) {
154
- A = a.clientX, $ = a.clientY, B || (B = !0), v === 0 && (v = requestAnimationFrame(D));
153
+ function R(g) {
154
+ A = g.clientX, $ = g.clientY, X || (X = !0), v === 0 && (v = requestAnimationFrame(I));
155
155
  }
156
- function J() {
157
- B = !1, v === 0 && (v = requestAnimationFrame(D));
156
+ function P() {
157
+ X = !1, v === 0 && (v = requestAnimationFrame(I));
158
158
  }
159
- function Q(a) {
160
- a.touches.length !== 0 && (A = a.touches[0].clientX, $ = a.touches[0].clientY, B || (B = !0), v === 0 && (v = requestAnimationFrame(D)));
159
+ function Z(g) {
160
+ g.touches.length !== 0 && (A = g.touches[0].clientX, $ = g.touches[0].clientY, X || (X = !0), v === 0 && (v = requestAnimationFrame(I)));
161
161
  }
162
- function l() {
163
- B = !1, v === 0 && (v = requestAnimationFrame(D));
162
+ function a() {
163
+ X = !1, v === 0 && (v = requestAnimationFrame(I));
164
164
  }
165
- const s = b === "document" ? document : t;
166
- return s.addEventListener("mousemove", k), s.addEventListener("mouseleave", J), s.addEventListener("touchmove", Q, { passive: !0 }), s.addEventListener("touchend", l), () => {
167
- R = !1, cancelAnimationFrame(v), T !== null && clearTimeout(T), Y && Y.disconnect(), s.removeEventListener("mousemove", k), s.removeEventListener("mouseleave", J), s.removeEventListener("touchmove", Q), s.removeEventListener("touchend", l), t.innerHTML = n;
165
+ const c = T === "document" ? document : t;
166
+ return c.addEventListener("mousemove", R), c.addEventListener("mouseleave", P), c.addEventListener("touchmove", Z, { passive: !0 }), c.addEventListener("touchend", a), () => {
167
+ J = !1, cancelAnimationFrame(v), C !== null && clearTimeout(C), Y && Y.disconnect(), c.removeEventListener("mousemove", R), c.removeEventListener("mouseleave", P), c.removeEventListener("touchmove", Z), c.removeEventListener("touchend", a), t.innerHTML = e;
168
168
  };
169
169
  }
170
- function ht(t, n, o = {}) {
171
- var a, E;
170
+ function vt(t, e, n = {}) {
171
+ var b, V, g, p;
172
172
  if (typeof window > "u") return () => {
173
173
  };
174
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
175
- return t.innerHTML = n, () => {
174
+ if ((V = (b = window.matchMedia) == null ? void 0 : b.call(window, "(prefers-reduced-motion: reduce)")) != null && V.matches)
175
+ return t.innerHTML = e, () => {
176
176
  };
177
- const M = o.axes ?? U.axes, h = o.radius ?? U.radius, d = o.falloff ?? U.falloff, b = o.magnetMode ?? U.magnetMode, F = o.scope ?? U.scope, m = o.props, w = o.transitionMs ?? 0, L = o.cachePositions ?? !0, X = o.stabilizeLayout ?? !0, V = window.scrollY;
178
- t.innerHTML = n;
179
- const j = ct(t), x = [];
180
- for (const c of j) {
181
- const e = c.textContent ?? "";
182
- if (!e.trim()) continue;
183
- const g = e.split(/(\S+)/), i = document.createDocumentFragment();
184
- for (let u = 0; u < g.length; u += 2) {
185
- const O = g[u], S = g[u + 1];
186
- if (!S) continue;
187
- const C = g[u + 3] === void 0 ? g[u + 2] ?? "" : "", q = document.createElement("span");
188
- q.className = nt.word, q.textContent = O + S + C, i.appendChild(q), x.push(q);
177
+ const M = n.axes ?? G.axes, h = n.radius ?? G.radius, f = n.falloff ?? G.falloff, T = n.magnetMode ?? G.magnetMode, F = n.scope ?? G.scope, d = n.props, S = n.transitionMs ?? 0, L = n.cachePositions ?? !0, k = n.stabilizeLayout ?? !0, N = window.scrollY;
178
+ t.innerHTML = e;
179
+ const H = it(t), E = [];
180
+ for (const i of H) {
181
+ const s = i.textContent ?? "";
182
+ if (!s.trim()) continue;
183
+ const w = s.split(/(\S+)/), l = document.createDocumentFragment();
184
+ for (let o = 0; o < w.length; o += 2) {
185
+ const u = w[o], x = w[o + 1];
186
+ if (!x) continue;
187
+ const B = w[o + 3] === void 0 ? w[o + 2] ?? "" : "", q = document.createElement("span");
188
+ q.className = ot.word, q.textContent = u + x + B, l.appendChild(q), E.push(q);
189
189
  }
190
- c.parentNode.replaceChild(i, c);
190
+ i.parentNode.replaceChild(l, i);
191
191
  }
192
192
  if (requestAnimationFrame(() => {
193
- typeof window < "u" && Math.abs(window.scrollY - V) > 2 && window.scrollTo({ top: V, behavior: "instant" });
194
- }), x.length === 0) return () => {
193
+ typeof window < "u" && Math.abs(window.scrollY - N) > 2 && window.scrollTo({ top: N, behavior: "instant" });
194
+ }), E.length === 0) return () => {
195
195
  };
196
- const y = getComputedStyle(t).fontVariationSettings, N = st(
196
+ const y = getComputedStyle(t).fontVariationSettings, O = ct(
197
197
  y,
198
- Object.fromEntries(Object.entries(M).map(([c, [e]]) => [c, e]))
198
+ Object.fromEntries(Object.entries(M).map(([i, [s]]) => [i, s]))
199
199
  );
200
- let H = 0;
201
- if (X) {
202
- const c = st(
200
+ let D = 0;
201
+ if (k) {
202
+ const i = ct(
203
203
  y,
204
- Object.fromEntries(Object.entries(M).map(([f, [, C]]) => [f, C]))
205
- ), e = t.style.fontVariationSettings, g = t.style.whiteSpace, i = t.style.overflow;
206
- t.style.whiteSpace = "nowrap", t.style.overflow = "visible", t.style.fontVariationSettings = c;
204
+ Object.fromEntries(Object.entries(M).map(([j, [, B]]) => [j, B]))
205
+ ), s = t.style.fontVariationSettings, w = t.style.whiteSpace, l = t.style.overflow;
206
+ t.style.whiteSpace = "nowrap", t.style.overflow = "visible", t.style.fontVariationSettings = i;
207
+ const o = t.scrollWidth;
208
+ t.style.fontVariationSettings = O;
207
209
  const u = t.scrollWidth;
208
- t.style.fontVariationSettings = N;
209
- const O = t.scrollWidth;
210
- t.style.fontVariationSettings = e, t.style.whiteSpace = g, t.style.overflow = i;
211
- const S = x.reduce(
212
- (f, C) => {
210
+ t.style.fontVariationSettings = s, t.style.whiteSpace = w, t.style.overflow = l;
211
+ const x = E.reduce(
212
+ (j, B) => {
213
213
  var q;
214
- return f + (((q = C.textContent) == null ? void 0 : q.replace(/\s+/g, "").length) ?? 0);
214
+ return j + (((q = B.textContent) == null ? void 0 : q.replace(/\s+/g, "").length) ?? 0);
215
215
  },
216
216
  0
217
217
  );
218
- S > 0 && u > O && (H = (u - O) / S);
218
+ x > 0 && o > u && (D = (o - u) / x);
219
219
  }
220
- x.forEach((c) => {
221
- c.style.fontVariationSettings = N, m && ot(c, m);
220
+ E.forEach((i) => {
221
+ i.style.fontVariationSettings = O, d && rt(i, d);
222
222
  });
223
- let _ = [], Y = !1;
223
+ let K = [], Y = !1;
224
224
  function A() {
225
- const c = window.scrollX, e = window.scrollY;
226
- _ = x.map((g) => {
227
- const i = g.getBoundingClientRect();
228
- return { cx: (i.left + i.right) / 2 + c, cy: (i.top + i.bottom) / 2 + e };
225
+ const i = window.scrollX, s = window.scrollY;
226
+ K = E.map((w) => {
227
+ const l = w.getBoundingClientRect();
228
+ return { cx: (l.left + l.right) / 2 + i, cy: (l.top + l.bottom) / 2 + s };
229
229
  }), Y = !0;
230
230
  }
231
231
  let $ = null;
232
232
  L && (A(), $ = new ResizeObserver(() => {
233
233
  Y = !1;
234
- }), $.observe(t), (E = (a = document.fonts) == null ? void 0 : a.ready) == null || E.then(() => {
234
+ }), $.observe(t), (p = (g = document.fonts) == null ? void 0 : g.ready) == null || p.then(() => {
235
235
  Y = !1;
236
236
  }));
237
- let B = -9999, v = -9999, R = !1, T = 0, D = !0, k = null;
238
- function J() {
239
- if (!D) return;
240
- if (!R) {
241
- x.forEach((i) => {
242
- w > 0 && (i.style.transition = `font-variation-settings ${w}ms ease`), i.style.fontVariationSettings = N, X && (i.style.letterSpacing = ""), m && ot(i, m);
243
- }), w > 0 && (k !== null && clearTimeout(k), k = setTimeout(() => {
244
- x.forEach((i) => {
245
- i.style.transition = "";
246
- }), k = null;
247
- }, w)), T = 0;
237
+ let X = -9999, v = -9999, J = !1, C = 0, I = !0, R = null;
238
+ function P() {
239
+ if (!I) return;
240
+ if (!J) {
241
+ E.forEach((l) => {
242
+ S > 0 && (l.style.transition = `font-variation-settings ${S}ms ease`), l.style.fontVariationSettings = O, k && (l.style.letterSpacing = ""), d && rt(l, d);
243
+ }), S > 0 && (R !== null && clearTimeout(R), R = setTimeout(() => {
244
+ E.forEach((l) => {
245
+ l.style.transition = "";
246
+ }), R = null;
247
+ }, S)), C = 0;
248
248
  return;
249
249
  }
250
250
  L && !Y && A();
251
- const c = L ? B + window.scrollX : B, e = L ? v + window.scrollY : v, g = L ? null : x.map((i) => i.getBoundingClientRect());
252
- x.forEach((i, u) => {
253
- i.style.transition = "";
254
- let O, S;
251
+ const i = L ? X + window.scrollX : X, s = L ? v + window.scrollY : v, w = L ? null : E.map((l) => l.getBoundingClientRect());
252
+ E.forEach((l, o) => {
253
+ l.style.transition = "";
254
+ let u, x;
255
255
  if (L)
256
- ({ cx: O, cy: S } = _[u]);
256
+ ({ cx: u, cy: x } = K[o]);
257
257
  else {
258
- const K = g[u];
259
- O = K.left + K.width / 2, S = K.top + K.height / 2;
258
+ const z = w[o];
259
+ u = z.left + z.width / 2, x = z.top + z.height / 2;
260
260
  }
261
- const f = Math.sqrt((c - O) ** 2 + (e - S) ** 2), C = Math.max(0, 1 - f / h), q = d === "quadratic" ? C * C : C, P = b === "repel" ? 1 - q : q, Z = {};
262
- for (const K of Object.keys(M)) {
263
- const [G, ut] = M[K] ?? [300, 500];
264
- Z[K] = G + (ut - G) * P;
261
+ const j = Math.sqrt((i - u) ** 2 + (s - x) ** 2), B = Math.max(0, 1 - j / h), q = f === "quadratic" ? B * B : B, U = T === "repel" ? 1 - q : q, Q = {};
262
+ for (const z of Object.keys(M)) {
263
+ const [lt, mt] = M[z] ?? [300, 500];
264
+ Q[z] = lt + (mt - lt) * U;
265
265
  }
266
- i.style.fontVariationSettings = st(y, Z), X && H !== 0 && (i.style.letterSpacing = `${(-H * P).toFixed(3)}px`), m && at(i, m, q);
267
- }), T = requestAnimationFrame(J);
266
+ l.style.fontVariationSettings = ct(y, Q), k && D !== 0 && (l.style.letterSpacing = `${(-D * U).toFixed(3)}px`), d && ft(l, d, q);
267
+ }), C = requestAnimationFrame(P);
268
268
  }
269
- function Q(c) {
270
- B = c.clientX, v = c.clientY, R || (R = !0), T === 0 && (T = requestAnimationFrame(J));
269
+ function Z(i) {
270
+ X = i.clientX, v = i.clientY, J || (J = !0), C === 0 && (C = requestAnimationFrame(P));
271
271
  }
272
- function l() {
273
- R = !1, T === 0 && (T = requestAnimationFrame(J));
272
+ function a() {
273
+ J = !1, C === 0 && (C = requestAnimationFrame(P));
274
274
  }
275
- function s(c) {
276
- c.touches.length !== 0 && (B = c.touches[0].clientX, v = c.touches[0].clientY, R || (R = !0), T === 0 && (T = requestAnimationFrame(J)));
275
+ function c(i) {
276
+ i.touches.length !== 0 && (X = i.touches[0].clientX, v = i.touches[0].clientY, J || (J = !0), C === 0 && (C = requestAnimationFrame(P)));
277
277
  }
278
- function p() {
279
- R = !1, T === 0 && (T = requestAnimationFrame(J));
278
+ function m() {
279
+ J = !1, C === 0 && (C = requestAnimationFrame(P));
280
280
  }
281
281
  const r = F === "document" ? document : t;
282
- return r.addEventListener("mousemove", Q), r.addEventListener("mouseleave", l), r.addEventListener("touchmove", s, { passive: !0 }), r.addEventListener("touchend", p), () => {
283
- D = !1, cancelAnimationFrame(T), k !== null && clearTimeout(k), $ && $.disconnect(), r.removeEventListener("mousemove", Q), r.removeEventListener("mouseleave", l), r.removeEventListener("touchmove", s), r.removeEventListener("touchend", p), t.innerHTML = n;
282
+ return r.addEventListener("mousemove", Z), r.addEventListener("mouseleave", a), r.addEventListener("touchmove", c, { passive: !0 }), r.addEventListener("touchend", m), () => {
283
+ I = !1, cancelAnimationFrame(C), R !== null && clearTimeout(R), $ && $.disconnect(), r.removeEventListener("mousemove", Z), r.removeEventListener("mouseleave", a), r.removeEventListener("touchmove", c), r.removeEventListener("touchend", m), t.innerHTML = e;
284
284
  };
285
285
  }
286
- function yt(t) {
287
- const n = I(null), o = I(null), M = I(t);
286
+ function wt(t) {
287
+ const e = _(null), n = _(null), M = _(t);
288
288
  M.current = t;
289
- const h = I(null), d = t.mode ?? "field", { axes: b, radius: F, falloff: m, magnetMode: w, wdthBoost: L, scope: X } = t, V = b ? JSON.stringify(b) : void 0, j = t.props ? JSON.stringify(t.props) : void 0, x = z(() => {
290
- const y = n.current;
289
+ const h = _(null), f = t.mode ?? "word", { axes: T, radius: F, falloff: d, magnetMode: S, wdthBoost: L, scope: k } = t, N = T ? JSON.stringify(T) : void 0, H = t.props ? JSON.stringify(t.props) : void 0, E = W(() => {
290
+ const y = e.current;
291
291
  if (!y) return;
292
- o.current === null && (o.current = mt(y)), h.current && (h.current(), h.current = null), (M.current.mode ?? "field") === "field" ? h.current = ht(y, o.current, M.current) : h.current = pt(y, o.current, M.current);
293
- }, [d, V, F, m, w, L, X, j]);
294
- return ft(() => (x(), () => {
292
+ n.current === null && (n.current = yt(y)), h.current && (h.current(), h.current = null), (M.current.mode ?? "word") === "word" ? h.current = vt(y, n.current, M.current) : h.current = gt(y, n.current, M.current);
293
+ }, [f, N, F, d, S, L, k, H]);
294
+ return pt(() => (E(), () => {
295
295
  h.current && (h.current(), h.current = null);
296
- }), [x]), W(() => {
297
- var y, N;
298
- (N = (y = document.fonts) == null ? void 0 : y.ready) == null || N.then(x);
299
- }, [x]), n;
296
+ }), [E]), tt(() => {
297
+ var y, O;
298
+ (O = (y = document.fonts) == null ? void 0 : y.ready) == null || O.then(E);
299
+ }, [E]), e;
300
300
  }
301
- const gt = lt(
302
- function({ children: n, as: o = "p", className: M, style: h, ...d }, b) {
303
- const F = yt(d), m = z(
304
- (w) => {
305
- F.current = w, typeof b == "function" ? b(w) : b && (b.current = w);
301
+ const St = ut(
302
+ function({ children: e, as: n = "p", className: M, style: h, ...f }, T) {
303
+ const F = wt(f), d = W(
304
+ (S) => {
305
+ F.current = S, typeof T == "function" ? T(S) : T && (T.current = S);
306
306
  },
307
307
  // eslint-disable-next-line react-hooks/exhaustive-deps
308
- [b]
308
+ [T]
309
309
  );
310
- return /* @__PURE__ */ tt(o, { ref: m, className: M, style: h, children: n });
310
+ return /* @__PURE__ */ et(n, { ref: d, className: M, style: h, children: e });
311
311
  }
312
312
  );
313
- gt.displayName = "MagnetTypeText";
314
- const vt = lt(
313
+ St.displayName = "MagnetTypeText";
314
+ const dt = ut(
315
315
  function({
316
- children: n,
317
- as: o = "p",
316
+ children: e,
317
+ as: n = "p",
318
318
  className: M,
319
319
  style: h,
320
- minWeight: d = 300,
321
- maxWeight: b = 600,
320
+ minWeight: f = 300,
321
+ maxWeight: T = 600,
322
322
  proximityRadius: F,
323
- spreadRadius: m,
324
- fixedAxes: w = {},
323
+ spreadRadius: d,
324
+ fixedAxes: S = {},
325
325
  cachePositions: L = !0,
326
- rafThrottle: X = !0,
327
- stabilizeLayout: V = !0
328
- }, j) {
329
- const x = I(null), y = I(null), N = I([]), H = I([]), _ = I(null), Y = I(!1), A = I(null), $ = I(0), B = z(
330
- (l) => {
331
- x.current = l, typeof j == "function" ? j(l) : j && (j.current = l);
326
+ rafThrottle: k = !0,
327
+ stabilizeLayout: N = !0
328
+ }, H) {
329
+ const E = _(null), y = _(null), O = _([]), D = _([]), K = _(null), Y = _(!1), A = _(null), $ = _(0), X = W(
330
+ (a) => {
331
+ E.current = a, typeof H == "function" ? H(a) : H && (H.current = a);
332
332
  },
333
333
  // eslint-disable-next-line react-hooks/exhaustive-deps
334
- [j]
334
+ [H]
335
335
  );
336
- function v(l) {
337
- const s = [`'wght' ${l.toFixed(0)}`];
338
- for (const [p, r] of Object.entries(w)) s.push(`'${p}' ${r}`);
339
- return s.join(", ");
336
+ function v(a) {
337
+ const c = [`'wght' ${a.toFixed(0)}`];
338
+ for (const [m, r] of Object.entries(S)) c.push(`'${m}' ${r}`);
339
+ return c.join(", ");
340
340
  }
341
- function R() {
342
- const l = x.current;
343
- if (!l) return;
344
- const s = window.scrollX, p = window.scrollY, r = l.getBoundingClientRect();
345
- _.current = {
346
- top: r.top + p,
347
- left: r.left + s,
348
- right: r.right + s,
349
- bottom: r.bottom + p
350
- }, H.current = N.current.map((a) => {
351
- if (!a) return { cx: 0, cy: 0 };
352
- const E = a.getBoundingClientRect();
341
+ function J() {
342
+ const a = E.current;
343
+ if (!a) return;
344
+ const c = window.scrollX, m = window.scrollY, r = a.getBoundingClientRect();
345
+ K.current = {
346
+ top: r.top + m,
347
+ left: r.left + c,
348
+ right: r.right + c,
349
+ bottom: r.bottom + m
350
+ }, D.current = O.current.map((b) => {
351
+ if (!b) return { cx: 0, cy: 0 };
352
+ const V = b.getBoundingClientRect();
353
353
  return {
354
- cx: (E.left + E.right) / 2 + s,
355
- cy: (E.top + E.bottom) / 2 + p
354
+ cx: (V.left + V.right) / 2 + c,
355
+ cy: (V.top + V.bottom) / 2 + m
356
356
  };
357
357
  }), Y.current = !0;
358
358
  }
359
- function T(l, s) {
360
- const p = x.current;
361
- if (!p) return;
362
- L && !Y.current && R();
363
- let r, a, E, c, e, g;
364
- if (L && _.current)
365
- e = l + window.scrollX, g = s + window.scrollY, { top: r, left: a, right: E, bottom: c } = _.current;
359
+ function C(a, c) {
360
+ const m = E.current;
361
+ if (!m) return;
362
+ L && !Y.current && J();
363
+ let r, b, V, g, p, i;
364
+ if (L && K.current)
365
+ p = a + window.scrollX, i = c + window.scrollY, { top: r, left: b, right: V, bottom: g } = K.current;
366
366
  else {
367
- const S = p.getBoundingClientRect();
368
- e = l, g = s, r = S.top, a = S.left, E = S.right, c = S.bottom;
367
+ const o = m.getBoundingClientRect();
368
+ p = a, i = c, r = o.top, b = o.left, V = o.right, g = o.bottom;
369
369
  }
370
- const i = Math.max(a - e, 0, e - E), u = Math.max(r - g, 0, g - c), O = Math.sqrt(i * i + u * u);
371
- if (F !== void 0 && !m) {
372
- const f = 1 - (1 - Math.max(0, 1 - O / F)) ** 2;
373
- p.style.fontVariationSettings = v(d + (b - d) * f);
370
+ const s = Math.max(b - p, 0, p - V), w = Math.max(r - i, 0, i - g), l = Math.sqrt(s * s + w * w);
371
+ if (F !== void 0 && !d) {
372
+ const u = 1 - (1 - Math.max(0, 1 - l / F)) ** 2;
373
+ m.style.fontVariationSettings = v(f + (T - f) * u);
374
374
  return;
375
375
  }
376
- if (m) {
377
- if (F !== void 0 && O > F) {
378
- p.style.fontVariationSettings = v(d);
379
- for (const f of N.current)
380
- f && (f.style.fontVariationSettings = v(d), V && (f.style.letterSpacing = ""));
376
+ if (d) {
377
+ if (F !== void 0 && l > F) {
378
+ m.style.fontVariationSettings = v(f);
379
+ for (const u of O.current)
380
+ u && (u.style.fontVariationSettings = v(f), N && (u.style.letterSpacing = ""));
381
381
  return;
382
382
  }
383
- if (O > m) {
384
- for (const f of N.current)
385
- f && (f.style.fontVariationSettings = v(d), V && (f.style.letterSpacing = ""));
383
+ if (l > d) {
384
+ for (const u of O.current)
385
+ u && (u.style.fontVariationSettings = v(f), N && (u.style.letterSpacing = ""));
386
386
  return;
387
387
  }
388
- const S = N.current;
389
- if (L && H.current.length === S.length)
390
- for (let f = 0; f < S.length; f++) {
391
- const C = S[f];
392
- if (!C) continue;
393
- const { cx: q, cy: P } = H.current[f], Z = Math.sqrt((e - q) ** 2 + (g - P) ** 2), G = 1 - (1 - Math.max(0, 1 - Z / m)) ** 2;
394
- C.style.fontVariationSettings = v(d + (b - d) * G), V && $.current !== 0 && (C.style.letterSpacing = `${(-$.current * G).toFixed(3)}px`);
388
+ const o = O.current;
389
+ if (L && D.current.length === o.length)
390
+ for (let u = 0; u < o.length; u++) {
391
+ const x = o[u];
392
+ if (!x) continue;
393
+ const { cx: j, cy: B } = D.current[u], q = Math.sqrt((p - j) ** 2 + (i - B) ** 2), Q = 1 - (1 - Math.max(0, 1 - q / d)) ** 2;
394
+ x.style.fontVariationSettings = v(f + (T - f) * Q), N && $.current !== 0 && (x.style.letterSpacing = `${(-$.current * Q).toFixed(3)}px`);
395
395
  }
396
396
  else
397
- for (const f of S) {
398
- if (!f) continue;
399
- const C = f.getBoundingClientRect(), q = (C.left + C.right) / 2, P = (C.top + C.bottom) / 2, Z = Math.sqrt((l - q) ** 2 + (s - P) ** 2), G = 1 - (1 - Math.max(0, 1 - Z / m)) ** 2;
400
- f.style.fontVariationSettings = v(d + (b - d) * G), V && $.current !== 0 && (f.style.letterSpacing = `${(-$.current * G).toFixed(3)}px`);
397
+ for (const u of o) {
398
+ if (!u) continue;
399
+ const x = u.getBoundingClientRect(), j = (x.left + x.right) / 2, B = (x.top + x.bottom) / 2, q = Math.sqrt((a - j) ** 2 + (c - B) ** 2), Q = 1 - (1 - Math.max(0, 1 - q / d)) ** 2;
400
+ u.style.fontVariationSettings = v(f + (T - f) * Q), N && $.current !== 0 && (u.style.letterSpacing = `${(-$.current * Q).toFixed(3)}px`);
401
401
  }
402
402
  }
403
403
  }
404
- const D = z(
405
- (l) => {
406
- y.current = { x: l.clientX, y: l.clientY }, X ? A.current === null && (A.current = requestAnimationFrame(() => {
407
- A.current = null, y.current && T(y.current.x, y.current.y);
408
- })) : T(l.clientX, l.clientY);
404
+ const I = W(
405
+ (a) => {
406
+ y.current = { x: a.clientX, y: a.clientY }, k ? A.current === null && (A.current = requestAnimationFrame(() => {
407
+ A.current = null, y.current && C(y.current.x, y.current.y);
408
+ })) : C(a.clientX, a.clientY);
409
409
  },
410
410
  // eslint-disable-next-line react-hooks/exhaustive-deps
411
- [d, b, F, m, L, X, V, JSON.stringify(w)]
412
- ), k = z(
411
+ [f, T, F, d, L, k, N, JSON.stringify(S)]
412
+ ), R = W(
413
413
  () => {
414
- y.current && T(y.current.x, y.current.y);
414
+ y.current && C(y.current.x, y.current.y);
415
415
  },
416
416
  // eslint-disable-next-line react-hooks/exhaustive-deps
417
- [d, b, F, m, L, V, JSON.stringify(w)]
418
- ), J = z(
417
+ [f, T, F, d, L, N, JSON.stringify(S)]
418
+ ), P = W(
419
419
  () => {
420
420
  y.current = null, A.current !== null && (cancelAnimationFrame(A.current), A.current = null);
421
- const l = x.current;
422
- l && (l.style.fontVariationSettings = v(d));
423
- for (const s of N.current)
424
- s && (s.style.fontVariationSettings = v(d), V && (s.style.letterSpacing = ""));
421
+ const a = E.current;
422
+ a && (a.style.fontVariationSettings = v(f));
423
+ for (const c of O.current)
424
+ c && (c.style.fontVariationSettings = v(f), N && (c.style.letterSpacing = ""));
425
425
  },
426
426
  // eslint-disable-next-line react-hooks/exhaustive-deps
427
- [d, V, JSON.stringify(w)]
427
+ [f, N, JSON.stringify(S)]
428
428
  );
429
- W(() => (window.addEventListener("mousemove", D, { passive: !0 }), window.addEventListener("scroll", k, { passive: !0, capture: !0 }), document.documentElement.addEventListener("mouseleave", J), () => {
430
- window.removeEventListener("mousemove", D), window.removeEventListener("scroll", k, { capture: !0 }), document.documentElement.removeEventListener("mouseleave", J), A.current !== null && (cancelAnimationFrame(A.current), A.current = null);
431
- }), [D, k, J]), W(() => {
432
- var p, r;
433
- if (!L || !m) return;
429
+ tt(() => (window.addEventListener("mousemove", I, { passive: !0 }), window.addEventListener("scroll", R, { passive: !0, capture: !0 }), document.documentElement.addEventListener("mouseleave", P), () => {
430
+ window.removeEventListener("mousemove", I), window.removeEventListener("scroll", R, { capture: !0 }), document.documentElement.removeEventListener("mouseleave", P), A.current !== null && (cancelAnimationFrame(A.current), A.current = null);
431
+ }), [I, R, P]), tt(() => {
432
+ var m, r;
433
+ if (!L || !d) return;
434
434
  Y.current = !1;
435
- const l = x.current;
436
- if (!l) return;
437
- const s = new ResizeObserver(() => {
435
+ const a = E.current;
436
+ if (!a) return;
437
+ const c = new ResizeObserver(() => {
438
438
  Y.current = !1;
439
439
  });
440
- return s.observe(l), (r = (p = document.fonts) == null ? void 0 : p.ready) == null || r.then(() => {
440
+ return c.observe(a), (r = (m = document.fonts) == null ? void 0 : m.ready) == null || r.then(() => {
441
441
  Y.current = !1;
442
- }), () => s.disconnect();
443
- }, [L, m]), W(() => {
442
+ }), () => c.disconnect();
443
+ }, [L, d]), tt(() => {
444
444
  Y.current = !1;
445
- }, [n, m]), W(() => {
446
- var s, p;
447
- if (!V || !m) {
445
+ }, [e, d]), tt(() => {
446
+ var c, m;
447
+ if (!N || !d) {
448
448
  $.current = 0;
449
449
  return;
450
450
  }
451
- function l() {
452
- const r = x.current;
451
+ function a() {
452
+ const r = E.current;
453
453
  if (!r) return;
454
- const a = r.textContent ?? "", E = a.replace(/\s/g, "").length;
455
- if (E === 0) return;
456
- const c = getComputedStyle(r), e = document.createElement("span");
457
- e.style.cssText = "position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;", e.style.fontFamily = c.fontFamily, e.style.fontSize = c.fontSize, e.style.fontWeight = c.fontWeight, e.style.lineHeight = c.lineHeight, e.style.letterSpacing = c.letterSpacing, e.textContent = a, document.body.appendChild(e);
458
- const g = (O) => {
459
- const S = [`'wght' ${O.toFixed(0)}`];
460
- for (const [f, C] of Object.entries(w)) S.push(`'${f}' ${C}`);
461
- return S.join(", ");
454
+ const b = r.textContent ?? "", V = b.replace(/\s/g, "").length;
455
+ if (V === 0) return;
456
+ const g = getComputedStyle(r), p = document.createElement("span");
457
+ p.style.cssText = "position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;", p.style.fontFamily = g.fontFamily, p.style.fontSize = g.fontSize, p.style.fontWeight = g.fontWeight, p.style.lineHeight = g.lineHeight, p.style.letterSpacing = g.letterSpacing, p.textContent = b, document.body.appendChild(p);
458
+ const i = (l) => {
459
+ const o = [`'wght' ${l.toFixed(0)}`];
460
+ for (const [u, x] of Object.entries(S)) o.push(`'${u}' ${x}`);
461
+ return o.join(", ");
462
462
  };
463
- e.style.fontVariationSettings = g(b);
464
- const i = e.scrollWidth;
465
- e.style.fontVariationSettings = g(d);
466
- const u = e.scrollWidth;
467
- document.body.removeChild(e), $.current = i > u ? (i - u) / E : 0;
463
+ p.style.fontVariationSettings = i(T);
464
+ const s = p.scrollWidth;
465
+ p.style.fontVariationSettings = i(f);
466
+ const w = p.scrollWidth;
467
+ document.body.removeChild(p), $.current = s > w ? (s - w) / V : 0;
468
468
  }
469
- l(), (p = (s = document.fonts) == null ? void 0 : s.ready) == null || p.then(l);
470
- }, [V, m, d, b, n, JSON.stringify(w)]);
471
- const Q = dt(() => {
472
- if (!m) return n;
473
- N.current = [];
474
- let l = 0;
475
- function s(p) {
476
- if (typeof p == "string")
477
- return [...p].map((r) => {
469
+ a(), (m = (c = document.fonts) == null ? void 0 : c.ready) == null || m.then(a);
470
+ }, [N, d, f, T, e, JSON.stringify(S)]);
471
+ const Z = ht(() => {
472
+ if (!d) return e;
473
+ O.current = [];
474
+ let a = 0;
475
+ function c(m) {
476
+ if (typeof m == "string")
477
+ return [...m].map((r) => {
478
478
  if (/\s/.test(r)) return r;
479
- const a = l++;
480
- return /* @__PURE__ */ tt(
479
+ const b = a++;
480
+ return /* @__PURE__ */ et(
481
481
  "span",
482
482
  {
483
- ref: (E) => {
484
- N.current[a] = E;
483
+ ref: (V) => {
484
+ O.current[b] = V;
485
485
  },
486
- style: { fontVariationSettings: v(d) },
486
+ style: { fontVariationSettings: v(f) },
487
487
  children: r
488
488
  },
489
- a
489
+ b
490
490
  );
491
491
  });
492
- if (Array.isArray(p)) return p.map((r, a) => /* @__PURE__ */ tt(rt.Fragment, { children: s(r) }, a));
493
- if (rt.isValidElement(p)) {
494
- const r = p;
492
+ if (Array.isArray(m)) return m.map((r, b) => /* @__PURE__ */ et(st.Fragment, { children: c(r) }, b));
493
+ if (st.isValidElement(m)) {
494
+ const r = m;
495
495
  if (r.props.children !== void 0)
496
- return rt.cloneElement(r, {}, s(r.props.children));
496
+ return st.cloneElement(r, {}, c(r.props.children));
497
497
  }
498
- return p;
498
+ return m;
499
499
  }
500
- return s(n);
501
- }, [n, m, d, JSON.stringify(w)]);
502
- return /* @__PURE__ */ tt(
503
- o,
500
+ return c(e);
501
+ }, [e, d, f, JSON.stringify(S)]);
502
+ return /* @__PURE__ */ et(
503
+ n,
504
504
  {
505
- ref: B,
505
+ ref: X,
506
506
  className: M,
507
- style: { fontVariationSettings: v(d), ...h },
508
- children: Q
507
+ style: { fontVariationSettings: v(f), ...h },
508
+ children: Z
509
509
  }
510
510
  );
511
511
  }
512
512
  );
513
- vt.displayName = "MagnetBlock";
513
+ dt.displayName = "MagnetChar";
514
+ const xt = dt;
514
515
  export {
515
- nt as MAGNET_TYPE_CLASSES,
516
- vt as MagnetBlock,
517
- gt as MagnetTypeText,
518
- pt as applyMagnetType,
519
- mt as getCleanHTML,
520
- Mt as removeMagnetType,
521
- ht as startMagnetType,
522
- yt as useMagnetType
516
+ ot as MAGNET_TYPE_CLASSES,
517
+ xt as MagnetBlock,
518
+ dt as MagnetChar,
519
+ St as MagnetTypeText,
520
+ gt as applyMagnetType,
521
+ yt as getCleanHTML,
522
+ Ct as removeMagnetType,
523
+ vt as startMagnetType,
524
+ wt as useMagnetType
523
525
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/magnettype",
3
- "version": "1.1.6",
3
+ "version": "1.2.1",
4
4
  "description": "Cursor-field per-word variable font axis variation and per-character legibility mode",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",