@liiift-studio/threadtext 0.1.0 → 0.2.0

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
@@ -2,12 +2,12 @@
2
2
 
3
3
  **Render any text as photorealistic, procedural satin-stitch embroidery — in real time, in the browser, from the font's actual glyph geometry.**
4
4
 
5
- > Status: **in-progress.** The framework-agnostic core (`createThreadText`) + React
6
- > hook/component are now extracted, typed, tested, and building to ESM/CJS/`.d.ts`. Still
7
- > to come: Framer/Webflow bindings and the landing site (and a free-licensed demo font).
8
- > The original reference renderer lives in [`demo/index.html`](./demo/index.html) and stays
9
- > the visual oracle. See [`HANDOFF.md`](./HANDOFF.md) for what's left; tool-build conventions
10
- > are in the monorepo [`../GUIDE.md`](../GUIDE.md).
5
+ > Status: **shipped.** Published to npm (`@liiift-studio/threadtext`) with a framework-agnostic
6
+ > core (`createThreadText`), a React hook + component, Framer and Webflow ports, tests, and a
7
+ > live landing site at [threadtext.com](https://threadtext.com) (demo font: the OFL Fraunces
8
+ > variable). The original single-file reference renderer lives in
9
+ > [`demo/index.html`](./demo/index.html) as the visual oracle; tool-build conventions are in the
10
+ > monorepo [`../GUIDE.md`](../GUIDE.md).
11
11
 
12
12
  Homepage: [threadtext.com](https://threadtext.com) · npm: `@liiift-studio/threadtext`
13
13
 
@@ -58,10 +58,12 @@ const thread = createThreadText(document.getElementById('host'), {
58
58
  weight: 680,
59
59
  })
60
60
 
61
- thread.setText('Threads') // appended letters sew in; unrelated text re-sews the word
62
- thread.replay() // re-run the full sew-in
63
- thread.resize() // re-fit to the container
64
- thread.destroy() // cancel rAF, remove listeners, free canvases
61
+ thread.setText('Threads') // re-fit to width and redraw instantly (no sew-in)
62
+ thread.update({ threadColor: '#e6c200', fill: 0.8 }) // live changes instant, no re-sew
63
+ thread.replay() // re-run the sew-in animation
64
+ thread.resize() // re-fit to the container
65
+ thread.focus() // focus the surface for typing (editable mode)
66
+ thread.destroy() // cancel rAF, remove listeners, free canvases
65
67
  ```
66
68
 
67
69
  React:
@@ -73,14 +75,19 @@ import { ThreadText } from '@liiift-studio/threadtext'
73
75
  ```
74
76
 
75
77
  Thread colour, `fill` (size — the word re-fits to the container width on load/resize),
76
- `weight`, `font`, `sewRate`, `sheen`, `animate`, and `editable` (type straight on the
77
- artwork) are all options see `ThreadTextOptions`. Change any of them live with
78
+ `weight`, `font`, `sewStyle` (`'machine'` satin rows or `'hand'` a single thread that
79
+ wanders stitch by stitch), `sewRate`, `sheen`, `animate`, and `editable` (type straight on
80
+ the artwork — backed by a real input, so touch keyboards and IME work) with an `onTextChange`
81
+ callback are all options — see `ThreadTextOptions`. Change any of them live with
78
82
  `instance.update(...)` — it redraws instantly, never re-running the sew-in. `react`/`react-dom`
79
83
  are optional peers; the core is framework-free.
80
84
 
81
85
  ## What's next
82
86
 
83
- The renderer is proven. Finishing it means productionising per [`../GUIDE.md`](../GUIDE.md): extract a framework-agnostic **core** with a typed API, add the **React hook + component**, **Framer** + **Webflow** bindings, tests, a landing **site**, then ship (npm + Vercel). A proposed core API and step-by-step plan are in `HANDOFF.md`.
87
+ Now shipped. Remaining ideas (see `HANDOFF.md`): region-limit or move the per-keystroke geometry
88
+ rebuild to a Web Worker/WASM (it currently re-runs the full distance-transform pipeline on every
89
+ edit); real variable-font axis control (`opsz`/`wght`) via path-based glyph rendering, since canvas
90
+ `fillText` can't set `font-variation-settings`; and additional stitch modes (chain, cross, running).
84
91
 
85
92
  ---
86
93
 
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const X=require("react"),xe=require("react/jsx-runtime"),pt={bg:"tt-bg",fx:"tt-fx",caret:"tt-caret"};function Q(m,e){let o=Math.imul(m|0,374761393)+Math.imul(e|0,668265263)|0;return o=Math.imul(o^o>>>13,1274126177),((o^o>>>16)>>>0)/4294967295}function Qt(m,e){const o=Math.floor(m),r=Math.floor(e),i=m-o,a=e-r,f=i*i*(3-2*i),A=a*a*(3-2*a),E=Q(o,r),C=Q(o+1,r),c=Q(o,r+1),d=Q(o+1,r+1);return E+(C-E)*f+(c-E)*A+(E-C-c+d)*f*A}const Mt=1e20;function Zt(m,e,o,r,i){let a=0;r[0]=0,i[0]=-Mt,i[1]=Mt;for(let f=1;f<e;f++){let A=(m[f]+f*f-(m[r[a]]+r[a]*r[a]))/(2*f-2*r[a]);for(;A<=i[a];)a--,A=(m[f]+f*f-(m[r[a]]+r[a]*r[a]))/(2*f-2*r[a]);a++,r[a]=f,i[a]=A,i[a+1]=Mt}a=0;for(let f=0;f<e;f++){for(;i[a+1]<f;)a++;o[f]=(f-r[a])*(f-r[a])+m[r[a]]}}function ge(m,e,o){const r=new Float64Array(e*o);for(let c=0;c<e*o;c++)r[c]=m[c]?Mt:0;const i=Math.max(e,o),a=new Float64Array(i),f=new Float64Array(i),A=new Int32Array(i),E=new Float64Array(i+1);for(let c=0;c<e;c++){for(let d=0;d<o;d++)a[d]=r[d*e+c];Zt(a,o,f,A,E);for(let d=0;d<o;d++)r[d*e+c]=f[d]}for(let c=0;c<o;c++){for(let d=0;d<e;d++)a[d]=r[c*e+d];Zt(a,e,f,A,E);for(let d=0;d<e;d++)r[c*e+d]=f[d]}const C=new Float32Array(e*o);for(let c=0;c<e*o;c++)C[c]=Math.sqrt(r[c]);return C}function Ht(m,e,o,r,i){let a=Float32Array.from(m);const f=new Float32Array(e*o),A=1/(2*r+1),E=(C,c)=>C<0?0:C>c?c:C;for(let C=0;C<i;C++){for(let c=0;c<o;c++){const d=c*e;let $=0;for(let B=-r;B<=r;B++)$+=a[d+E(B,e-1)];for(let B=0;B<e;B++)f[d+B]=$*A,$+=a[d+E(B+r+1,e-1)]-a[d+E(B-r,e-1)]}for(let c=0;c<e;c++){let d=0;for(let $=-r;$<=r;$++)d+=f[E($,o-1)*e+c];for(let $=0;$<o;$++)a[$*e+c]=d*A,d+=f[E($+r+1,o-1)*e+c]-f[E($-r,o-1)*e+c]}}return a}function Wt(m,e){if(!m)return e;const o=m.trim();if(o[0]==="#"){let i=o.slice(1);if(i.length===3&&(i=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]),i.length>=6){const a=parseInt(i.slice(0,2),16),f=parseInt(i.slice(2,4),16),A=parseInt(i.slice(4,6),16);if(!Number.isNaN(a)&&!Number.isNaN(f)&&!Number.isNaN(A))return[a,f,A]}return e}const r=o.match(/rgba?\(([^)]+)\)/i);if(r){const i=r[1].split(",").map(a=>parseFloat(a));if(i.length>=3&&i.slice(0,3).every(a=>!Number.isNaN(a)))return[Math.round(i[0]),Math.round(i[1]),Math.round(i[2])]}return e}function K(m){return m<0?0:m>255?255:Math.round(m)}function te(m){return(m.split(",")[0]||"serif").trim().replace(/^["']|["']$/g,"")}function ee(m,e){if(typeof window>"u"||typeof document>"u"||!m)return{setText(){},replay(){},resize(){},update(){},focus(){},destroy(){},get text(){return e.text??""}};let o=e.text??"",r=e.font??"Georgia, serif",i=e.weight??680,a=e.fill??.9,f=e.sewRate??110,A=e.sheen??!0,E=e.animate??!0,C=e.editable??!1,c=e.pitch,d=e.onTextChange,$=te(r);const B=e.reducedMotion??!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);let D=Wt(e.threadColor??"#fffbf3",[255,251,243]),tt=[0,0,0],N=[0,0,0];function et(t){D=Wt(t,[255,251,243]),tt=[D[0]*.585,D[1]*.585,D[2]*.585],N=[D[0]*.47,D[1]*.47,D[2]*.47]}et(e.threadColor??"#fffbf3");const G=[];let P,g;m instanceof HTMLCanvasElement?(P=m,g=m.parentElement??m):(g=m,P=document.createElement("canvas"),G.push(P),g.appendChild(P)),P.classList.add(pt.bg);const O=document.createElement("canvas");G.push(O),O.classList.add(pt.fx),g.appendChild(O),getComputedStyle(g).position==="static"&&(g.style.position="relative"),P.style.display="block",P.style.width="100%",Object.assign(O.style,{position:"absolute",inset:"0",width:"100%",pointerEvents:"none",mixBlendMode:"screen"});let S=null,Z=!1,s=0,h=0,H=0,bt=0,ft=4,nt=[],ot=0,rt=0,Ct=new Uint8Array(0),Tt=new Float32Array(0),ut=new Float32Array(0),dt=new Float32Array(0),U=null,it=null,ht=null,mt=[],yt=3;const L={rows:[],idx:0,acc:0,rate:0,on:!1},Y={x:0,y:0,set:!1};let W=!0,vt=0,Bt=!1,xt=0,q=!1,J=!1;function re(){const t=g.getBoundingClientRect(),n=Math.max(1,Math.round(t.width||Math.min(window.innerWidth*.95,1240))),l=o||"Ag",y=P.getContext("2d");let T=100;y&&(y.font=`${i} 100px ${r}`,T=y.measureText(l).width||100);let b=Math.max(8,100*(n*a)/T),v=b*.7,R=b*.3,I=n*a;if(y){y.font=`${i} ${b}px ${r}`;const p=y.measureText(l);I=p.width||I,p.actualBoundingBoxAscent>0&&(v=p.actualBoundingBoxAscent),p.actualBoundingBoxDescent>0&&(R=p.actualBoundingBoxDescent)}const w=Math.max(1,Math.round(2*Math.max(v,R)*1.12));let u=Math.min(window.devicePixelRatio||1,1.7);for(;n*u*w*u>9e5&&u>.4;)u-=.1;s=Math.max(1,Math.round(n*u)),h=Math.max(1,Math.round(w*u)),H=Math.max(4,Math.round(b*u)),bt=Math.round(s/2-I*u/2);for(const p of[P,O])p.width=s,p.height=h,p.style.height=w+"px";g.style.height=w+"px",ft=c??Math.max(3,h*.0095)}function ie(){it=document.createElement("canvas"),it.width=s,it.height=h,ht=it.getContext("2d",{willReadFrequently:!0})}function Nt(){const t=Math.max(7,ft*2.5),n=Math.max(3.2,ft*1.3);ot=Math.ceil(t)+2,rt=Math.ceil(n)+2,nt=[];for(let l=0;l<20;l++){const y=.34+l/19*.82,T=document.createElement("canvas");T.width=ot,T.height=rt;const b=T.getContext("2d");if(!b){nt.push(T);continue}const v=ot/2,R=rt/2,I=b.createLinearGradient(0,R-n/2,0,R+n/2),w=(k,j)=>`rgba(${K(k[0]*y)},${K(k[1]*y)},${K(k[2]*y)},${j})`;I.addColorStop(0,w(N,0)),I.addColorStop(.16,w(tt,1)),I.addColorStop(.5,w(D,1)),I.addColorStop(.84,w(tt,1)),I.addColorStop(1,w(N,0)),b.fillStyle=I,b.beginPath();const u=n/2;b.moveTo(v-t/2+u,R-u),b.lineTo(v+t/2-u,R-u),b.arc(v+t/2-u,R,u,-Math.PI/2,Math.PI/2),b.lineTo(v-t/2+u,R+u),b.arc(v-t/2+u,R,u,Math.PI/2,-Math.PI/2),b.closePath(),b.fill(),b.globalCompositeOperation="destination-in";const p=b.createLinearGradient(v-t/2,0,v+t/2,0);p.addColorStop(0,"rgba(0,0,0,0)"),p.addColorStop(.22,"rgba(0,0,0,1)"),p.addColorStop(.78,"rgba(0,0,0,1)"),p.addColorStop(1,"rgba(0,0,0,0)"),b.fillStyle=p,b.fillRect(0,0,ot,rt),nt.push(T)}}function ae(){if(!ht)return;const t=ht;t.clearRect(0,0,s,h),t.fillStyle="#fff",t.textAlign="left",t.textBaseline="middle",t.font=`${i} ${H}px ${r}`,t.fillText(o,bt,h*.5);const n=t.getImageData(0,0,s,h).data,l=new Uint8Array(s*h);for(let x=0;x<h;x++)for(let M=0;M<s;M++){const F=x*s+M,_=n[F*4+3]/255,z=Qt(M*.5,x*.5)*.22+Qt(M*1.8,x*1.8)*.1;l[F]=_>.5+(z-.16)?1:0}Ct=l;const y=ge(l,s,h),T=new Float32Array(s*h);for(let x=1;x<h-1;x++)for(let M=1;M<s-1;M++){const F=x*s+M;T[F]=(y[F]*4+y[F-1]+y[F+1]+y[F-s]+y[F+s])/8}const b=new Float32Array(s*h),v=new Float32Array(s*h),R=new Float32Array(s*h),I=new Float32Array(s*h);for(let x=1;x<h-1;x++)for(let M=1;M<s-1;M++){const F=x*s+M;if(!l[F])continue;const _=(T[F+1]-T[F-1])*.5,z=(T[F+s]-T[F-s])*.5;R[F]=_,I[F]=z,b[F]=_*_-z*z,v[F]=2*_*z}const w=Math.max(7,Math.round(h*.05)),u=Ht(b,s,h,w,2),p=Ht(v,s,h,w,2);ut=new Float32Array(s*h),dt=new Float32Array(s*h);for(let x=0;x<s*h;x++){if(!l[x])continue;const M=.5*Math.atan2(p[x],u[x]);ut[x]=Math.cos(M),dt[x]=Math.sin(M)}const k=Math.max(9,h*.04),j=new Float32Array(s*h),ct=-.45,st=-.62,wt=.64;for(let x=1;x<h-1;x++)for(let M=1;M<s-1;M++){const F=x*s+M;if(!l[F])continue;let _=R[F],z=I[F];const Ut=Math.hypot(_,z)||1;_/=Ut,z/=Ut;const Vt=T[F],Yt=(1-Math.min(Vt/k,1))*1.25;let It=-_*Yt,Pt=-z*Yt;const Jt=1.5,Dt=Math.hypot(It,Pt,Jt);It/=Dt,Pt/=Dt;let kt=It*ct+Pt*st+Jt/Dt*wt;kt<0&&(kt=0);const ye=Math.min(1,.45+.55*Math.min(Vt/(k*.7),1));j[F]=Math.min(1.12,(.5+.75*kt)*ye)}Tt=j,U=document.createElement("canvas"),U.width=s,U.height=h;const V=U.getContext("2d");if(V){const x=V.createImageData(s,h);for(let M=0;M<s*h;M++)l[M]&&(x.data[M*4]=255,x.data[M*4+1]=255,x.data[M*4+2]=255,x.data[M*4+3]=255);V.putImageData(x,0,0)}}function le(){const t=Math.max(1.6,ft*.6);yt=t;const n=[];for(let l=0;l<h;l+=t)for(let y=0;y<s;y+=t){const T=(Q(Math.floor(y),Math.floor(l))-.5)*t*.9,b=(Q(Math.floor(l),Math.floor(y))-.5)*t*.9,v=Math.round(y+T),R=Math.round(l+b);if(v<1||R<1||v>=s-1||R>=h-1)continue;const I=R*s+v;if(!Ct[I])continue;const w=Math.atan2(dt[I],ut[I]),u=.86+Q(v*7,R*13)*.18;let p=Tt[I]*u;p=p<0?0:p>1.12?1.12:p,n.push({x:v,y:R,ang:w,idx:Math.min(19,Math.max(0,Math.round(p/1.12*19)))})}n.sort((l,y)=>l.x-y.x),mt=n}function Ot(t,n){t.save(),t.translate(n.x,n.y),t.rotate(n.ang),t.drawImage(nt[n.idx],-ot/2,-rt/2),t.restore()}function At(){const t=P.getContext("2d");if(t)for(const n of mt)Ot(t,n)}function Et(){const t=P.getContext("2d");t&&t.clearRect(0,0,s,h)}function ce(t){if(!t.length)return[];const n=Math.max(4,yt*1.5),l=new Map,y=(w,u)=>w+","+u;t.forEach((w,u)=>{const p=y(Math.floor(w.x/n),Math.floor(w.y/n));let k=l.get(p);k||l.set(p,k=[]),k.push(u)});const T=yt*1.8*(yt*1.8);function b(w){const u=t[w],p=Math.floor(u.x/n),k=Math.floor(u.y/n),j=[];for(let ct=-1;ct<=1;ct++)for(let st=-1;st<=1;st++){const wt=l.get(y(p+ct,k+st));if(wt)for(const V of wt){if(V===w)continue;const x=t[V].x-u.x,M=t[V].y-u.y;x*x+M*M<=T&&j.push(V)}}return j}const v=new Uint8Array(t.length),R=[],I=t.map((w,u)=>u).sort((w,u)=>t[w].x+t[w].y-(t[u].x+t[u].y));for(const w of I){if(v[w])continue;let u=[w];for(v[w]=1;u.length;){R.push(u.map(k=>t[k]));const p=[];for(const k of u)for(const j of b(k))v[j]||(v[j]=1,p.push(j));u=p}}return R}function se(){if(Et(),B||!E){At(),L.on=!1;return}L.rows=ce(mt),L.idx=0,L.acc=0,L.rate=f,L.on=L.rows.length>0}function fe(t){const n=P.getContext("2d");if(n)for(t>L.rows.length&&(t=L.rows.length);L.idx<t;){const l=L.rows[L.idx++];for(const y of l)Ot(n,y)}}function ue(t,n){const l=O.getContext("2d");if(!l||(l.clearRect(0,0,s,h),!A||!U))return;const y=s*.32,T=l.createRadialGradient(t,n,0,t,n,y);T.addColorStop(0,"rgba(255,250,238,0.18)"),T.addColorStop(.5,"rgba(255,250,238,0.055)"),T.addColorStop(1,"rgba(255,250,238,0)"),l.fillStyle=T,l.fillRect(0,0,s,h),l.globalCompositeOperation="destination-in",l.drawImage(U,0,0),l.globalCompositeOperation="source-over"}function de(){S||!C||(S=document.createElement("span"),S.className=pt.caret,S.setAttribute("aria-hidden","true"),Object.assign(S.style,{position:"absolute",pointerEvents:"none",borderRadius:"1px",opacity:"0",background:`rgb(${K(D[0])},${K(D[1])},${K(D[2])})`}),g.appendChild(S),G.push(S))}function St(){if(!C||!S)return;const t=P.getContext("2d");if(!t)return;t.font=`${i} ${H}px ${r}`;const n=t.measureText(o).width,l=(P.clientWidth||s)/s,y=H*.7;S.style.left=(bt+n+H*.05)*l+"px",S.style.top=.5*(P.clientHeight||h)-y*l/2+"px",S.style.height=y*l+"px",S.style.width=Math.max(2,Math.round(H*.02*l))+"px",S.style.background=`rgb(${K(D[0])},${K(D[1])},${K(D[2])})`}function jt(t){if(q)return;xt||(xt=t);const n=Math.min(.25,(t-xt)/1e3);xt=t,L.on&&(L.acc+=L.rate*n,fe(L.acc),L.idx>=L.rows.length&&(L.on=!1)),W&&U&&(ue(Y.set?Y.x:s*.5,Y.set?Y.y:h*.34),W=!1),S&&(S.style.opacity=C&&Z&&Math.floor(t/530)%2===0?"1":"0"),vt=requestAnimationFrame(jt)}function gt(t){const n=O.getBoundingClientRect();Y.x=(t.clientX-n.left)/(n.width||1)*s,Y.y=(t.clientY-n.top)/(n.height||1)*h,Y.set=!0,W=!0}function qt(t){if(!(!C||t.metaKey||t.ctrlKey||t.altKey)){if(t.key==="Backspace"){t.preventDefault(),Rt(o.slice(0,-1),!0);return}if(t.key==="Enter"){t.preventDefault(),$t.replay();return}[...t.key].length===1&&(t.preventDefault(),Rt(o+t.key,!0))}}function _t(){Z=!0}function zt(){Z=!1,S&&(S.style.opacity="0")}function Kt(){C&&g.focus()}function Ft(t){C=t,t?(g.tabIndex=0,g.setAttribute("role","textbox"),g.getAttribute("aria-label")||g.setAttribute("aria-label","Editable embroidered text — type to change it"),de(),g.addEventListener("keydown",qt),g.addEventListener("focus",_t),g.addEventListener("blur",zt),g.addEventListener("pointerdown",Kt),St()):(g.removeAttribute("tabindex"),g.removeAttribute("role"),g.removeEventListener("keydown",qt),g.removeEventListener("focus",_t),g.removeEventListener("blur",zt),g.removeEventListener("pointerdown",Kt),S&&(S.style.opacity="0"))}function he(t){if(t!==A)if(A=t,t)window.addEventListener("pointermove",gt,{passive:!0}),W=!0;else{window.removeEventListener("pointermove",gt);const n=O.getContext("2d");n&&n.clearRect(0,0,s,h)}}function at(t){re(),ie(),Nt(),ae(),le(),t?se():(Et(),At(),L.on=!1),St(),W=!0}function Rt(t,n){t=t??"",!(t===o&&J)&&(o=t,J&&(at(!1),n&&d&&d(o)))}let lt;function Xt(t,n,l){const y=`${n} 200px ${t}`;let T=!1;try{T=!!document.fonts&&document.fonts.check(y)}catch{T=!0}if(!document.fonts||T){l();return}let b=!1;const v=()=>{b||(b=!0,lt&&clearTimeout(lt),l())};document.fonts.load(y,o).then(()=>document.fonts.ready).then(v,v),lt=setTimeout(v,1500)}function me(){q||(at(!0),J=!0,Bt||(Bt=!0,vt=requestAnimationFrame(jt)))}C&&Ft(!0),A&&window.addEventListener("pointermove",gt,{passive:!0}),Xt($,i,me);let Lt;function Gt(){clearTimeout(Lt),Lt=setTimeout(()=>$t.resize(),200)}window.addEventListener("resize",Gt);const $t={get text(){return o},setText(t){if(!q){if(!J){o=t??"";return}Rt(t,!1)}},replay(){q||!J||at(!0)},resize(){q||!J||at(!1)},update(t){if(q)return;let n=!1,l=!1;t.font!==void 0&&t.font!==r&&(r=t.font,$=te(r),n=!0),t.weight!==void 0&&t.weight!==i&&(i=t.weight,n=!0),t.fill!==void 0&&t.fill!==a&&(a=t.fill,n=!0),t.pitch!==void 0&&t.pitch!==c&&(c=t.pitch,n=!0),t.threadColor!==void 0&&(et(t.threadColor),l=!0),t.sewRate!==void 0&&(f=t.sewRate),t.animate!==void 0&&(E=t.animate),t.onTextChange!==void 0&&(d=t.onTextChange),t.sheen!==void 0&&he(t.sheen),t.editable!==void 0&&t.editable!==C&&Ft(t.editable),J&&(n?Xt($,i,()=>{q||at(!1)}):l&&(Nt(),Et(),At(),St()),W=!0)},focus(){!q&&C&&g.focus()},destroy(){if(!q){q=!0,cancelAnimationFrame(vt),clearTimeout(Lt),lt&&clearTimeout(lt),window.removeEventListener("resize",Gt),window.removeEventListener("pointermove",gt),C&&Ft(!1);for(const t of G)t.remove();nt=[],mt=[],L.rows=[],Ct=new Uint8Array(0),Tt=new Float32Array(0),ut=new Float32Array(0),dt=new Float32Array(0),U=it=null,ht=null,S=null}}};return $t}function ne(m){const e=X.useRef(null),o=X.useRef(null),r=X.useRef(m);r.current=m;const i=X.useRef(m.onTextChange);i.current=m.onTextChange;const{text:a,font:f,weight:A,threadColor:E,pitch:C,fill:c,animate:d,sewRate:$,sheen:B,editable:D,reducedMotion:tt}=m;return X.useLayoutEffect(()=>{const N=e.current;if(!N)return;const et=ee(N,{...r.current,onTextChange:O=>{var S;return(S=i.current)==null?void 0:S.call(i,O)}});o.current=et;let G=0,P=0,g;return typeof ResizeObserver<"u"&&(g=new ResizeObserver(O=>{const S=Math.round(O[0].contentRect.width);S!==P&&(P=S,cancelAnimationFrame(G),G=requestAnimationFrame(()=>{var Z;return(Z=o.current)==null?void 0:Z.resize()}))}),g.observe(N)),()=>{g==null||g.disconnect(),cancelAnimationFrame(G),et.destroy(),o.current=null}},[tt]),X.useEffect(()=>{var N;(N=o.current)==null||N.update({font:f,weight:A,threadColor:E,pitch:C,fill:c,animate:d,sewRate:$,sheen:B,editable:D})},[f,A,E,C,c,d,$,B,D]),X.useEffect(()=>{var N;(N=o.current)==null||N.setText(a)},[a]),e}const oe=X.forwardRef(function({className:e,style:o,as:r="div","aria-label":i,role:a,"aria-describedby":f,...A},E){const C=ne(A),c=X.useCallback(d=>{C.current=d,typeof E=="function"?E(d):E&&(E.current=d)},[C,E]);return xe.jsx(r,{ref:c,className:e,style:o,role:a??"img","aria-label":i??A.text,"aria-describedby":f})});oe.displayName="ThreadText";exports.THREAD_TEXT_CLASSES=pt;exports.ThreadText=oe;exports.createThreadText=ee;exports.useThreadText=ne;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=require("react"),Fe=require("react/jsx-runtime"),Et={bg:"tt-bg",fx:"tt-fx",caret:"tt-caret"};function ot(y,o){let r=Math.imul(y|0,374761393)+Math.imul(o|0,668265263)|0;return r=Math.imul(r^r>>>13,1274126177),((r^r>>>16)>>>0)/4294967295}function ne(y,o){const r=Math.floor(y),l=Math.floor(o),c=y-r,s=o-l,h=c*c*(3-2*c),R=s*s*(3-2*s),E=ot(r,l),P=ot(r+1,l),f=ot(r,l+1),m=ot(r+1,l+1);return E+(P-E)*h+(f-E)*R+(E-P-f+m)*h*R}const At=1e20;function oe(y,o,r,l,c){let s=0;l[0]=0,c[0]=-At,c[1]=At;for(let h=1;h<o;h++){let R=(y[h]+h*h-(y[l[s]]+l[s]*l[s]))/(2*h-2*l[s]);for(;R<=c[s];)s--,R=(y[h]+h*h-(y[l[s]]+l[s]*l[s]))/(2*h-2*l[s]);s++,l[s]=h,c[s]=R,c[s+1]=At}s=0;for(let h=0;h<o;h++){for(;c[s+1]<h;)s++;r[h]=(h-l[s])*(h-l[s])+y[l[s]]}}function Re(y,o,r){const l=new Float64Array(o*r);for(let f=0;f<o*r;f++)l[f]=y[f]?At:0;const c=Math.max(o,r),s=new Float64Array(c),h=new Float64Array(c),R=new Int32Array(c),E=new Float64Array(c+1);for(let f=0;f<o;f++){for(let m=0;m<r;m++)s[m]=l[m*o+f];oe(s,r,h,R,E);for(let m=0;m<r;m++)l[m*o+f]=h[m]}for(let f=0;f<r;f++){for(let m=0;m<o;m++)s[m]=l[f*o+m];oe(s,o,h,R,E);for(let m=0;m<o;m++)l[f*o+m]=h[m]}const P=new Float32Array(o*r);for(let f=0;f<o*r;f++)P[f]=Math.sqrt(l[f]);return P}function re(y,o,r,l,c){let s=Float32Array.from(y);const h=new Float32Array(o*r),R=1/(2*l+1),E=(P,f)=>P<0?0:P>f?f:P;for(let P=0;P<c;P++){for(let f=0;f<r;f++){const m=f*o;let O=0;for(let z=-l;z<=l;z++)O+=s[m+E(z,o-1)];for(let z=0;z<o;z++)h[m+z]=O*R,O+=s[m+E(z+l+1,o-1)]-s[m+E(z-l,o-1)]}for(let f=0;f<o;f++){let m=0;for(let O=-l;O<=l;O++)m+=h[E(O,r-1)*o+f];for(let O=0;O<r;O++)s[O*o+f]=m*R,m+=h[E(O+l+1,r-1)*o+f]-h[E(O-l,r-1)*o+f]}}return s}function ie(y,o){if(!y)return o;const r=y.trim();if(r[0]==="#"){let c=r.slice(1);if(c.length===3&&(c=c[0]+c[0]+c[1]+c[1]+c[2]+c[2]),c.length>=6){const s=parseInt(c.slice(0,2),16),h=parseInt(c.slice(2,4),16),R=parseInt(c.slice(4,6),16);if(!Number.isNaN(s)&&!Number.isNaN(h)&&!Number.isNaN(R))return[s,h,R]}return o}const l=r.match(/rgba?\(([^)]+)\)/i);if(l){const c=l[1].split(",").map(s=>parseFloat(s));if(c.length>=3&&c.slice(0,3).every(s=>!Number.isNaN(s)))return[Math.round(c[0]),Math.round(c[1]),Math.round(c[2])]}return o}function J(y){return y<0?0:y>255?255:Math.round(y)}function St(y,o,r){return y<o?o:y>r?r:y}function ae(y){return(y.split(",")[0]||"serif").trim().replace(/^["']|["']$/g,"")}function le(y,o){if(typeof window>"u"||typeof document>"u"||!y)return{setText(){},replay(){},resize(){},update(){},focus(){},destroy(){},get text(){return o.text??""}};let r=o.text??"",l=o.font??"Georgia, serif",c=St(o.weight??680,1,1e3),s=St(o.fill??.9,.05,1),h=Math.max(1,o.sewRate??110),R=o.sewStyle==="hand"?"hand":"machine",E=o.sheen??!0,P=o.animate??!0,f=o.editable??!1,m=o.pitch,O=o.onTextChange,z=ae(l);const pt=o.reducedMotion??!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);let B=ie(o.threadColor??"#fffbf3",[255,251,243]),ut=[0,0,0],G=[0,0,0];function dt(t){B=ie(t,[255,251,243]),ut=[B[0]*.585,B[1]*.585,B[2]*.585],G=[B[0]*.47,B[1]*.47,B[2]*.47]}dt(o.threadColor??"#fffbf3");const Y=[];let N,k;y instanceof HTMLCanvasElement?(N=y,k=y.parentElement??y):(k=y,N=document.createElement("canvas"),Y.push(N),k.appendChild(N)),N.classList.add(Et.bg);const U=document.createElement("canvas");Y.push(U),U.classList.add(Et.fx),k.appendChild(U),getComputedStyle(k).position==="static"&&(k.style.position="relative"),N.style.display="block",N.style.width="100%",Object.assign(U.style,{position:"absolute",inset:"0",width:"100%",pointerEvents:"none",mixBlendMode:"screen"});let w=null,F=null,tt=!1,u=0,g=0,rt=0,Ft=0,it=4,at=[],ht=0,mt=0,zt=-1,_t="",Xt=-1,Gt=-1,Rt=new Uint8Array(0),Lt=new Float32Array(0),wt=new Float32Array(0),Mt=new Float32Array(0),Z=null,lt=null,Ct=null,ct=[],et=3;const I={rows:[],idx:0,acc:0,rate:0,on:!1},H={x:0,y:0,set:!1};let nt=!0,It=0,xt=!1,yt=0,K=!1,W=!1;function fe(){const t=k.getBoundingClientRect(),e=Math.max(1,Math.round(t.width||Math.min(window.innerWidth*.95,1240))),i=r||"Ag",d=N.getContext("2d");d&&(d.textBaseline="middle");let b=100;d&&(d.font=`${c} 100px ${l}`,b=d.measureText(i).width||100);const C=Math.max(8,100*(e*s)/b);let T=C*.62,A=C*.62,S=e*s;if(d){d.font=`${c} ${C}px ${l}`;const a=d.measureText(i);S=a.width||S,a.actualBoundingBoxAscent>0&&(T=a.actualBoundingBoxAscent),a.actualBoundingBoxDescent>0&&(A=a.actualBoundingBoxDescent)}const M=Math.max(1,Math.round(2*Math.max(T,A)*1.1));let n=Math.min(window.devicePixelRatio||1,1.7);for(;e*n*M*n>9e5&&n>.4;)n-=.1;u=Math.max(1,Math.round(e*n)),g=Math.max(1,Math.round(M*n)),rt=Math.max(4,Math.round(C*n)),Ft=Math.round(u/2-S*n/2);for(const a of[N,U])a.width=u,a.height=g,a.style.height=M+"px";k.style.height=M+"px",it=m??Math.max(3,g*.0095)}function ue(){lt&&Xt===u&&Gt===g||(lt=document.createElement("canvas"),lt.width=u,lt.height=g,Ct=lt.getContext("2d",{willReadFrequently:!0}),Xt=u,Gt=g)}function Ut(){const t=Math.max(7,it*2.5),e=Math.max(3.2,it*1.3);ht=Math.ceil(t)+2,mt=Math.ceil(e)+2,at=[];for(let i=0;i<20;i++){const d=.34+i/19*.82,b=document.createElement("canvas");b.width=ht,b.height=mt;const C=b.getContext("2d");if(!C){at.push(b);continue}const T=ht/2,A=mt/2,S=C.createLinearGradient(0,A-e/2,0,A+e/2),M=(L,$)=>`rgba(${J(L[0]*d)},${J(L[1]*d)},${J(L[2]*d)},${$})`;S.addColorStop(0,M(G,0)),S.addColorStop(.16,M(ut,1)),S.addColorStop(.5,M(B,1)),S.addColorStop(.84,M(ut,1)),S.addColorStop(1,M(G,0)),C.fillStyle=S,C.beginPath();const n=e/2;C.moveTo(T-t/2+n,A-n),C.lineTo(T+t/2-n,A-n),C.arc(T+t/2-n,A,n,-Math.PI/2,Math.PI/2),C.lineTo(T-t/2+n,A+n),C.arc(T-t/2+n,A,n,Math.PI/2,-Math.PI/2),C.closePath(),C.fill(),C.globalCompositeOperation="destination-in";const a=C.createLinearGradient(T-t/2,0,T+t/2,0);a.addColorStop(0,"rgba(0,0,0,0)"),a.addColorStop(.22,"rgba(0,0,0,1)"),a.addColorStop(.78,"rgba(0,0,0,1)"),a.addColorStop(1,"rgba(0,0,0,0)"),C.fillStyle=a,C.fillRect(0,0,ht,mt),at.push(b)}zt=it,_t=B.join(",")}function de(){at.length&&zt===it&&_t===B.join(",")||Ut()}function he(){if(!Ct)return;const t=Ct;t.clearRect(0,0,u,g),t.fillStyle="#fff",t.textAlign="left",t.textBaseline="middle",t.font=`${c} ${rt}px ${l}`,t.fillText(r,Ft,g*.5);const e=t.getImageData(0,0,u,g).data,i=new Uint8Array(u*g);for(let x=0;x<g;x++)for(let p=0;p<u;p++){const v=x*u+p,_=e[v*4+3]/255,q=ne(p*.5,x*.5)*.22+ne(p*1.8,x*1.8)*.1;i[v]=_>.5+(q-.16)?1:0}Rt=i;const d=Re(i,u,g),b=new Float32Array(u*g);for(let x=1;x<g-1;x++)for(let p=1;p<u-1;p++){const v=x*u+p;b[v]=(d[v]*4+d[v-1]+d[v+1]+d[v-u]+d[v+u])/8}const C=new Float32Array(u*g),T=new Float32Array(u*g),A=new Float32Array(u*g),S=new Float32Array(u*g);for(let x=1;x<g-1;x++)for(let p=1;p<u-1;p++){const v=x*u+p;if(!i[v])continue;const _=(b[v+1]-b[v-1])*.5,q=(b[v+u]-b[v-u])*.5;A[v]=_,S[v]=q,C[v]=_*_-q*q,T[v]=2*_*q}const M=Math.max(7,Math.round(g*.05)),n=re(C,u,g,M,2),a=re(T,u,g,M,2);wt=new Float32Array(u*g),Mt=new Float32Array(u*g);for(let x=0;x<u*g;x++){if(!i[x])continue;const p=.5*Math.atan2(a[x],n[x]);wt[x]=Math.cos(p),Mt[x]=Math.sin(p)}const L=Math.max(9,g*.04),$=new Float32Array(u*g),X=-.45,V=-.62,D=.64;for(let x=1;x<g-1;x++)for(let p=1;p<u-1;p++){const v=x*u+p;if(!i[v])continue;let _=A[v],q=S[v];const gt=Math.hypot(_,q)||1;_/=gt,q/=gt;const Wt=b[v],te=(1-Math.min(Wt/L,1))*1.25;let Nt=-_*te,Dt=-q*te;const ee=1.5,jt=Math.hypot(Nt,Dt,ee);Nt/=jt,Dt/=jt;let qt=Nt*X+Dt*V+ee/jt*D;qt<0&&(qt=0);const Ae=Math.min(1,.45+.55*Math.min(Wt/(L*.7),1));$[v]=Math.min(1.12,(.5+.75*qt)*Ae)}Lt=$,Z=document.createElement("canvas"),Z.width=u,Z.height=g;const j=Z.getContext("2d");if(j){const x=j.createImageData(u,g);for(let p=0;p<u*g;p++)i[p]&&(x.data[p*4]=255,x.data[p*4+1]=255,x.data[p*4+2]=255,x.data[p*4+3]=255);j.putImageData(x,0,0)}}function me(){const t=Math.max(1.6,it*.6);et=t;const e=[];for(let i=0;i<g;i+=t)for(let d=0;d<u;d+=t){const b=(ot(Math.floor(d),Math.floor(i))-.5)*t*.9,C=(ot(Math.floor(i),Math.floor(d))-.5)*t*.9,T=Math.round(d+b),A=Math.round(i+C);if(T<1||A<1||T>=u-1||A>=g-1)continue;const S=A*u+T;if(!Rt[S])continue;const M=Math.atan2(Mt[S],wt[S]),n=.86+ot(T*7,A*13)*.18;let a=Lt[S]*n;a=a<0?0:a>1.12?1.12:a,e.push({x:T,y:A,ang:M,idx:Math.min(19,Math.max(0,Math.round(a/1.12*19)))})}ct=e}function Kt(t,e){t.save(),t.translate(e.x,e.y),t.rotate(e.ang),t.drawImage(at[e.idx],-ht/2,-mt/2),t.restore()}function $t(){const t=N.getContext("2d");if(t)for(const e of ct)Kt(t,e)}function kt(){const t=N.getContext("2d");t&&t.clearRect(0,0,u,g)}function xe(t){if(!t.length)return[];const e=Math.max(4,et*1.5),i=new Map,d=(M,n)=>M+","+n;t.forEach((M,n)=>{const a=d(Math.floor(M.x/e),Math.floor(M.y/e));let L=i.get(a);L||i.set(a,L=[]),L.push(n)});const b=et*1.8*(et*1.8);function C(M){const n=t[M],a=Math.floor(n.x/e),L=Math.floor(n.y/e),$=[];for(let X=-1;X<=1;X++)for(let V=-1;V<=1;V++){const D=i.get(d(a+X,L+V));if(D)for(const j of D){if(j===M)continue;const x=t[j].x-n.x,p=t[j].y-n.y;x*x+p*p<=b&&$.push(j)}}return $}const T=new Uint8Array(t.length),A=[],S=t.map((M,n)=>n).sort((M,n)=>t[M].x+t[M].y-(t[n].x+t[n].y));for(const M of S){if(T[M])continue;let n=[M];for(T[M]=1;n.length;){A.push(n.map(L=>t[L]));const a=[];for(const L of n)for(const $ of C(L))T[$]||(T[$]=1,a.push($));n=a}}return A}function ye(t){if(!t.length)return[];const e=Math.max(4,et*1.5),i=new Map,d=(n,a)=>n+","+a;t.forEach((n,a)=>{const L=d(Math.floor(n.x/e),Math.floor(n.y/e));let $=i.get(L);$||i.set(L,$=[]),$.push(a)});const b=et*1.8*(et*1.8),C=n=>{const a=t[n],L=d(Math.floor(a.x/e),Math.floor(a.y/e)),$=i.get(L);if(!$)return;const X=$.indexOf(n);X>=0&&$.splice(X,1),$.length||i.delete(L)};function T(n){const a=t[n],L=Math.floor(a.x/e),$=Math.floor(a.y/e);let X=-1,V=1/0;for(let D=-1;D<=1;D++)for(let j=-1;j<=1;j++){const x=i.get(d(L+D,$+j));if(x)for(const p of x){const v=t[p].x-a.x,_=t[p].y-a.y,q=v*v+_*_;q<=b&&q<V&&(V=q,X=p)}}return X}function A(n){const a=t[n],L=Math.floor(a.x/e),$=Math.floor(a.y/e);let X=-1,V=1/0;for(let D=1;D<512;D++){for(let j=-D;j<=D;j++)for(let x=-D;x<=D;x++){if(Math.max(Math.abs(j),Math.abs(x))!==D)continue;const p=i.get(d(L+j,$+x));if(p)for(const v of p){const _=t[v].x-a.x,q=t[v].y-a.y,gt=_*_+q*q;gt<V&&(V=gt,X=v)}}if(X>=0&&D>Math.ceil(Math.sqrt(V)/e)+1)break}return X}let S=0;for(let n=1;n<t.length;n++)t[n].x+t[n].y<t[S].x+t[S].y&&(S=n);const M=[t[S]];C(S);for(let n=1;n<t.length;n++){let a=T(S);if(a<0&&(a=A(S)),a<0)break;M.push(t[a]),C(a),S=a}return M}function ge(){if(kt(),pt||!P){$t(),I.on=!1;return}R==="hand"?(I.rows=ye(ct).map(t=>[t]),I.rate=Math.max(h,ct.length/3.2)):(I.rows=xe(ct),I.rate=h),I.idx=0,I.acc=0,I.on=I.rows.length>0}function pe(t){const e=N.getContext("2d");if(e)for(t>I.rows.length&&(t=I.rows.length);I.idx<t;){const i=I.rows[I.idx++];for(const d of i)Kt(e,d)}}function we(t,e){const i=U.getContext("2d");if(!i||(i.clearRect(0,0,u,g),!E||!Z))return;const d=u*.32,b=i.createRadialGradient(t,e,0,t,e,d);b.addColorStop(0,"rgba(255,250,238,0.18)"),b.addColorStop(.5,"rgba(255,250,238,0.055)"),b.addColorStop(1,"rgba(255,250,238,0)"),i.fillStyle=b,i.fillRect(0,0,u,g),i.globalCompositeOperation="destination-in",i.drawImage(Z,0,0),i.globalCompositeOperation="source-over"}function Me(){F||!f||(F=document.createElement("span"),F.className=Et.caret,F.setAttribute("aria-hidden","true"),Object.assign(F.style,{position:"absolute",pointerEvents:"none",borderRadius:"1px",opacity:"0",background:`rgb(${J(B[0])},${J(B[1])},${J(B[2])})`}),k.appendChild(F),Y.push(F))}function Pt(){if(!f||!F)return;const t=N.getContext("2d");if(!t)return;t.font=`${c} ${rt}px ${l}`;const e=t.measureText(r).width,i=(N.clientWidth||u)/u,d=rt*.7;F.style.left=(Ft+e+rt*.05)*i+"px",F.style.top=.5*(N.clientHeight||g)-d*i/2+"px",F.style.height=d*i+"px",F.style.width=Math.max(2,Math.round(rt*.02*i))+"px",F.style.background=`rgb(${J(B[0])},${J(B[1])},${J(B[2])})`}function st(){K||xt||(xt=!0,yt=0,It=requestAnimationFrame(Vt))}function Vt(t){if(K){xt=!1;return}yt||(yt=t);const e=Math.min(.25,(t-yt)/1e3);yt=t,I.on&&(I.acc+=I.rate*e,pe(I.acc),I.idx>=I.rows.length&&(I.on=!1)),nt&&Z&&(we(H.set?H.x:u*.5,H.set?H.y:g*.45),nt=!1);const i=f&&tt;if(F&&(F.style.opacity=i&&Math.floor(t/530)%2===0?"1":"0"),!I.on&&!nt&&!i){xt=!1;return}It=requestAnimationFrame(Vt)}function vt(t){const e=k.getBoundingClientRect();H.x=(t.clientX-e.left)/(e.width||1)*u,H.y=(t.clientY-e.top)/(e.height||1)*g,H.set=!0,nt=!0,st()}function Tt(){H.set=!1,nt=!0,st()}function Ce(){w&&Ht(w.value,!0)}function ve(t){t.key==="Enter"&&(t.preventDefault(),Bt.replay())}function Te(){tt=!0,st()}function be(){tt=!1,F&&(F.style.opacity="0")}function Yt(t){t===f&&w||(f=t,t?(w||(w=document.createElement("input"),w.type="text",w.value=r,w.maxLength=64,w.setAttribute("aria-label","Embroidered text — type to change it"),w.setAttribute("autocomplete","off"),w.setAttribute("autocapitalize","off"),w.spellcheck=!1,Object.assign(w.style,{position:"absolute",inset:"0",width:"100%",height:"100%",margin:"0",padding:"0",border:"0",outline:"none",background:"transparent",color:"transparent",caretColor:"transparent",font:"inherit",textAlign:"center",cursor:"text"}),w.addEventListener("input",Ce),w.addEventListener("keydown",ve),w.addEventListener("focus",Te),w.addEventListener("blur",be),k.appendChild(w),Y.push(w)),Me(),Pt()):(w&&(w.remove(),w=null),tt=!1,F&&(F.style.opacity="0")))}function Se(t){if(t!==E)if(E=t,t)k.addEventListener("pointermove",vt,{passive:!0}),k.addEventListener("pointerleave",Tt,{passive:!0}),H.set=!1,nt=!0,st();else{k.removeEventListener("pointermove",vt),k.removeEventListener("pointerleave",Tt);const e=U.getContext("2d");e&&e.clearRect(0,0,u,g)}}function ft(t){fe(),ue(),de(),he(),me(),t?ge():(kt(),$t(),I.on=!1),w&&w.value!==r&&(w.value=r),Pt(),nt=!0,st()}function Ht(t,e){t=t??"",!(t===r&&W)&&(r=t,W&&(ft(!1),e&&O&&O(r)))}const bt=new Set;function Jt(t,e,i){const d=`${e} 200px ${t}`;let b=!1;try{b=!!document.fonts&&document.fonts.check(d)}catch{b=!0}if(!document.fonts||b){i();return}let C=!1,T;const A=()=>{C||(C=!0,clearTimeout(T),bt.delete(T),K||i())};document.fonts.load(d,r).then(()=>document.fonts.ready).then(A,A),T=setTimeout(A,1500),bt.add(T)}function Ee(){K||(ft(!0),W=!0)}f&&(f=!1,Yt(!0)),E&&(k.addEventListener("pointermove",vt,{passive:!0}),k.addEventListener("pointerleave",Tt,{passive:!0})),Jt(z,c,Ee);let Ot,Qt=-1;function Zt(){clearTimeout(Ot),Ot=setTimeout(()=>{if(K||!W)return;const t=Math.round(k.getBoundingClientRect().width);t!==Qt&&(Qt=t,ft(!1))},200)}window.addEventListener("resize",Zt);const Bt={get text(){return r},setText(t){if(!K){if(!W){r=t??"",w&&(w.value=r);return}Ht(t,!1)}},replay(){K||!W||ft(!0)},resize(){K||!W||ft(!1)},update(t){if(K)return;t.text!==void 0&&Bt.setText(t.text);let e=!1,i=!1;if(t.font!==void 0&&t.font!==l&&(l=t.font,z=ae(l),e=!0),t.weight!==void 0){const d=St(t.weight,1,1e3);d!==c&&(c=d,e=!0)}if(t.fill!==void 0){const d=St(t.fill,.05,1);d!==s&&(s=d,e=!0)}t.pitch!==void 0&&t.pitch!==m&&(m=t.pitch,e=!0),t.threadColor!==void 0&&(dt(t.threadColor),i=!0),t.sewRate!==void 0&&(h=Math.max(1,t.sewRate)),t.sewStyle!==void 0&&(R=t.sewStyle==="hand"?"hand":"machine"),t.animate!==void 0&&(P=t.animate),t.onTextChange!==void 0&&(O=t.onTextChange),t.sheen!==void 0&&Se(t.sheen),t.editable!==void 0&&Yt(t.editable),W&&(e?Jt(z,c,()=>{K||ft(!1)}):i&&(Ut(),kt(),$t(),Pt(),st()))},focus(){!K&&f&&(w==null||w.focus())},destroy(){if(!K){K=!0,xt=!1,cancelAnimationFrame(It),clearTimeout(Ot);for(const t of bt)clearTimeout(t);bt.clear(),window.removeEventListener("resize",Zt),k.removeEventListener("pointermove",vt),k.removeEventListener("pointerleave",Tt);for(const t of Y)t.remove();at=[],ct=[],I.rows=[],Rt=new Uint8Array(0),Lt=new Float32Array(0),wt=new Float32Array(0),Mt=new Float32Array(0),Z=lt=null,Ct=null,F=null,w=null}}};return Bt}function ce(y){const o=Q.useRef(null),r=Q.useRef(null),l=Q.useRef(y);l.current=y;const c=Q.useRef(y.onTextChange);c.current=y.onTextChange;const{text:s,font:h,weight:R,threadColor:E,pitch:P,fill:f,animate:m,sewStyle:O,sewRate:z,sheen:pt,editable:B,reducedMotion:ut}=y;return Q.useLayoutEffect(()=>{const G=o.current;if(!G)return;const dt=le(G,{...l.current,onTextChange:w=>{var F;return(F=c.current)==null?void 0:F.call(c,w)}});r.current=dt;let Y=0,N=0,k=!0,U;return typeof ResizeObserver<"u"&&(U=new ResizeObserver(w=>{const F=Math.round(w[0].contentRect.width);if(k){k=!1,N=F;return}F!==N&&(N=F,cancelAnimationFrame(Y),Y=requestAnimationFrame(()=>{var tt;return(tt=r.current)==null?void 0:tt.resize()}))}),U.observe(G)),()=>{U==null||U.disconnect(),cancelAnimationFrame(Y),dt.destroy(),r.current=null}},[ut]),Q.useEffect(()=>{var G;(G=r.current)==null||G.update({font:h,weight:R,threadColor:E,pitch:P,fill:f,animate:m,sewStyle:O,sewRate:z,sheen:pt,editable:B})},[h,R,E,P,f,m,O,z,pt,B]),Q.useEffect(()=>{var G;(G=r.current)==null||G.setText(s)},[s]),o}const se=Q.forwardRef(function({className:o,style:r,as:l="div","aria-label":c,role:s,"aria-describedby":h,...R},E){const P=ce(R),f=Q.useCallback(m=>{P.current=m,typeof E=="function"?E(m):E&&(E.current=m)},[P,E]);return Fe.jsx(l,{ref:f,className:o,style:r,role:R.editable?s:s??"img","aria-label":R.editable?c:c??R.text,"aria-describedby":h})});se.displayName="ThreadText";exports.THREAD_TEXT_CLASSES=Et;exports.ThreadText=se;exports.createThreadText=le;exports.useThreadText=ce;
package/dist/index.d.ts CHANGED
@@ -77,7 +77,15 @@ export declare interface ThreadTextOptions {
77
77
  fill?: number;
78
78
  /** Play the sew-in animation on mount and on `replay()`. (default: true) */
79
79
  animate?: boolean;
80
- /** Satin cross-rows laid per second during the sew-in. (default: 110) */
80
+ /**
81
+ * How the word is sewn in:
82
+ * - **'machine'** (default) — satin cross-rows appear in parallel across each stroke, like
83
+ * machine embroidery (a BFS over the stitch graph).
84
+ * - **'hand'** — a single continuous thread wanders stitch by stitch along each stroke and
85
+ * carries to the next, laying one stitch at a time, like hand embroidery.
86
+ */
87
+ sewStyle?: 'machine' | 'hand';
88
+ /** Satin cross-rows (machine) or stitches (hand) laid per second during the sew-in. (default: 110) */
81
89
  sewRate?: number;
82
90
  /** Enable the cursor-following radial sheen on the overlay canvas. (default: true) */
83
91
  sheen?: boolean;