moodi 0.1.1 → 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 +21 -1
- package/dist/index.cjs +7 -4
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +7 -4
- package/dist/react.cjs +1 -1
- package/dist/react.d.cts +21 -6
- package/dist/react.d.ts +21 -6
- package/dist/react.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ textures, nothing to download besides three.js.
|
|
|
9
9
|
|
|
10
10
|

|
|
11
11
|
|
|
12
|
-
**Playground:** <https://
|
|
12
|
+
**Playground:** <https://www.getmoodi.dev> · by [@itsk0rn3l](https://x.com/itsk0rn3l)
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -39,6 +39,19 @@ import { Moodi } from 'moodi/react';
|
|
|
39
39
|
`moodi/react` is SSR-safe (Next.js, Remix, …): three.js is loaded lazily in the
|
|
40
40
|
browser, so nothing WebGL-related runs on the server.
|
|
41
41
|
|
|
42
|
+
React-only props:
|
|
43
|
+
|
|
44
|
+
| Prop | Type | Default | Notes |
|
|
45
|
+
| -------------- | -------------------------------------- | -------- | --------------------------------------------------------------------- |
|
|
46
|
+
| `poster` | `string` | — | Image shown until the first 3D frame (e.g. a still of the same buddy) |
|
|
47
|
+
| `loading` | `'idle' \| 'interaction' \| 'eager'` | `'idle'` | When to load three.js; `'interaction'` waits for the first pointer, touch, key or scroll |
|
|
48
|
+
| `onMoodChange` | `(detail) => void` | — | Same as the `moodchange` event |
|
|
49
|
+
| `onReady` | `() => void` | — | First 3D frame is on screen |
|
|
50
|
+
|
|
51
|
+
`poster` + `loading="interaction"` keeps three.js out of the critical path entirely: the
|
|
52
|
+
page paints the still instantly and the live buddy takes over on the first interaction.
|
|
53
|
+
With the Web Component, put an `<img>` inside `<moodi-buddy>`; it shows until the first frame.
|
|
54
|
+
|
|
42
55
|
### CDN, no build step
|
|
43
56
|
|
|
44
57
|
```html
|
|
@@ -60,6 +73,7 @@ browser, so nothing WebGL-related runs on the server.
|
|
|
60
73
|
| `followCursor` (`follow-cursor`) | `boolean` | `true` | Eyes (and slightly the body) follow the pointer |
|
|
61
74
|
| `interactive` | `boolean` | `true` | Hover lift, click reaction |
|
|
62
75
|
| `shadow` | `boolean` | `true` | Soft ground shadow |
|
|
76
|
+
| `sound` | `boolean` | `false` | Short synthesized effect per mood (and when it appears) |
|
|
63
77
|
|
|
64
78
|
Boolean attributes on the Web Component take `"true"` / `"false"`.
|
|
65
79
|
|
|
@@ -74,6 +88,7 @@ const el = document.querySelector('moodi-buddy');
|
|
|
74
88
|
el.mood = 'thinking';
|
|
75
89
|
el.react('happy', { duration: 1200 }); // temporary reaction, then back to the current mood
|
|
76
90
|
el.addEventListener('moodchange', (e) => console.log(e.detail)); // { mood, previous, reaction }
|
|
91
|
+
el.addEventListener('ready', () => {}); // first frame rendered; the element also gets a [ready] attribute
|
|
77
92
|
```
|
|
78
93
|
|
|
79
94
|
In React, pass a `ref` (it points at the element) or use `onMoodChange`.
|
|
@@ -85,6 +100,11 @@ Also exported: `SHAPES`, `MOODS`, `PALETTES`, `MOOD_INFO`, `DEFAULTS`, `defineMo
|
|
|
85
100
|
- All buddies on a page share **one WebGL context**; dozens are fine.
|
|
86
101
|
- Rendering stops while a buddy is off-screen or the tab is hidden; paused buddies (`speed={0}`) only redraw when something changes.
|
|
87
102
|
- `prefers-reduced-motion`: only blinking and soft fades.
|
|
103
|
+
- Software WebGL (SwiftShader, llvmpipe): no continuous animation, frames only on change, 1× pixels.
|
|
104
|
+
- Changing `size` gives a little squash-and-stretch.
|
|
105
|
+
- Sound is opt-in (`sound` / `sound="true"`): one tiny Web Audio effect per mood, no audio files.
|
|
106
|
+
Browsers only allow audio after a click, tap or key press; a sound requested earlier waits a
|
|
107
|
+
few seconds for that first gesture.
|
|
88
108
|
- Transparent background, pixel ratio capped at 2.
|
|
89
109
|
- `role="img"` and `aria-label="moodi: <mood>"` (set your own `aria-label` to override).
|
|
90
110
|
- ~11 kB gzip, three.js excluded.
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
'use strict';var three=require('three');var Ne=["ghost","pebble","blob","drop","bean","cube"],Xe=["idle","thinking","loading","listening","speaking","happy","surprised","sad","error","sleeping","love"],M={shape:"ghost",mood:"idle",color:"#6c5ce7",size:160,speed:1,followCursor:true,interactive:true,shadow:true},wt={Iris:"#6c5ce7",Peach:"#f2a07b",Sage:"#8fb996",Sky:"#8ec5e8",Lilac:"#b9a3e3",Butter:"#f3d36b",Coral:"#f07167",Ink:"#2b2d42",Cloud:"#ecebe8"},St={idle:{label:"Idle",description:"Breathing softly. Blinks now and then."},thinking:{label:"Thinking",description:"Eyes drift up. Little thoughts pop."},loading:{label:"Loading",description:"Half-lidded, wobbling patiently."},listening:{label:"Listening",description:"All eyes. Nods along to every word."},speaking:{label:"Speaking",description:"Chatty mouth, bouncing to the rhythm."},happy:{label:"Happy",description:"Bounces with joy. Cheeks glow."},surprised:{label:"Surprised",description:"Wide eyes, tiny o. Did not see that coming."},sad:{label:"Sad",description:"Slumps a little. Needs a minute."},error:{label:"Error",description:"Something broke. Shakes it off."},sleeping:{label:"Sleeping",description:"Deep, slow breaths. Do not disturb."},love:{label:"Love",description:"Heart eyes, floating on air."}},Ct={pebble:"Pebble",blob:"Blob",drop:"Drop",bean:"Bean",cube:"Cube",ghost:"Ghost"},re=t=>Ne.includes(t),ae=t=>Xe.includes(t);var z=null;function _(t,o){if(!t)return o;let e=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(t.trim());if(e)return Se(e[1]);if(z||(z=document.createElement("canvas").getContext("2d")),!z)return o;z.fillStyle="#010203",z.fillStyle=t;let s=String(z.fillStyle);if(s==="#010203"&&t.trim()!=="#010203")return o;if(s.startsWith("#"))return Se(s.slice(1));z.canvas.width=z.canvas.height=1,z.clearRect(0,0,1,1),z.fillRect(0,0,1,1);let i=z.getImageData(0,0,1,1).data;return [i[0]/255,i[1]/255,i[2]/255]}function Se(t){t.length===3&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]);let o=parseInt(t,16);return [(o>>16&255)/255,(o>>8&255)/255,(o&255)/255]}var O=t=>`rgb(${Math.round(t[0]*255)},${Math.round(t[1]*255)},${Math.round(t[2]*255)})`,P=(t,o,e)=>[t[0]+(o[0]-t[0])*e,t[1]+(o[1]-t[1])*e,t[2]+(o[2]-t[2])*e],ne=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4),he=t=>.2126*ne(t[0])+.7152*ne(t[1])+.0722*ne(t[2]),Ve=[.07,.06,.08],He=[1,.97,.93],$e=[1,.42,.48],U=t=>he(t)>.06?P(Ve,t,.06):He,J=t=>P(t,$e,.78);var $=["eyeOpen","eyeSize","lookX","lookY","lidCover","lidTilt","happyEye","closedEye","xEye","heartEye","mouthW","mouthCurve","mouthOpen","mouthRound","mouthSkew","mouthWave","blush"],L={eyeOpen:1,eyeSize:1,lookX:0,lookY:0,lidCover:0,lidTilt:0,happyEye:0,closedEye:0,xEye:0,heartEye:0,mouthW:.1,mouthCurve:.35,mouthOpen:0,mouthRound:0,mouthSkew:0,mouthWave:0,blush:.45},Z={idle:{...L},thinking:{...L,eyeOpen:.92,lookX:.7,lookY:-0.8,lidCover:.12,mouthW:.065,mouthCurve:0,mouthSkew:.8,blush:.3},loading:{...L,lidCover:.48,mouthW:.06,mouthCurve:.12,blush:.3},listening:{...L,eyeOpen:1.12,eyeSize:1.16,mouthW:.06,mouthCurve:.2,mouthOpen:.02},speaking:{...L,mouthW:.09,mouthCurve:.25},happy:{...L,happyEye:1,mouthW:.13,mouthCurve:1,mouthOpen:.08,blush:1},surprised:{...L,eyeOpen:1.25,eyeSize:1.22,mouthW:.06,mouthCurve:0,mouthOpen:.12,mouthRound:1,blush:.35},sad:{...L,eyeOpen:.95,lidCover:.32,lidTilt:.5,lookY:.45,mouthW:.08,mouthCurve:-0.75,blush:.2},error:{...L,xEye:1,mouthW:.1,mouthCurve:-0.2,mouthWave:1,blush:.15},sleeping:{...L,closedEye:1,mouthW:.04,mouthCurve:0,mouthOpen:.035,mouthRound:.7,blush:.55},love:{...L,heartEye:1,mouthW:.11,mouthCurve:.9,mouthOpen:.03,blush:1}},Ce={idle:1,thinking:.15,loading:0,listening:1,speaking:.9,happy:.6,surprised:.8,sad:.3,error:.3,sleeping:0,love:.7},de=t=>({...t}),je=.31,Ke=-0.04,Ue=.098,le=.128,Je=.2,ce=.05;function Te(t,o,e,s,i,a,l,d,u,p){let r=t.canvas.width;t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,r,r),t.setTransform(r/2,0,0,r/2,r/2,r/2),t.lineCap="round",t.lineJoin="round";let h=o.lookX*.075,c=o.lookY*.06;if(o.blush>.01){let m=r*.018;t.save(),"filter"in t&&(t.filter=`blur(${m}px)`);for(let y of [-1,1]){let v=y*.52+h*.4,n=.16+c*.4,f=t.createRadialGradient(v,n,0,v,n,.16);f.addColorStop(0,i),f.addColorStop(1,"rgba(0,0,0,0)"),t.globalAlpha=Math.min(1,o.blush)*.62,t.fillStyle=f,t.beginPath(),t.ellipse(v,n,.17,.12,0,0,Math.PI*2),t.fill();}t.restore();let b=Math.min(1,Math.max(0,(o.blush-.6)/.4));if(b>.01){t.save(),t.globalAlpha=b*.55,t.strokeStyle=i,t.lineWidth=.016,t.lineCap="round";for(let y of [-1,1]){let v=y*.52+h*.4,n=.16+c*.4;for(let f=-1;f<=1;f++){let F=v+f*.045;t.beginPath(),t.moveTo(F+.018,n-.035),t.lineTo(F-.018,n+.035),t.stroke();}}t.restore();}}t.fillStyle=s,t.strokeStyle=s,t.lineWidth=ce;let A=1-Math.min(1,o.happyEye+o.closedEye+o.xEye+o.heartEye);for(let m of [-1,1]){let b=m*je+h,y=Ke+c,v=Ue*o.eyeSize;if(A>.01){t.globalAlpha=A;let n=Math.max(.06,o.eyeOpen*e),f=le*o.eyeSize*n;if(t.save(),o.lidCover>.01||o.lidTilt>.01){let W=y-le*o.eyeSize+2*le*o.eyeSize*o.lidCover;t.translate(b,W),t.rotate(m*o.lidTilt*.6),t.beginPath(),t.rect(-1,0,2,2),t.clip(),t.rotate(-m*o.lidTilt*.6),t.translate(-b,-W);}t.shadowColor=s,t.shadowBlur=r*.035*a;let F=t.createRadialGradient(b,y-f*.2,0,b,y,Math.max(v,f));if(F.addColorStop(0,l),F.addColorStop(1,s),t.fillStyle=F,t.beginPath(),t.ellipse(b,y,v,f,0,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.fillStyle=s,n>.35){let x=Math.max(-1,Math.min(1,o.lookX)),W=Math.max(-1,Math.min(1,o.lookY)),ie=d==="#fff"?.3:.34;t.globalAlpha=A*Math.min(1,(n-.35)*3),t.fillStyle=d,t.beginPath(),t.arc(b-v*.1+x*v*.5,y-f*.16+W*f*.42,v*ie,0,Math.PI*2),t.fill(),t.fillStyle=s;}t.restore();}if(t.shadowColor=s,t.shadowBlur=r*.03*a,o.happyEye>.01){let n=.6+.4*o.happyEye;t.globalAlpha=Math.min(1,o.happyEye),t.beginPath(),t.arc(b,y+.045,v*1.15*n,Math.PI*1.12,Math.PI*1.88),t.stroke();}if(o.closedEye>.01){let n=.6+.4*o.closedEye;t.globalAlpha=Math.min(1,o.closedEye),t.beginPath(),t.moveTo(b-v*1.15*n,y+.01),t.quadraticCurveTo(b,y+.07*n,b+v*1.15*n,y+.01),t.stroke();}if(o.xEye>.01){let n=v*.95*(.6+.4*o.xEye);t.globalAlpha=Math.min(1,o.xEye),t.beginPath(),t.moveTo(b-n,y-n),t.lineTo(b+n,y+n),t.moveTo(b+n,y-n),t.lineTo(b-n,y+n),t.stroke();}o.heartEye>.01&&(t.globalAlpha=Math.min(1,o.heartEye),ue(t,b,y,v*1.35*(.5+.5*o.heartEye)),t.fill());}t.shadowBlur=0;let k=h*.6+o.mouthSkew*.07,w=Je+c*.6,T=Math.min(1,Math.max(0,o.mouthRound)),S=o.mouthW,R=t.createLinearGradient(k-S-.02,w,k+S+.02,w);R.addColorStop(0,p),R.addColorStop(.5,l),R.addColorStop(1,p),t.shadowColor=s,t.shadowBlur=r*.02*a;let B=(m,b,y,v)=>{t.fillStyle=u,t.fill(m),t.shadowBlur=0,b>.02&&(t.save(),t.clip(m),t.fillStyle=i,t.globalAlpha*=Math.min(1,(b-.02)*12),t.beginPath(),t.ellipse(k,y,v,b*.55,0,0,Math.PI*2),t.fill(),t.restore()),t.strokeStyle=R;let n=t.lineWidth;t.lineWidth=n*.6,t.stroke(m),t.lineWidth=n,t.shadowBlur=r*.02*a;};if(T<.99){t.globalAlpha=1-T;let m=18,b=f=>o.mouthCurve*.075*(1-f*f),y=(f,F)=>{let x=f/m*2-1,W=k+x*S,ie=w+b(x)+o.mouthSkew*x*.03+o.mouthWave*.018*Math.sin((x+1)*Math.PI*2)+F*(1-x*x);return [W,ie]},v=Math.max(0,o.mouthOpen),n=new Path2D;for(let f=0;f<=m;f++){let[F,x]=y(f,-v*.15);f?n.lineTo(F,x):n.moveTo(F,x);}if(v>.003){for(let f=m;f>=0;f--){let[F,x]=y(f,v);n.lineTo(F,x);}n.closePath(),B(n,v,y(m/2,v)[1],S*.55);}else t.strokeStyle=R,t.stroke(n);}if(T>.01){t.globalAlpha=T;let m=Math.max(.02,o.mouthOpen*.55+.01),b=new Path2D;b.ellipse(k,w+.01,m*.85,m,0,0,Math.PI*2),t.lineWidth=ce*.8,B(b,o.mouthOpen,w+.01+m*.8,m*.6),t.lineWidth=ce;}t.shadowBlur=0,t.globalAlpha=1;}function ue(t,o,e,s){t.beginPath(),t.moveTo(o,e+s*.85),t.bezierCurveTo(o-s*1.25,e+s*.05,o-s*.75,e-s*.95,o,e-s*.35),t.bezierCurveTo(o+s*.75,e-s*.95,o+s*1.25,e+s*.05,o,e+s*.85),t.closePath();}var te=()=>({y:0,rx:0,ry:0,rz:0,st:0,mouth:0,lookX:0,lookY:0}),{sin:g,cos:Ze,abs:Qe,max:Q,exp:et,PI:ee}=Math;function pe(t,o,e,s){switch(s.y=s.rx=s.ry=s.rz=s.st=s.mouth=s.lookX=s.lookY=0,t){case "idle":{let i=g(o*1.6);s.y=.015*i,s.st=.018*i;break}case "thinking":{s.rz=.07*g(o*1.1),s.y=.02*g(o*2.2),s.ry=.15,s.rx=-0.06;break}case "loading":{let i=o*2.4;s.rx=.12*g(i),s.rz=.12*Ze(i),s.ry=.22*g(i),s.y=.02*g(i*2),s.lookX=.55*g(i);break}case "listening":{let i=Q(0,g(o*3.2));s.rx=.09*i*i;let a=Qe(g(o*7.1)*.5+g(o*11.3)*.3+g(o*3.7)*.2);s.st=.03*a;break}case "speaking":{let i=Q(0,g(o*9)*.6+g(o*14.3)*.4)*(.65+.35*g(o*1.7));s.mouth=.11*i,s.st=.04*i,s.rz=.03*g(o*2.1);break}case "happy":{let i=o*1.35%1;s.y=.2*g(ee*i);let a=Q(0,1-i/.14)+Q(0,(i-.9)/.1);s.st=.08*g(ee*i)-.14*a,s.rz=.05*g(o*1.35*ee);break}case "surprised":{if(e<.45){let i=g(ee*e/.45);s.y=.16*i,s.st=.12*i;}s.rz=.01*g(o*38)*et(-e*2),s.rx=-0.06;break}case "sad":{let i=g(o*1.1);s.y=-0.06,s.rx=.2,s.st=-0.04+.015*i,s.rz=-0.04;break}case "error":{let i=e%2.8;if(i<.55){let a=1-i/.55;s.ry=.26*g(i*42)*a,s.rz=.05*g(i*42+1)*a;}s.st=-0.02;break}case "sleeping":{let i=g(o*1);s.st=.04*i,s.y=-0.03,s.rz=.1,s.rx=.1;break}case "love":{s.y=.07+.06*g(o*1.6),s.rz=.05*g(o*.8),s.st=.015*g(o*1.6+1);break}}return s}function Ae(t,o,e,s){for(let i of Object.keys(s))s[i]=t[i]+(o[i]-t[i])*e;return s}var Ee={thinking:"dots",sleeping:"zz",love:"hearts",listening:"waves",surprised:"bang"};var X=new Set,E=null,q=0,Y=0,oe=0,be,Pe=false,N="idle",I={x:0,y:0,lastMove:-1e9,known:false},G={reducedMotion:false};function ot(){if(Pe)return;Pe=true,addEventListener("pointermove",o=>{I.x=o.clientX,I.y=o.clientY,I.lastMove=performance.now(),I.known=true;},{passive:true}),document.addEventListener("visibilitychange",D);let t=matchMedia("(prefers-reduced-motion: reduce)");G.reducedMotion=t.matches,t.addEventListener?.("change",o=>G.reducedMotion=o.matches);}function Fe(t,o){return E||(E=new three.WebGLRenderer({antialias:true,alpha:true,powerPreference:"low-power",preserveDrawingBuffer:true}),E.setPixelRatio(1),E.setClearColor(0,0),E.setScissorTest(true),E.domElement.addEventListener("webglcontextlost",e=>{e.preventDefault(),E?.dispose(),E=null,q=Y=0,N="idle";})),(t>q||o>Y)&&(q=Math.max(q,t),Y=Math.max(Y,o),E.setSize(q,Y,false)),E}var Re=()=>Y;function Be(t,o){if(N==="ready")return true;if(N==="idle"&&E){N="compiling";let e=E;e.compileAsync(t,o).catch(()=>{}).then(()=>{e===E&&(N="ready"),D();});}return false}function ze(t){ot(),clearTimeout(be),X.add(t),D();}function Oe(t){X.delete(t),X.size===0&&(clearTimeout(be),be=setTimeout(()=>{X.size||!E||(E.dispose(),E.forceContextLoss(),E=null,q=Y=0,N="idle");},1500));}function D(){if(!oe){for(let t of X)if(t.active){oe=requestAnimationFrame(Le);return}}}var st=10,xe=new WeakMap,me=0;function Le(t){oe=0;let o=[...X].filter(a=>a.active);if(!o.length)return;let e=performance.now(),s=o.length,i=0;for(;i<s;i++){let a=o[(me+i)%s],l=xe.get(a)??t-16;if(xe.set(a,t),a.tick(Math.min(.05,Math.max(0,(t-l)/1e3)),t),performance.now()-e>st){i++;break}}me=(me+i)%s,oe=requestAnimationFrame(Le);}var j={pebble:{faceY:.02,faceScale:1.3,animated:false},blob:{faceY:.02,faceScale:1.28,animated:true},drop:{faceY:-0.24,faceScale:1.22,animated:false},bean:{faceY:.14,faceScale:.92,animated:false},cube:{faceY:0,faceScale:1.22,animated:false},ghost:{faceY:.22,faceScale:1.24,animated:true}};function Ie(t){let o=new three.SphereGeometry(1,t*2,Math.round(t*1.5)),e=o.getAttribute("position"),s=o.getIndex(),i=new Map,a=[],l=new Uint32Array(e.count);for(let r=0;r<e.count;r++){let h=e.getX(r),c=e.getY(r),C=e.getZ(r),A=`${Math.round(h*1e4)},${Math.round(c*1e4)},${Math.round(C*1e4)}`,k=i.get(A);if(k===void 0){k=a.length/3,i.set(A,k);let w=Math.hypot(h,c,C)||1;a.push(h/w,c/w,C/w);}l[r]=k;}let d=[];for(let r=0;r<s.count;r+=3){let h=l[s.getX(r)],c=l[s.getX(r+1)],C=l[s.getX(r+2)];h!==c&&c!==C&&h!==C&&d.push(h,c,C);}o.dispose();let u=new three.BufferGeometry,p=new Float32Array(a);return u.setAttribute("position",new three.BufferAttribute(new Float32Array(p),3)),u.setIndex(d),{geometry:u,dirs:p}}var nt=(t,o,e)=>{let s=Math.min(1,Math.max(0,(e-t)/(o-t)));return s*s*(3-2*s)};function fe(t,o,e,s,i,a,l){let d=o,u=e,p=s;switch(t){case "pebble":{d=o*1.1+.04*e,u=e*.92*(.95+.05*Math.tanh(3*e))+.03*o,p=s*.94;break}case "blob":{let r=.97+.045*Math.sin(2.1*o+1.3*i)*Math.sin(2.4*e+1.7*i+1)+.035*Math.sin(2.7*s+2.3*o+1.1*i);d=o*r,u=e*r,p=s*r;break}case "drop":{let r=Math.max(e,0),h=1-.8*Math.pow(r,1.6);d=o*h*.98+.1*r*r*r,u=(e<0?e*.9:e*1.32)-.12,p=s*h*.98;break}case "bean":{let r=e*.78+.42*Math.tanh(e*3);d=(o*.8+.14*(r*r/1.44)-.07)*.9,u=r*.88,p=s*.8*.9;break}case "cube":{let h=Math.pow(Math.pow(Math.abs(o),4.5)+Math.pow(Math.abs(e),4.5)+Math.pow(Math.abs(s),4.5),-0.2222222222222222);d=o*h*.84,u=e*h*.82,p=s*h*.84;break}case "ghost":{if(e>=0)d=o,u=e,p=s;else {let h=(Math.acos(Math.max(-1,e))-Math.PI/2)/(Math.PI/2),c=Math.hypot(o,s)||1,C=o/c,A=s/c,k=Math.atan2(s,o),w=.085*Math.sin(5*k+2.2*i),T=1.04,S=.13,R=-1.05+w,B;if(h<.45){let m=h/.45;B=1+.04*m,u=-m*(1.05-S)+w*nt(.45,1,m);}else if(h<.75){let m=(h-.45)/.3*(Math.PI/2);B=T-S+S*Math.cos(m),u=R+S-S*Math.sin(m);}else {let m=(h-.75)/.25;B=(T-S)*(1-m),u=R+.22*m*m*(3-2*m);}d=C*B,p=A*B;}d*=.9,u=(u+.1)*.9,p*=.9;break}}a[l]=d,a[l+1]=u,a[l+2]=p;}var ve=-0.85,De=new Map;function gt(t){let o=De.get(t);if(!o){let{geometry:e,dirs:s}=Ie(t);o={dirs:s,index:new Uint32Array(e.getIndex().array)},e.dispose(),De.set(t,o);}return o}var V=(t,o)=>1-Math.exp(-t*o),We=t=>1-Math.pow(1-t,3),se=class{constructor(){this.scene=new three.Scene;this.camera=new three.PerspectiveCamera(30,1,.1,50);this.group=new three.Group;this.v=new three.Vector3;this.geometry=new three.BufferGeometry;this.faceCanvas=document.createElement("canvas");this.uniforms={uFace:{value:null},uFaceY:{value:0},uFaceScale:{value:1.5}};this.state={...M};this.visible=false;this.connected=false;this.w=0;this.h=0;this.t=Math.random()*100;this.shapeFrom=M.shape;this.shapeTo=M.shape;this.morph=1;this.shapeDirty=true;this.shown=M.mood;this.prevShown=M.mood;this.tau=10;this.prevTau=10;this.blend=1;this.reaction=null;this.pose=te();this.poseA=te();this.poseB=te();this.face=de(Z.idle);this.target=de(Z.idle);this.drawn=new Float32Array($.length+7).fill(NaN);this.blink=1;this.blinkT=-1;this.nextBlink=2+Math.random()*3;this.body=_(M.color,[.95,.63,.48]);this.bodyT=this.body;this.faceC=U(this.body);this.faceCT=this.faceC;this.cheek=J(this.body);this.cheekT=this.cheek;this.look={x:0,y:0};this.wander={x:0,y:0,next:0};this.squash=0;this.squashV=0;this.pop=.6;this.popV=0;this.hover=0;this.hovering=false;this.overlay={dots:0,zz:0,hearts:0,waves:0,bang:0};this.needsFrame=true;this.css=[0,0];this.quality=1;this.cost=0;this.costFrames=0;this.lastFrame=[];this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),this.faceCtx=this.faceCanvas.getContext("2d"),this.faceCanvas.width=this.faceCanvas.height=256,this.faceTex=new three.CanvasTexture(this.faceCanvas),this.faceTex.colorSpace=three.SRGBColorSpace,this.faceTex.minFilter=three.LinearMipmapLinearFilter,this.faceTex.anisotropy=4,this.uniforms.uFace.value=this.faceTex,this.setSegments(24),this.material=this.createMaterial(),this.mesh=new three.Mesh(this.geometry,this.material),this.mesh.frustumCulled=false,this.mesh.position.y=-ve,this.group.add(this.mesh),this.scene.add(this.group),this.scene.add(new three.HemisphereLight(16774120,7234140,1.7));let o=new three.DirectionalLight(16777215,1.9);o.position.set(2.5,3.5,4);let e=new three.DirectionalLight(16770770,1.4);e.position.set(-3.5,2,-3);let s=new three.DirectionalLight(16773350,.45);s.position.set(-2,-1,3),this.scene.add(o,e,s),this.camera.position.set(0,.3,6.6),this.camera.lookAt(0,.12,0);}get active(){return this.visible&&this.connected&&this.w>0}createMaterial(){let o=new three.MeshStandardMaterial({roughness:.62,metalness:0});return o.color.setRGB(...this.body,three.SRGBColorSpace),o.customProgramCacheKey=()=>"moodi-1",o.onBeforeCompile=e=>{Object.assign(e.uniforms,this.uniforms),e.vertexShader=e.vertexShader.replace("#include <common>",`#include <common>
|
|
1
|
+
'use strict';var three=require('three');var Je=["ghost","pebble","blob","drop","bean","cube"],Ze=["idle","thinking","loading","listening","speaking","happy","surprised","sad","error","sleeping","love"],M={shape:"ghost",mood:"idle",color:"#6c5ce7",size:160,speed:1,followCursor:true,interactive:true,shadow:true,sound:false},Ot={Iris:"#6c5ce7",Peach:"#f2a07b",Sage:"#8fb996",Sky:"#8ec5e8",Lilac:"#b9a3e3",Butter:"#f3d36b",Coral:"#f07167",Ink:"#2b2d42",Cloud:"#ecebe8"},Bt={idle:{label:"Idle",description:"Breathing softly. Blinks now and then."},thinking:{label:"Thinking",description:"Eyes drift up. Little thoughts pop."},loading:{label:"Loading",description:"Half-lidded, wobbling patiently."},listening:{label:"Listening",description:"All eyes. Nods along to every word."},speaking:{label:"Speaking",description:"Chatty mouth, bouncing to the rhythm."},happy:{label:"Happy",description:"Bounces with joy. Cheeks glow."},surprised:{label:"Surprised",description:"Wide eyes, tiny o. Did not see that coming."},sad:{label:"Sad",description:"Slumps a little. Needs a minute."},error:{label:"Error",description:"Something broke. Shakes it off."},sleeping:{label:"Sleeping",description:"Deep, slow breaths. Do not disturb."},love:{label:"Love",description:"Heart eyes, floating on air."}},Lt={pebble:"Pebble",blob:"Blob",drop:"Drop",bean:"Bean",cube:"Cube",ghost:"Ghost"},ce=e=>Je.includes(e),de=e=>Ze.includes(e);var L=null;function X(e,o){if(!e)return o;let t=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(e.trim());if(t)return xe(t[1]);if(L||(L=document.createElement("canvas").getContext("2d")),!L)return o;L.fillStyle="#010203",L.fillStyle=e;let s=String(L.fillStyle);if(s==="#010203"&&e.trim()!=="#010203")return o;if(s.startsWith("#"))return xe(s.slice(1));L.canvas.width=L.canvas.height=1,L.clearRect(0,0,1,1),L.fillRect(0,0,1,1);let i=L.getImageData(0,0,1,1).data;return [i[0]/255,i[1]/255,i[2]/255]}function xe(e){e.length===3&&(e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);let o=parseInt(e,16);return [(o>>16&255)/255,(o>>8&255)/255,(o&255)/255]}var G=e=>`rgb(${Math.round(e[0]*255)},${Math.round(e[1]*255)},${Math.round(e[2]*255)})`,x=(e,o,t)=>[e[0]+(o[0]-e[0])*t,e[1]+(o[1]-e[1])*t,e[2]+(o[2]-e[2])*t],ue=e=>e<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4),pe=e=>.2126*ue(e[0])+.7152*ue(e[1])+.0722*ue(e[2]),Qe=[.07,.06,.08],et=[1,.97,.93],tt=[1,.42,.48],Q=e=>pe(e)>.06?x(Qe,e,.06):et,ee=e=>x(e,tt,.78);var U=["eyeOpen","eyeSize","lookX","lookY","lidCover","lidTilt","happyEye","closedEye","xEye","heartEye","mouthW","mouthCurve","mouthOpen","mouthRound","mouthSkew","mouthWave","blush"],q={eyeOpen:1,eyeSize:1,lookX:0,lookY:0,lidCover:0,lidTilt:0,happyEye:0,closedEye:0,xEye:0,heartEye:0,mouthW:.1,mouthCurve:.35,mouthOpen:0,mouthRound:0,mouthSkew:0,mouthWave:0,blush:.45},te={idle:{...q},thinking:{...q,eyeOpen:.92,lookX:.7,lookY:-0.8,lidCover:.12,mouthW:.065,mouthCurve:0,mouthSkew:.8,blush:.3},loading:{...q,lidCover:.48,mouthW:.06,mouthCurve:.12,blush:.3},listening:{...q,eyeOpen:1.12,eyeSize:1.16,mouthW:.06,mouthCurve:.2,mouthOpen:.02},speaking:{...q,mouthW:.09,mouthCurve:.25},happy:{...q,happyEye:1,mouthW:.13,mouthCurve:1,mouthOpen:.08,blush:1},surprised:{...q,eyeOpen:1.25,eyeSize:1.22,mouthW:.06,mouthCurve:0,mouthOpen:.12,mouthRound:1,blush:.35},sad:{...q,eyeOpen:.95,lidCover:.32,lidTilt:.5,lookY:.45,mouthW:.08,mouthCurve:-0.75,blush:.2},error:{...q,xEye:1,mouthW:.1,mouthCurve:-0.2,mouthWave:1,blush:.15},sleeping:{...q,closedEye:1,mouthW:.04,mouthCurve:0,mouthOpen:.035,mouthRound:.7,blush:.55},love:{...q,heartEye:1,mouthW:.11,mouthCurve:.9,mouthOpen:.03,blush:1}},Fe={idle:1,thinking:.15,loading:0,listening:1,speaking:.9,happy:.6,surprised:.8,sad:.3,error:.3,sleeping:0,love:.7},be=e=>({...e}),ot=.31,st=-0.04,it=.098,me=.128,nt=.2,fe=.05;function ze(e,o,t,s,i,r,h,c,l,p){let n=e.canvas.width;e.setTransform(1,0,0,1,0,0),e.clearRect(0,0,n,n),e.setTransform(n/2,0,0,n/2,n/2,n/2),e.lineCap="round",e.lineJoin="round";let a=o.lookX*.075,u=o.lookY*.06;if(o.blush>.01){let m=n*.018;e.save(),"filter"in e&&(e.filter=`blur(${m}px)`);for(let g of [-1,1]){let b=g*.52+a,d=.16+u,y=e.createRadialGradient(b,d,0,b,d,.16);y.addColorStop(0,i),y.addColorStop(1,"rgba(0,0,0,0)"),e.globalAlpha=Math.min(1,o.blush)*.62,e.fillStyle=y,e.beginPath(),e.ellipse(b,d,.17,.12,0,0,Math.PI*2),e.fill();}e.restore();let f=Math.min(1,Math.max(0,(o.blush-.6)/.4));if(f>.01){e.save(),e.globalAlpha=f*.55,e.strokeStyle=i,e.lineWidth=.016,e.lineCap="round";for(let g of [-1,1]){let b=g*.52+a,d=.16+u;for(let y=-1;y<=1;y++){let P=b+y*.045;e.beginPath(),e.moveTo(P+.018,d-.035),e.lineTo(P-.018,d+.035),e.stroke();}}e.restore();}}e.fillStyle=s,e.strokeStyle=s,e.lineWidth=fe;let w=Math.min(1,o.happyEye+o.closedEye+o.xEye+o.heartEye),A=1-w;for(let m of [-1,1]){let f=m*ot+a,g=st+u,b=it*o.eyeSize;if(A>.01){e.globalAlpha=A;let d=Math.max(.06,o.eyeOpen*t),y=me*o.eyeSize*d;if(e.save(),o.lidCover>.01||o.lidTilt>.01){let W=g-me*o.eyeSize+2*me*o.eyeSize*o.lidCover;e.translate(f,W),e.rotate(m*o.lidTilt*.6),e.beginPath(),e.rect(-1,0,2,2),e.clip(),e.rotate(-m*o.lidTilt*.6),e.translate(-f,-W);}r>.5&&Re(e,f,g,b*2.1,y*1.9,s,.35*A);let P=e.createRadialGradient(f,g-y*.2,0,f,g,Math.max(b,y));if(P.addColorStop(0,h),P.addColorStop(1,s),e.fillStyle=P,e.beginPath(),e.ellipse(f,g,b,y,0,0,Math.PI*2),e.fill(),e.fillStyle=s,d>.35){let R=Math.max(-1,Math.min(1,o.lookX)),W=Math.max(-1,Math.min(1,o.lookY)),le=c==="#fff"?.3:.34;e.globalAlpha=A*Math.min(1,(d-.35)*3),e.fillStyle=c,e.beginPath(),e.arc(f-b*.1+R*b*.5,g-y*.16+W*y*.42,b*le,0,Math.PI*2),e.fill(),e.fillStyle=s;}e.restore();}if(r>.5&&w>.01&&Re(e,f,g,b*2,b*1.8,s,.3*w),e.fillStyle=s,o.happyEye>.01){let d=.6+.4*o.happyEye;e.globalAlpha=Math.min(1,o.happyEye),e.beginPath(),e.arc(f,g+.045,b*1.15*d,Math.PI*1.12,Math.PI*1.88),e.stroke();}if(o.closedEye>.01){let d=.6+.4*o.closedEye;e.globalAlpha=Math.min(1,o.closedEye),e.beginPath(),e.moveTo(f-b*1.15*d,g+.01),e.quadraticCurveTo(f,g+.07*d,f+b*1.15*d,g+.01),e.stroke();}if(o.xEye>.01){let d=b*.95*(.6+.4*o.xEye);e.globalAlpha=Math.min(1,o.xEye),e.beginPath(),e.moveTo(f-d,g-d),e.lineTo(f+d,g+d),e.moveTo(f+d,g-d),e.lineTo(f-d,g+d),e.stroke();}o.heartEye>.01&&(e.globalAlpha=Math.min(1,o.heartEye),ge(e,f,g,b*1.35*(.5+.5*o.heartEye)),e.fill());}let k=a+o.mouthSkew*.07,S=nt+u,E=Math.min(1,Math.max(0,o.mouthRound)),C=o.mouthW,O=e.createLinearGradient(k-C-.02,S,k+C+.02,S);O.addColorStop(0,p),O.addColorStop(.5,h),O.addColorStop(1,p);let B=(m,f,g,b)=>{e.fillStyle=l,m(),e.fill(),f>.02&&(e.save(),m(),e.clip(),e.fillStyle=i,e.globalAlpha*=Math.min(1,(f-.02)*12),e.beginPath(),e.ellipse(k,g,b,f*.55,0,0,Math.PI*2),e.fill(),e.restore()),e.strokeStyle=O;let d=e.lineWidth;e.lineWidth=d*.6,m(),e.stroke(),e.lineWidth=d;};if(E<.99){e.globalAlpha=1-E;let m=18,f=y=>o.mouthCurve*.075*(1-y*y),g=(y,P)=>{let R=y/m*2-1,W=k+R*C,le=S+f(R)+o.mouthSkew*R*.03+o.mouthWave*.018*Math.sin((R+1)*Math.PI*2)+P*(1-R*R);return [W,le]},b=Math.max(0,o.mouthOpen),d=()=>{for(let y=0;y<=m;y++){let[P,R]=g(y,-b*.15);y?e.lineTo(P,R):e.moveTo(P,R);}};b>.003?B(()=>{e.beginPath(),d();for(let P=m;P>=0;P--){let[R,W]=g(P,b);e.lineTo(R,W);}e.closePath();},b,g(m/2,b)[1],C*.55):(e.strokeStyle=O,e.beginPath(),d(),e.stroke());}if(E>.01){e.globalAlpha=E;let m=Math.max(.02,o.mouthOpen*.55+.01),f=()=>{e.beginPath(),e.ellipse(k,S+.01,m*.85,m,0,0,Math.PI*2);};e.lineWidth=fe*.8,B(f,o.mouthOpen,S+.01+m*.8,m*.6),e.lineWidth=fe;}e.globalAlpha=1;}function Re(e,o,t,s,i,r,h){let c=e.createRadialGradient(o,t,0,o,t,1);c.addColorStop(0,r),c.addColorStop(1,"rgba(0,0,0,0)"),e.save(),e.globalAlpha=h,e.translate(o,t),e.scale(s,i),e.translate(-o,-t),e.fillStyle=c,e.beginPath(),e.arc(o,t,1,0,Math.PI*2),e.fill(),e.restore();}function ge(e,o,t,s){e.beginPath(),e.moveTo(o,t+s*.85),e.bezierCurveTo(o-s*1.25,t+s*.05,o-s*.75,t-s*.95,o,t-s*.35),e.bezierCurveTo(o+s*.75,t-s*.95,o+s*1.25,t+s*.05,o,t+s*.85),e.closePath();}var ie=()=>({y:0,rx:0,ry:0,rz:0,st:0,mouth:0,lookX:0,lookY:0}),{sin:v,cos:rt,abs:at,max:oe,exp:ht,PI:se}=Math;function ye(e,o,t,s){switch(s.y=s.rx=s.ry=s.rz=s.st=s.mouth=s.lookX=s.lookY=0,e){case "idle":{let i=v(o*1.6);s.y=.015*i,s.st=.018*i;break}case "thinking":{s.rz=.07*v(o*1.1),s.y=.02*v(o*2.2),s.ry=.15,s.rx=-0.06;break}case "loading":{let i=o*2.4;s.rx=.12*v(i),s.rz=.12*rt(i),s.ry=.22*v(i),s.y=.02*v(i*2),s.lookX=.55*v(i);break}case "listening":{let i=oe(0,v(o*3.2));s.rx=.09*i*i;let r=at(v(o*7.1)*.5+v(o*11.3)*.3+v(o*3.7)*.2);s.st=.03*r;break}case "speaking":{let i=oe(0,v(o*9)*.6+v(o*14.3)*.4)*(.65+.35*v(o*1.7));s.mouth=.11*i,s.st=.04*i,s.rz=.03*v(o*2.1);break}case "happy":{let i=o*1.35%1;s.y=.2*v(se*i);let r=oe(0,1-i/.14)+oe(0,(i-.9)/.1);s.st=.08*v(se*i)-.14*r,s.rz=.05*v(o*1.35*se);break}case "surprised":{if(t<.45){let i=v(se*t/.45);s.y=.16*i,s.st=.12*i;}s.rz=.01*v(o*38)*ht(-t*2),s.rx=-0.06;break}case "sad":{let i=v(o*1.1);s.y=-0.06,s.rx=.2,s.st=-0.04+.015*i,s.rz=-0.04;break}case "error":{let i=t%2.8;if(i<.55){let r=1-i/.55;s.ry=.26*v(i*42)*r,s.rz=.05*v(i*42+1)*r;}s.st=-0.02;break}case "sleeping":{let i=v(o*1);s.st=.04*i,s.y=-0.03,s.rz=.1,s.rx=.1;break}case "love":{s.y=.07+.06*v(o*1.6),s.rz=.05*v(o*.8),s.st=.015*v(o*1.6+1);break}}return s}function Oe(e,o,t,s){for(let i of Object.keys(s))s[i]=e[i]+(o[i]-e[i])*t;return s}var Be={thinking:"dots",sleeping:"zz",love:"hearts",listening:"waves",surprised:"bang"};var j=new Set,T=null,H=0,V=0,ne=0,Me,Le=false,$="idle",D={x:0,y:0,lastMove:-1e9,known:false},z={reducedMotion:false,lowPower:false};function ct(){if(Le)return;Le=true,addEventListener("pointermove",o=>{D.x=o.clientX,D.y=o.clientY,D.lastMove=performance.now(),D.known=true;},{passive:true}),document.addEventListener("visibilitychange",_);let e=matchMedia("(prefers-reduced-motion: reduce)");z.reducedMotion=e.matches,e.addEventListener?.("change",o=>z.reducedMotion=o.matches);}function qe(e,o){return T||(T=new three.WebGLRenderer({antialias:true,alpha:true,powerPreference:"low-power",preserveDrawingBuffer:true}),T.setPixelRatio(1),T.setClearColor(0,0),z.lowPower=dt(T.getContext()),T.setScissorTest(true),T.domElement.addEventListener("webglcontextlost",t=>{t.preventDefault(),T?.dispose(),T=null,H=V=0,$="idle";})),(e>H||o>V)&&(H=Math.max(H,e),V=Math.max(V,o),T.setSize(H,V,false)),T}var Ie=()=>V;function dt(e){try{let o=e.getExtension("WEBGL_debug_renderer_info"),t=String(e.getParameter(o?o.UNMASKED_RENDERER_WEBGL:e.RENDERER));return /swiftshader|llvmpipe|softpipe|software|basic render/i.test(t)}catch{return false}}function Ye(e,o){if($==="ready")return true;if($==="idle"&&T){$="compiling";let t=T;t.compileAsync(e,o).catch(()=>{}).then(()=>{t===T&&($="ready"),_();});}return false}function We(e){ct(),clearTimeout(Me),j.add(e),_();}function De(e){j.delete(e),j.size===0&&(clearTimeout(Me),Me=setTimeout(()=>{j.size||!T||(T.dispose(),T.forceContextLoss(),T=null,H=V=0,$="idle");},1500));}function _(){if(!ne){for(let e of j)if(e.active){ne=requestAnimationFrame(_e);return}}}var ut=10,Ge=new WeakMap,ve=0;function _e(e){ne=0;let o=[...j].filter(r=>r.active);if(!o.length)return;let t=performance.now(),s=o.length,i=0;for(;i<s;i++){let r=o[(ve+i)%s],h=Ge.get(r)??e-16;if(Ge.set(r,e),r.tick(Math.min(.05,Math.max(0,(e-h)/1e3)),e),performance.now()-t>ut){i++;break}}ve=(ve+i)%s,ne=requestAnimationFrame(_e);}var J={pebble:{faceY:.02,faceScale:1.3,animated:false},blob:{faceY:.02,faceScale:1.28,animated:true},drop:{faceY:-0.24,faceScale:1.22,animated:false},bean:{faceY:.14,faceScale:.92,animated:false},cube:{faceY:0,faceScale:1.22,animated:false},ghost:{faceY:.22,faceScale:1.24,animated:true}};function Ne(e){let o=new three.SphereGeometry(1,e*2,Math.round(e*1.5)),t=o.getAttribute("position"),s=o.getIndex(),i=new Map,r=[],h=new Uint32Array(t.count);for(let n=0;n<t.count;n++){let a=t.getX(n),u=t.getY(n),w=t.getZ(n),A=`${Math.round(a*1e4)},${Math.round(u*1e4)},${Math.round(w*1e4)}`,k=i.get(A);if(k===void 0){k=r.length/3,i.set(A,k);let S=Math.hypot(a,u,w)||1;r.push(a/S,u/S,w/S);}h[n]=k;}let c=[];for(let n=0;n<s.count;n+=3){let a=h[s.getX(n)],u=h[s.getX(n+1)],w=h[s.getX(n+2)];a!==u&&u!==w&&a!==w&&c.push(a,u,w);}o.dispose();let l=new three.BufferGeometry,p=new Float32Array(r);return l.setAttribute("position",new three.BufferAttribute(new Float32Array(p),3)),l.setIndex(c),{geometry:l,dirs:p}}var bt=(e,o,t)=>{let s=Math.min(1,Math.max(0,(t-e)/(o-e)));return s*s*(3-2*s)};function we(e,o,t,s,i,r,h){let c=o,l=t,p=s;switch(e){case "pebble":{c=o*1.1+.04*t,l=t*.92*(.95+.05*Math.tanh(3*t))+.03*o,p=s*.94;break}case "blob":{let n=.97+.045*Math.sin(2.1*o+1.3*i)*Math.sin(2.4*t+1.7*i+1)+.035*Math.sin(2.7*s+2.3*o+1.1*i);c=o*n,l=t*n,p=s*n;break}case "drop":{let n=Math.max(t,0),a=1-.8*Math.pow(n,1.6);c=o*a*.98+.1*n*n*n,l=(t<0?t*.9:t*1.32)-.12,p=s*a*.98;break}case "bean":{let n=t*.78+.42*Math.tanh(t*3);c=(o*.8+.14*(n*n/1.44)-.07)*.9,l=n*.88,p=s*.8*.9;break}case "cube":{let a=Math.pow(Math.pow(Math.abs(o),4.5)+Math.pow(Math.abs(t),4.5)+Math.pow(Math.abs(s),4.5),-0.2222222222222222);c=o*a*.84,l=t*a*.82,p=s*a*.84;break}case "ghost":{if(t>=0)c=o,l=t,p=s;else {let a=(Math.acos(Math.max(-1,t))-Math.PI/2)/(Math.PI/2),u=Math.hypot(o,s)||1,w=o/u,A=s/u,k=Math.atan2(s,o),S=.085*Math.sin(5*k+2.2*i),E=1.04,C=.13,O=-1.05+S,B;if(a<.45){let m=a/.45;B=1+.04*m,l=-m*(1.05-C)+S*bt(.45,1,m);}else if(a<.75){let m=(a-.45)/.3*(Math.PI/2);B=E-C+C*Math.cos(m),l=O+C-C*Math.sin(m);}else {let m=(a-.75)/.25;B=(E-C)*(1-m),l=O+.22*m*m*(3-2*m);}c=w*B,p=A*B;}c*=.9,l=(l+.1)*.9,p*=.9;break}}r[h]=c,r[h+1]=l,r[h+2]=p;}var I=null,re=null;function gt(){if(typeof window>"u"||!("AudioContext"in window))return null;if(yt(),!I){I=new AudioContext;let e=I.createBiquadFilter();e.type="lowpass",e.frequency.value=3200,re=I.createGain(),re.gain.value=.55,re.connect(e).connect(I.destination);}return I.state==="suspended"&&I.resume(),I}function F(e,{freq:o,to:t,at:s=0,dur:i=.12,type:r="sine",gain:h=.1,wobble:c=0}){let l=e.currentTime+s,p=e.createOscillator(),n=e.createGain();if(p.type=r,p.frequency.setValueAtTime(o,l),t&&p.frequency.exponentialRampToValueAtTime(t,l+i),c){let a=e.createOscillator(),u=e.createGain();a.frequency.value=7,u.gain.value=c,a.connect(u).connect(p.frequency),a.start(l),a.stop(l+i+.05);}n.gain.setValueAtTime(1e-4,l),n.gain.exponentialRampToValueAtTime(h,l+Math.min(.012,i/4)),n.gain.exponentialRampToValueAtTime(1e-4,l+i),p.connect(n).connect(re),p.start(l),p.stop(l+i+.02);}var ke={idle:e=>F(e,{freq:520,to:780,dur:.09,gain:.08}),thinking:e=>[660,760,900].forEach((o,t)=>F(e,{freq:o,to:o*1.08,at:t*.1,dur:.07,type:"triangle",gain:.06})),loading:e=>[0,.14,.28].forEach(o=>F(e,{freq:940,at:o,dur:.04,gain:.05})),listening:e=>{F(e,{freq:523,dur:.12,type:"triangle",gain:.07}),F(e,{freq:784,at:.1,dur:.22,type:"triangle",gain:.07});},speaking:e=>[380,460,340,520,420].forEach((o,t)=>F(e,{freq:o,to:o*1.15,at:t*.075,dur:.06,type:"triangle",gain:.06})),happy:e=>[523,659,784,1047].forEach((o,t)=>F(e,{freq:o,at:t*.07,dur:.14,type:"triangle",gain:.07})),surprised:e=>F(e,{freq:280,to:980,dur:.18,gain:.09,wobble:30}),sad:e=>{F(e,{freq:560,to:300,dur:.5,gain:.07}),F(e,{freq:420,to:240,at:.12,dur:.5,gain:.04});},error:e=>[0,.14].forEach(o=>F(e,{freq:170,to:150,at:o,dur:.1,type:"sawtooth",gain:.045})),sleeping:e=>{F(e,{freq:150,to:115,dur:.6,gain:.07,wobble:6}),F(e,{freq:300,to:230,at:.05,dur:.5,gain:.02});},love:e=>[1319,1568,2093,1760].forEach((o,t)=>F(e,{freq:o,at:t*.08,dur:.22,gain:.04}))},Y=null,Ve=false;function yt(){if(Ve)return;Ve=true;let e=()=>{I&&I.resume().then(()=>{Y&&performance.now()<Y.until&&ke[Y.mood](I),Y=null;});};for(let o of ["pointerdown","touchend","keydown"])addEventListener(o,e,{passive:true});}function ae(e,o=false){let t=gt();if(t){if(t.state==="running"){Y=null,ke[e](t);return}Y={mood:e,until:performance.now()+(o?6e3:1200)},t.resume().then(()=>{Y&&performance.now()<Y.until&&ke[Y.mood](t),Y=null;});}}var Ae=-0.85,$e=new Map;function Pt(e){let o=$e.get(e);if(!o){let{geometry:t,dirs:s}=Ne(e);o={dirs:s,index:new Uint32Array(t.getIndex().array)},t.dispose(),$e.set(e,o);}return o}var K=(e,o)=>1-Math.exp(-e*o),je=e=>1-Math.pow(1-e,3),he=class{constructor(){this.scene=new three.Scene;this.camera=new three.PerspectiveCamera(30,1,.1,50);this.group=new three.Group;this.v=new three.Vector3;this.geometry=new three.BufferGeometry;this.faceCanvas=document.createElement("canvas");this.uniforms={uFace:{value:null},uFaceY:{value:0},uFaceScale:{value:1.5}};this.state={...M};this.visible=false;this.connected=false;this.w=0;this.h=0;this.t=Math.random()*100;this.shapeFrom=M.shape;this.shapeTo=M.shape;this.morph=1;this.shapeDirty=true;this.shown=M.mood;this.prevShown=M.mood;this.tau=10;this.prevTau=10;this.blend=1;this.reaction=null;this.pose=ie();this.poseA=ie();this.poseB=ie();this.face=be(te.idle);this.target=be(te.idle);this.drawn=new Float32Array(U.length+7).fill(NaN);this.blink=1;this.blinkT=-1;this.nextBlink=2+Math.random()*3;this.body=X(M.color,[.95,.63,.48]);this.bodyT=this.body;this.faceC=Q(this.body);this.faceCT=this.faceC;this.cheek=ee(this.body);this.cheekT=this.cheek;this.look={x:0,y:0};this.wander={x:0,y:0,next:0};this.squash=0;this.squashV=0;this.pop=.6;this.popV=0;this.hover=0;this.hovering=false;this.overlay={dots:0,zz:0,hearts:0,waves:0,bang:0};this.needsFrame=true;this.ready=false;this.css=[0,0];this.quality=1;this.lowPowerApplied=false;this.cost=0;this.costFrames=0;this.lastFrame=[];this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),this.faceCtx=this.faceCanvas.getContext("2d"),this.faceCanvas.width=this.faceCanvas.height=256,this.faceTex=new three.CanvasTexture(this.faceCanvas),this.faceTex.colorSpace=three.SRGBColorSpace,this.faceTex.minFilter=three.LinearMipmapLinearFilter,this.faceTex.anisotropy=4,this.uniforms.uFace.value=this.faceTex,this.setSegments(24),this.material=this.createMaterial(),this.mesh=new three.Mesh(this.geometry,this.material),this.mesh.frustumCulled=false,this.mesh.position.y=-Ae,this.group.add(this.mesh),this.scene.add(this.group),this.scene.add(new three.HemisphereLight(16774120,7234140,1.7));let o=new three.DirectionalLight(16777215,1.9);o.position.set(2.5,3.5,4);let t=new three.DirectionalLight(16770770,1.4);t.position.set(-3.5,2,-3);let s=new three.DirectionalLight(16773350,.45);s.position.set(-2,-1,3),this.scene.add(o,t,s),this.camera.position.set(0,.3,6.6),this.camera.lookAt(0,.12,0);}get active(){return this.visible&&this.connected&&this.w>0}createMaterial(){let o=new three.MeshStandardMaterial({roughness:.62,metalness:0});return o.color.setRGB(...this.body,three.SRGBColorSpace),o.customProgramCacheKey=()=>"moodi-1",o.onBeforeCompile=t=>{Object.assign(t.uniforms,this.uniforms),t.vertexShader=t.vertexShader.replace("#include <common>",`#include <common>
|
|
2
2
|
varying vec3 vFaceP;
|
|
3
3
|
varying vec3 vFaceN;`).replace("#include <begin_vertex>",`#include <begin_vertex>
|
|
4
4
|
vFaceP = position;
|
|
5
|
-
vFaceN = objectNormal;`),
|
|
5
|
+
vFaceN = objectNormal;`),t.fragmentShader=t.fragmentShader.replace("#include <common>",`#include <common>
|
|
6
6
|
varying vec3 vFaceP;
|
|
7
7
|
varying vec3 vFaceN;
|
|
8
8
|
uniform sampler2D uFace;
|
|
@@ -20,6 +20,9 @@ outgoingLight *= mix(0.62, 1.0, smoothstep(-0.95, 0.35, normalize(vFaceN).y));
|
|
|
20
20
|
// eyes and mouth glow a little: mostly unlit, so they read as light, not paint
|
|
21
21
|
float emis = smoothstep(0.75, 1.0, fc.a) * inb * smoothstep(0.12, 0.45, normalize(vFaceN).z);
|
|
22
22
|
outgoingLight = mix(outgoingLight, diffuseColor.rgb * 1.12, emis * 0.65);
|
|
23
|
-
#include <opaque_fragment>`);},o}setSegments(o){let e=gt(o);this.dirs!==e.dirs&&(this.dirs=e.dirs,this.geometry.dispose(),this.geometry=new three.BufferGeometry,this.geometry.setAttribute("position",new three.BufferAttribute(new Float32Array(e.dirs.length),3)),this.geometry.setIndex(new three.BufferAttribute(e.index,1)),this.mesh&&(this.mesh.geometry=this.geometry),this.shapeDirty=true);}connect(){this.connected=true,ze(this);}disconnect(){this.connected=false,Oe(this);}setVisible(o){this.visible=o,o&&D();}resize(o,e){this.css=[o,e];let s=Math.min(2,devicePixelRatio||1)*this.quality,i=Math.max(1,Math.round(o*s)),a=Math.max(1,Math.round(e*s));if(i===this.w&&a===this.h)return;this.w=this.canvas.width=i,this.h=this.canvas.height=a;let l=Math.min(i,a),d=Math.min(o,e);this.camera.position.set(0,.3,d<90?5.1:6.1),this.camera.lookAt(0,d<90?.02:.12,0),this.camera.aspect=i/a,this.camera.updateProjectionMatrix(),this.setSegments(l<160?16:l<400?24:32);let u=l<220?256:512;this.faceCanvas.width!==u&&(this.faceCanvas.width=this.faceCanvas.height=u,this.faceTex.dispose(),this.drawn.fill(NaN)),this.needsFrame=true,D();}update(o){let e=this.state;this.state={...e,...o};let s=this.state;s.shape!==this.shapeTo&&(this.shapeFrom=this.shapeTo,this.shapeTo=s.shape,this.morph=0,G.reducedMotion||(this.squashV-=2.2)),(s.color!==e.color||s.faceColor!==e.faceColor||s.cheekColor!==e.cheekColor)&&(this.bodyT=_(s.color,_(M.color,this.bodyT)),this.faceCT=s.faceColor?_(s.faceColor,U(this.bodyT)):U(this.bodyT),this.cheekT=s.cheekColor?_(s.cheekColor,J(this.bodyT)):J(this.bodyT)),s.mood!==e.mood&&!this.reaction&&this.show(s.mood),this.needsFrame=true,D();}react(o,e=1200){this.reaction={mood:o,left:e/1e3},this.show(o);}get currentMood(){return this.shown}dispose(){this.disconnect(),this.geometry.dispose(),this.material.dispose(),this.faceTex.dispose(),this.onMoodChange=void 0;}show(o){if(o===this.shown)return;let e=this.shown;this.prevShown=e,this.prevTau=this.tau,this.shown=o,this.tau=0,this.blend=0,G.reducedMotion||(this.squashV-=1.1),this.onMoodChange?.({mood:o,previous:e,reaction:!!this.reaction});}tick(o,e){let s=this.state,i=G.reducedMotion,a=o*Math.max(0,s.speed);this.t+=a,this.tau+=a,this.prevTau+=a,this.reaction&&(this.reaction.left-=o,this.reaction.left<=0&&(this.reaction=null,this.show(s.mood))),pe(this.shown,this.t,this.tau,this.poseB),this.blend<1?(this.blend=Math.min(1,this.blend+o/.45),pe(this.prevShown,this.t,this.prevTau,this.poseA),Ae(this.poseA,this.poseB,We(this.blend),this.pose)):Object.assign(this.pose,this.poseB);let l=this.pose;i&&(l.y=l.rx=l.ry=l.rz=l.st=l.lookX=0);let d=s.followCursor&&!i&&s.speed>0?Ce[this.shown]:0,u=0,p=0;if(d>0&&I.known){let n=this.canvas.getBoundingClientRect(),f=Math.max(200,n.width*.85);u=Math.max(-1,Math.min(1,(I.x-(n.left+n.width/2))/f)),p=Math.max(-1,Math.min(1,(I.y-(n.top+n.height*.45))/f)),u*=d,p*=d;}else if(this.shown==="idle"&&!i&&s.speed>0){if(this.t>this.wander.next){let n=Math.random()<.4;this.wander.x=n?0:(Math.random()*2-1)*.6,this.wander.y=n?0:(Math.random()*2-1)*.35,this.wander.next=this.t+1.8+Math.random()*2.6;}u=this.wander.x,p=this.wander.y;}let r=V(7,o);this.look.x+=(u-this.look.x)*r,this.look.y+=(p-this.look.y)*r;let h=Z[this.shown],c=this.target;for(let n of $)c[n]=h[n];c.lookX+=this.look.x+l.lookX,c.lookY+=this.look.y+l.lookY,c.mouthOpen+=l.mouth,c.eyeOpen+=this.hover*.08;let C=V(11,o);for(let n of $)this.face[n]+=(c[n]-this.face[n])*C;let A=V(16,o);if(this.face.lookX+=(c.lookX-this.face.lookX)*A,this.face.lookY+=(c.lookY-this.face.lookY)*A,this.face.mouthOpen+=(c.mouthOpen-this.face.mouthOpen)*A,this.nextBlink-=a,this.nextBlink<=0&&this.blinkT<0&&(this.blinkT=0,this.nextBlink=Math.random()<.2?.25:2.2+Math.random()*3.8),this.blinkT>=0){this.blinkT+=a;let n=this.blinkT/.16;this.blink=n>=1?1:1-Math.sin(Math.PI*n),n>=1&&(this.blinkT=-1);}let k=Math.min(o,1/30);this.squashV+=(-170*this.squash-11*this.squashV)*k,this.squash+=this.squashV*k,i&&(this.squash=this.squashV=0),i?this.pop=1:(this.popV+=(-140*(this.pop-1)-13*this.popV)*k,this.pop+=this.popV*k),this.hover+=((this.hovering&&s.interactive?1:0)-this.hover)*V(12,o);let w=V(9,o);this.body=P(this.body,this.bodyT,w),this.faceC=P(this.faceC,this.faceCT,w),this.cheek=P(this.cheek,this.cheekT,w),this.material.color.setRGB(this.body[0],this.body[1],this.body[2],three.SRGBColorSpace);let T=Ee[this.shown],S=V(6,o);for(let n of Object.keys(this.overlay))this.overlay[n]+=((T===n?1:0)-this.overlay[n])*S;this.morph<1&&(this.morph=Math.min(1,this.morph+o/(i?.2:.38)),this.shapeDirty=true);let R=j[this.shapeTo].animated||this.morph<1&&j[this.shapeFrom].animated;(this.shapeDirty||R&&a>0)&&this.rebuild();let B=this.group,m=(1+l.st+this.squash)*(1+this.hover*.04),b=1/Math.sqrt(Math.max(.3,1+l.st+this.squash))*(1+this.hover*.04);B.position.y=ve+l.y,B.rotation.set(l.rx+this.look.y*.22*(d>0?1:0),l.ry+this.look.x*.42,l.rz),B.scale.set(b*this.pop,m*this.pop,b*this.pop);let y=this.drawFaceIfNeeded();if(s.speed===0&&!y&&!this.needsFrame&&this.settled())return;let v=performance.now();this.render(l)&&(this.needsFrame=false,this.adapt(performance.now()-v));}adapt(o){this.cost=this.costFrames?this.cost*.9+o*.1:o,!(++this.costFrames<20||this.cost<9||this.quality<=.5)&&(this.quality=Math.max(.5,this.quality-.25),this.costFrames=0,this.resize(this.css[0],this.css[1]));}settled(){let o=this.group,e=this.overlay,s=[e.bang,o.position.y,o.rotation.x,o.rotation.y,o.rotation.z,o.scale.x,o.scale.y,...this.body,this.morph,e.dots,e.zz,e.hearts,e.waves],i=this.lastFrame,a=i.length===s.length;for(let l=0;a&&l<s.length;l++)a=Math.abs(s[l]-i[l])<1e-4;return this.lastFrame=s,a}rebuild(){let o=this.geometry.getAttribute("position"),e=o.array,s=this.dirs,i=We(this.morph),a=this.shapeFrom,l=this.shapeTo,d=new Float32Array(3);for(let r=0;r<s.length;r+=3)fe(l,s[r],s[r+1],s[r+2],this.t,e,r),i<1&&(fe(a,s[r],s[r+1],s[r+2],this.t,d,0),e[r]=d[0]+(e[r]-d[0])*i,e[r+1]=d[1]+(e[r+1]-d[1])*i,e[r+2]=d[2]+(e[r+2]-d[2])*i);o.needsUpdate=true,this.geometry.computeVertexNormals();let u=j[a],p=j[l];this.uniforms.uFaceY.value=u.faceY+(p.faceY-u.faceY)*i,this.uniforms.uFaceScale.value=u.faceScale+(p.faceScale-u.faceScale)*i,this.shapeDirty=false;}drawFaceIfNeeded(){let o=this.face,e=this.drawn,s=false,i=0;for(let d of $)Math.abs(e[i]-o[d])<.002||(s=true,e[i]=o[d]),i++;let a=[this.blink,...this.faceC,...this.cheek];for(let d of a)Math.abs(e[i]-d)<.002||(s=true,e[i]=d),i++;if(!s)return false;let l=he(this.faceC)>.45;return Te(this.faceCtx,o,this.blink,O(this.faceC),O(this.cheek),l?1:.3,O(P(this.faceC,[1,1,1],l?.6:.16)),l?O(P(this.body,[.04,.03,.06],.86)):"#fff",O(l?P(this.body,[.03,.02,.05],.78):P(this.faceC,[0,0,0],.35)),O(this.faceC)),this.faceTex.needsUpdate=true,true}project(o,e,s){let i=this.v.set(o,e,s).applyMatrix4(this.group.matrixWorld).project(this.camera);return [(i.x*.5+.5)*this.w,(-i.y*.5+.5)*this.h]}render(o){let{w:e,h:s,ctx:i}=this,a=Fe(e,s);if(!Be(this.scene,this.camera))return false;if(i.clearRect(0,0,e,s),this.group.updateMatrixWorld(),this.state.shadow){let l=this.v.set(0,ve-.08,0).project(this.camera),d=(l.x*.5+.5)*e,u=(-l.y*.5+.5)*s,p=Math.max(0,o.y)*3,r=Math.min(e,s)*.26*(1-p*.25)*this.pop,h=i.createRadialGradient(d,u,0,d,u,r),c=P(this.body,[.05,.03,.02],.75);h.addColorStop(0,`rgba(${c[0]*255|0},${c[1]*255|0},${c[2]*255|0},${.42*(1-p*.4)})`),h.addColorStop(1,"rgba(0,0,0,0)"),i.fillStyle=h,i.save(),i.translate(d,u),i.scale(1,.22),i.translate(-d,-u),i.beginPath(),i.arc(d,u,r,0,Math.PI*2),i.fill(),i.restore();}return a.setViewport(0,0,e,s),a.setScissor(0,0,e,s),a.render(this.scene,this.camera),i.drawImage(a.domElement,0,Re()-s,e,s,0,0,e,s),this.drawOverlays(),true}drawOverlays(){let o=this.overlay;if(o.dots+o.zz+o.hearts+o.waves+o.bang<.01)return;let{ctx:e}=this,s=this.t,i=G.reducedMotion,a=Math.min(this.w,this.h),l=P(this.body,[1,1,1],.22),d=O(l);if(e.save(),o.dots>.01){let[u,p]=this.project(.6,.92,.2),r=i?2:s*.55%1.6,h=[.014,.022,.052];for(let c=0;c<3;c++){let C=i?1:we(Me((r-c*.18)/.32)),A=i?0:Math.sin(s*2.2+c)*a*.006,k=u+[0,.05,.12][c]*a,w=p-[0,.05,.12][c]*a+A,T=h[c]*a*C;if(!(T<=.2)&&(e.globalAlpha=o.dots*.95,e.fillStyle=d,e.beginPath(),c<2?e.arc(k,w,T,0,Math.PI*2):yt(e,k,w,T),e.fill(),c===2&&C>.6)){e.fillStyle=O(P(this.body,[0,0,0],.35));for(let S=0;S<3;S++){let R=i?0:Math.max(0,Math.sin(s*7-S*.9))*T*.16;e.beginPath(),e.arc(k+(S-1)*T*.34,w-R,T*.1,0,Math.PI*2),e.fill();}}}}if(o.zz>.01){let[u,p]=this.project(.45,.85,.2);e.textAlign="center",e.textBaseline="middle",e.fillStyle=d;for(let r=0;r<3;r++){let h=i?.25+r*.25:(s*.33+r/3)%1,c=u+h*a*.17+Math.sin(h*7+r)*a*.018,C=p-ke(h)*a*.26;e.globalAlpha=o.zz*Math.min(1,h*5)*(1-h)*1.3,e.save(),e.translate(c,C),e.rotate(-0.25+Math.sin(h*5+r)*.22),e.font=`800 ${Math.round(a*(.05+.06*h))}px ui-rounded, system-ui, sans-serif`,e.fillText("z",0,0),e.restore();}}if(o.hearts>.01){let[u,p]=this.project(0,.8,.2),r=P(this.cheek,[1,.3,.45],.55);for(let h=0;h<4;h++){let c=i?.4:(s*.3+h/4)%1,C=h%2?1:-1,A=u+C*a*(.16+.07*(h>>1))+Math.sin(c*6+h)*a*.025,k=p-a*.02-ke(c)*a*.28,w=i?1:we(Me(c/.22)),T=a*(.026+.008*(h%3))*w;e.globalAlpha=o.hearts*(1-Math.max(0,(c-.6)/.4)),e.save(),e.translate(A,k),e.rotate(Math.sin(s*3+h)*.25);let S=e.createLinearGradient(0,-T,0,T);S.addColorStop(0,O(P(r,[1,1,1],.35))),S.addColorStop(1,O(r)),e.fillStyle=S,ue(e,0,0,T),e.fill(),e.restore();}}if(o.waves>.01){e.lineCap="round",e.strokeStyle=d;for(let u of [-1,1]){let[p,r]=this.project(u*1,.12,0);for(let h=0;h<3;h++){let c=i?.3+h*.2:(s*.8+h/3)%1,C=ke(c);e.globalAlpha=o.waves*Math.sin(Math.PI*c)*.9,e.lineWidth=a*(.016-.008*c),e.beginPath();let A=a*(.03+.11*C);u>0?e.arc(p-a*.03,r,A,-0.55,.55):e.arc(p+a*.03,r,A,Math.PI-.55,Math.PI+.55),e.stroke();}}}if(o.bang>.01){let[u,p]=this.project(.55,1,.2),r=i?1:we(Me(this.tau/.35));e.globalAlpha=o.bang,e.fillStyle=d,e.save(),e.translate(u,p),e.rotate(.18),e.scale(r,r),e.beginPath(),e.roundRect(-a*.016,-a*.1,a*.032,a*.07,a*.016),e.arc(0,a*.005,a*.018,0,Math.PI*2),e.fill(),e.restore();}e.restore();}},Me=t=>t<0?0:t>1?1:t,ke=t=>1-(1-t)*(1-t),we=t=>1+(1.9+1)*Math.pow(t-1,3)+1.9*Math.pow(t-1,2);function yt(t,o,e,s){t.ellipse(o,e,s,s*.72,0,0,Math.PI*2),t.moveTo(o-s*.35+s*.45,e-s*.38),t.arc(o-s*.35,e-s*.38,s*.45,0,Math.PI*2),t.moveTo(o+s*.3+s*.5,e-s*.32),t.arc(o+s*.3,e-s*.32,s*.5,0,Math.PI*2);}var vt=["shape","mood","color","face-color","cheek-color","size","speed","follow-cursor","interactive","shadow"],H=(t,o)=>t===null?o:t!=="false"&&t!=="0",_e=t=>t==null||t===""?`${M.size}px`:typeof t=="number"||/^\d+(\.\d+)?$/.test(String(t))?`${t}px`:String(t),Mt=`:host{display:inline-block;position:relative;cursor:pointer;width:var(--moodi-size);height:var(--moodi-size);vertical-align:middle;contain:layout paint style;-webkit-tap-highlight-color:transparent}
|
|
23
|
+
#include <opaque_fragment>`);},o}setSegments(o){let t=Pt(o);this.dirs!==t.dirs&&(this.dirs=t.dirs,this.geometry.dispose(),this.geometry=new three.BufferGeometry,this.geometry.setAttribute("position",new three.BufferAttribute(new Float32Array(t.dirs.length),3)),this.geometry.setIndex(new three.BufferAttribute(t.index,1)),this.mesh&&(this.mesh.geometry=this.geometry),this.shapeDirty=true);}connect(){this.connected=true,We(this);}disconnect(){this.connected=false,De(this);}setVisible(o){this.visible=o,o&&_();}resize(o,t){this.css=[o,t];let s=(z.lowPower?1:Math.min(2,devicePixelRatio||1))*this.quality,i=Math.max(1,Math.round(o*s)),r=Math.max(1,Math.round(t*s));if(i===this.w&&r===this.h)return;this.w=this.canvas.width=i,this.h=this.canvas.height=r;let h=Math.min(i,r),c=Math.min(o,t);this.camera.position.set(0,.3,c<90?5.1:6.1),this.camera.lookAt(0,c<90?.02:.12,0),this.camera.aspect=i/r,this.camera.updateProjectionMatrix(),this.setSegments(h<160?16:h<400?24:32);let l=h<220?256:512;this.faceCanvas.width!==l&&(this.faceCanvas.width=this.faceCanvas.height=l,this.faceTex.dispose(),this.drawn.fill(NaN)),this.needsFrame=true,_();}update(o){let t=this.state;this.state={...t,...o};let s=this.state;s.shape!==this.shapeTo&&(this.shapeFrom=this.shapeTo,this.shapeTo=s.shape,this.morph=0,z.reducedMotion||(this.squashV-=2.2)),(s.color!==t.color||s.faceColor!==t.faceColor||s.cheekColor!==t.cheekColor)&&(this.bodyT=X(s.color,X(M.color,this.bodyT)),this.faceCT=s.faceColor?X(s.faceColor,Q(this.bodyT)):Q(this.bodyT),this.cheekT=s.cheekColor?X(s.cheekColor,ee(this.bodyT)):ee(this.bodyT)),s.mood!==t.mood&&!this.reaction&&this.show(s.mood),s.sound&&!t.sound&&this.ready&&ae(this.shown),this.needsFrame=true,_();}bump(o=1){z.reducedMotion||(this.squashV-=1.6*o,this.needsFrame=true,_());}react(o,t=1200){this.reaction={mood:o,left:t/1e3},this.show(o);}get currentMood(){return this.shown}dispose(){this.disconnect(),this.geometry.dispose(),this.material.dispose(),this.faceTex.dispose(),this.onMoodChange=void 0;}show(o,t=true){if(o===this.shown)return;t&&this.state.sound&&this.ready&&ae(o);let s=this.shown;this.prevShown=s,this.prevTau=this.tau,this.shown=o,this.tau=0,this.blend=0,z.reducedMotion||(this.squashV-=1.1),this.onMoodChange?.({mood:o,previous:s,reaction:!!this.reaction});}tick(o,t){let s=this.state,i=z.reducedMotion,r=z.lowPower?0:o*Math.max(0,s.speed);this.t+=r,this.tau+=r,this.prevTau+=r,this.reaction&&(this.reaction.left-=o,this.reaction.left<=0&&(this.reaction=null,this.show(s.mood,false))),ye(this.shown,this.t,this.tau,this.poseB),this.blend<1?(this.blend=Math.min(1,this.blend+o/.45),ye(this.prevShown,this.t,this.prevTau,this.poseA),Oe(this.poseA,this.poseB,je(this.blend),this.pose)):Object.assign(this.pose,this.poseB);let h=this.pose;i&&(h.y=h.rx=h.ry=h.rz=h.st=h.lookX=0);let c=s.followCursor&&!i&&s.speed>0?Fe[this.shown]:0,l=0,p=0;if(c>0&&D.known){let d=this.canvas.getBoundingClientRect(),y=Math.max(200,d.width*.85);l=Math.max(-1,Math.min(1,(D.x-(d.left+d.width/2))/y)),p=Math.max(-1,Math.min(1,(D.y-(d.top+d.height*.45))/y)),l*=c,p*=c;}else if(this.shown==="idle"&&!i&&s.speed>0){if(this.t>this.wander.next){let d=Math.random()<.4;this.wander.x=d?0:(Math.random()*2-1)*.6,this.wander.y=d?0:(Math.random()*2-1)*.35,this.wander.next=this.t+1.8+Math.random()*2.6;}l=this.wander.x,p=this.wander.y;}let n=K(7,o);this.look.x+=(l-this.look.x)*n,this.look.y+=(p-this.look.y)*n;let a=te[this.shown],u=this.target;for(let d of U)u[d]=a[d];u.lookX+=this.look.x+h.lookX,u.lookY+=this.look.y+h.lookY,u.mouthOpen+=h.mouth,u.eyeOpen+=this.hover*.08;let w=K(11,o);for(let d of U)this.face[d]+=(u[d]-this.face[d])*w;let A=K(16,o);if(this.face.lookX+=(u.lookX-this.face.lookX)*A,this.face.lookY+=(u.lookY-this.face.lookY)*A,this.face.mouthOpen+=(u.mouthOpen-this.face.mouthOpen)*A,this.nextBlink-=r,this.nextBlink<=0&&this.blinkT<0&&(this.blinkT=0,this.nextBlink=Math.random()<.2?.25:2.2+Math.random()*3.8),this.blinkT>=0){this.blinkT+=r;let d=this.blinkT/.16;this.blink=d>=1?1:1-Math.sin(Math.PI*d),d>=1&&(this.blinkT=-1);}let k=Math.min(o,1/30);this.squashV+=(-170*this.squash-11*this.squashV)*k,this.squash+=this.squashV*k,i&&(this.squash=this.squashV=0),i?this.pop=1:(this.popV+=(-140*(this.pop-1)-13*this.popV)*k,this.pop+=this.popV*k),this.hover+=((this.hovering&&s.interactive?1:0)-this.hover)*K(12,o);let S=K(9,o);this.body=x(this.body,this.bodyT,S),this.faceC=x(this.faceC,this.faceCT,S),this.cheek=x(this.cheek,this.cheekT,S),this.material.color.setRGB(this.body[0],this.body[1],this.body[2],three.SRGBColorSpace);let E=Be[this.shown],C=K(6,o);for(let d of Object.keys(this.overlay))this.overlay[d]+=((E===d?1:0)-this.overlay[d])*C;this.morph<1&&(this.morph=Math.min(1,this.morph+o/(i?.2:.38)),this.shapeDirty=true);let O=J[this.shapeTo].animated||this.morph<1&&J[this.shapeFrom].animated;(this.shapeDirty||O&&r>0)&&this.rebuild();let B=this.group,m=(1+h.st+this.squash)*(1+this.hover*.04),f=1/Math.sqrt(Math.max(.3,1+h.st+this.squash))*(1+this.hover*.04);B.position.y=Ae+h.y,B.rotation.set(h.rx+this.look.y*.22*(c>0?1:0),h.ry+this.look.x*.42,h.rz),B.scale.set(f*this.pop,m*this.pop,f*this.pop);let g=this.drawFaceIfNeeded();if((s.speed===0||z.lowPower)&&!g&&!this.needsFrame&&this.settled())return;let b=performance.now();this.render(h)&&(this.needsFrame=false,this.ready||(this.ready=true,this.state.sound&&ae(this.shown,true),this.onReady?.(),this.onReady=void 0),this.adapt(performance.now()-b));}adapt(o){this.cost=this.costFrames?this.cost*.9+o*.1:o,!(++this.costFrames<20||this.cost<9||this.quality<=.5)&&(this.quality=Math.max(.5,this.quality-.25),this.costFrames=0,this.resize(this.css[0],this.css[1]));}settled(){let o=this.group,t=this.overlay,s=[t.bang,o.position.y,o.rotation.x,o.rotation.y,o.rotation.z,o.scale.x,o.scale.y,...this.body,this.morph,t.dots,t.zz,t.hearts,t.waves],i=this.lastFrame,r=i.length===s.length;for(let h=0;r&&h<s.length;h++)r=Math.abs(s[h]-i[h])<1e-4;return this.lastFrame=s,r}rebuild(){let o=this.geometry.getAttribute("position"),t=o.array,s=this.dirs,i=je(this.morph),r=this.shapeFrom,h=this.shapeTo,c=new Float32Array(3);for(let n=0;n<s.length;n+=3)we(h,s[n],s[n+1],s[n+2],this.t,t,n),i<1&&(we(r,s[n],s[n+1],s[n+2],this.t,c,0),t[n]=c[0]+(t[n]-c[0])*i,t[n+1]=c[1]+(t[n+1]-c[1])*i,t[n+2]=c[2]+(t[n+2]-c[2])*i);o.needsUpdate=true,this.geometry.computeVertexNormals();let l=J[r],p=J[h];this.uniforms.uFaceY.value=l.faceY+(p.faceY-l.faceY)*i,this.uniforms.uFaceScale.value=l.faceScale+(p.faceScale-l.faceScale)*i,this.shapeDirty=false;}drawFaceIfNeeded(){let o=this.face,t=this.drawn,s=false,i=0;for(let c of U)Math.abs(t[i]-o[c])<.002||(s=true,t[i]=o[c]),i++;let r=[this.blink,...this.faceC,...this.cheek];for(let c of r)Math.abs(t[i]-c)<.002||(s=true,t[i]=c),i++;if(!s)return false;let h=pe(this.faceC)>.45;return ze(this.faceCtx,o,this.blink,G(this.faceC),G(this.cheek),h?1:.3,G(x(this.faceC,[1,1,1],h?.6:.16)),h?G(x(this.body,[.04,.03,.06],.86)):"#fff",G(h?x(this.body,[.03,.02,.05],.78):x(this.faceC,[0,0,0],.35)),G(this.faceC)),this.faceTex.needsUpdate=true,true}project(o,t,s){let i=this.v.set(o,t,s).applyMatrix4(this.group.matrixWorld).project(this.camera);return [(i.x*.5+.5)*this.w,(-i.y*.5+.5)*this.h]}render(o){let{w:t,h:s,ctx:i}=this,r=qe(t,s);if(z.lowPower&&!this.lowPowerApplied){this.lowPowerApplied=true;let[h,c]=this.css;return this.w=this.h=0,this.resize(h,c),false}if(!Ye(this.scene,this.camera))return false;if(i.clearRect(0,0,t,s),this.group.updateMatrixWorld(),this.state.shadow){let h=this.v.set(0,Ae-.08,0).project(this.camera),c=(h.x*.5+.5)*t,l=(-h.y*.5+.5)*s,p=Math.max(0,o.y)*3,n=Math.min(t,s)*.26*(1-p*.25)*this.pop,a=i.createRadialGradient(c,l,0,c,l,n),u=x(this.body,[.05,.03,.02],.75);a.addColorStop(0,`rgba(${u[0]*255|0},${u[1]*255|0},${u[2]*255|0},${.42*(1-p*.4)})`),a.addColorStop(1,"rgba(0,0,0,0)"),i.fillStyle=a,i.save(),i.translate(c,l),i.scale(1,.22),i.translate(-c,-l),i.beginPath(),i.arc(c,l,n,0,Math.PI*2),i.fill(),i.restore();}return r.setViewport(0,0,t,s),r.setScissor(0,0,t,s),r.render(this.scene,this.camera),i.drawImage(r.domElement,0,Ie()-s,t,s,0,0,t,s),this.drawOverlays(),true}drawOverlays(){let o=this.overlay;if(o.dots+o.zz+o.hearts+o.waves+o.bang<.01)return;let{ctx:t}=this,s=this.t,i=z.reducedMotion,r=Math.min(this.w,this.h),h=x(this.body,[1,1,1],.22),c=G(h);if(t.save(),o.dots>.01){let[l,p]=this.project(.6,.92,.2),n=i?2:s*.55%1.6,a=[.014,.022,.052];for(let u=0;u<3;u++){let w=i?1:Pe(Ee((n-u*.18)/.32)),A=i?0:Math.sin(s*2.2+u)*r*.006,k=l+[0,.05,.12][u]*r,S=p-[0,.05,.12][u]*r+A,E=a[u]*r*w;if(!(E<=.2)&&(t.globalAlpha=o.dots*.95,t.fillStyle=c,t.beginPath(),u<2?t.arc(k,S,E,0,Math.PI*2):xt(t,k,S,E),t.fill(),u===2&&w>.6)){t.fillStyle=G(x(this.body,[0,0,0],.35));for(let C=0;C<3;C++){let O=i?0:Math.max(0,Math.sin(s*7-C*.9))*E*.16;t.beginPath(),t.arc(k+(C-1)*E*.34,S-O,E*.1,0,Math.PI*2),t.fill();}}}}if(o.zz>.01){let[l,p]=this.project(.45,.85,.2);t.textAlign="center",t.textBaseline="middle",t.fillStyle=c;for(let n=0;n<3;n++){let a=i?.25+n*.25:(s*.33+n/3)%1,u=l+a*r*.17+Math.sin(a*7+n)*r*.018,w=p-Te(a)*r*.26;t.globalAlpha=o.zz*Math.min(1,a*5)*(1-a)*1.3,t.save(),t.translate(u,w),t.rotate(-0.25+Math.sin(a*5+n)*.22),t.font=`800 ${Math.round(r*(.05+.06*a))}px ui-rounded, system-ui, sans-serif`,t.fillText("z",0,0),t.restore();}}if(o.hearts>.01){let[l,p]=this.project(0,.8,.2),n=x(this.cheek,[1,.3,.45],.55);for(let a=0;a<4;a++){let u=i?.4:(s*.3+a/4)%1,w=a%2?1:-1,A=l+w*r*(.16+.07*(a>>1))+Math.sin(u*6+a)*r*.025,k=p-r*.02-Te(u)*r*.28,S=i?1:Pe(Ee(u/.22)),E=r*(.026+.008*(a%3))*S;t.globalAlpha=o.hearts*(1-Math.max(0,(u-.6)/.4)),t.save(),t.translate(A,k),t.rotate(Math.sin(s*3+a)*.25);let C=t.createLinearGradient(0,-E,0,E);C.addColorStop(0,G(x(n,[1,1,1],.35))),C.addColorStop(1,G(n)),t.fillStyle=C,ge(t,0,0,E),t.fill(),t.restore();}}if(o.waves>.01){t.lineCap="round",t.strokeStyle=c;for(let l of [-1,1]){let[p,n]=this.project(l*1,.12,0);for(let a=0;a<3;a++){let u=i?.3+a*.2:(s*.8+a/3)%1,w=Te(u);t.globalAlpha=o.waves*Math.sin(Math.PI*u)*.9,t.lineWidth=r*(.016-.008*u),t.beginPath();let A=r*(.03+.11*w);l>0?t.arc(p-r*.03,n,A,-0.55,.55):t.arc(p+r*.03,n,A,Math.PI-.55,Math.PI+.55),t.stroke();}}}if(o.bang>.01){let[l,p]=this.project(.55,1,.2),n=i?1:Pe(Ee(this.tau/.35));t.globalAlpha=o.bang,t.fillStyle=c,t.save(),t.translate(l,p),t.rotate(.18),t.scale(n,n),t.beginPath(),t.roundRect(-r*.016,-r*.1,r*.032,r*.07,r*.016),t.arc(0,r*.005,r*.018,0,Math.PI*2),t.fill(),t.restore();}t.restore();}},Ee=e=>e<0?0:e>1?1:e,Te=e=>1-(1-e)*(1-e),Pe=e=>1+(1.9+1)*Math.pow(e-1,3)+1.9*Math.pow(e-1,2);function xt(e,o,t,s){e.ellipse(o,t,s,s*.72,0,0,Math.PI*2),e.moveTo(o-s*.35+s*.45,t-s*.38),e.arc(o-s*.35,t-s*.38,s*.45,0,Math.PI*2),e.moveTo(o+s*.3+s*.5,t-s*.32),e.arc(o+s*.3,t-s*.32,s*.5,0,Math.PI*2);}var Rt=["shape","mood","color","face-color","cheek-color","size","speed","follow-cursor","interactive","shadow","sound"],N=(e,o)=>e===null?o:e!=="false"&&e!=="0",Ke=e=>e==null||e===""?`${M.size}px`:typeof e=="number"||/^\d+(\.\d+)?$/.test(String(e))?`${e}px`:String(e),Ft=`:host{display:inline-block;position:relative;cursor:pointer;width:var(--moodi-size);height:var(--moodi-size);vertical-align:middle;contain:layout paint style;-webkit-tap-highlight-color:transparent}
|
|
24
24
|
:host([interactive="false"]){pointer-events:none}
|
|
25
|
-
canvas{display:block;width:100%;height:100%}
|
|
25
|
+
canvas{position:absolute;inset:0;display:block;width:100%;height:100%}
|
|
26
|
+
slot{display:block;width:100%;height:100%}
|
|
27
|
+
::slotted(img){display:block;width:100%;height:100%;object-fit:contain}
|
|
28
|
+
:host([ready]) slot{display:none}`,zt=typeof HTMLElement>"u"?class{}:HTMLElement,Z=class extends zt{constructor(){super();this.buddy=null;this.onEnter=()=>{this.buddy&&(this.buddy.hovering=true);};this.onLeave=()=>{this.buddy&&(this.buddy.hovering=false);};this.onClick=()=>{this.buddy?.state.interactive&&this.react(Math.random()<.5?"happy":"surprised",{duration:1100});};this.root=this.attachShadow({mode:"open"});let t=document.createElement("style");t.textContent=Ft,this.root.append(t),this.style.setProperty("--moodi-size",Ke(this.getAttribute("size")));}connectedCallback(){if(this.hasAttribute("role")||this.setAttribute("role","img"),!this.buddy){let t=this.buddy=new he;t.onMoodChange=s=>{this.syncLabel(s.mood),this.dispatchEvent(new CustomEvent("moodchange",{detail:s,bubbles:true}));},t.onReady=()=>{this.setAttribute("ready",""),this.dispatchEvent(new Event("ready"));},this.root.append(t.canvas),t.update(this.readState()),this.addEventListener("pointerenter",this.onEnter),this.addEventListener("pointerleave",this.onLeave),this.addEventListener("click",this.onClick);}this.syncLabel(this.buddy.currentMood),this.buddy.connect(),this.io=new IntersectionObserver(([t])=>this.buddy?.setVisible(t.isIntersecting),{rootMargin:"64px"}),this.io.observe(this),this.ro=new ResizeObserver(([t])=>{let s=t.contentRect;this.buddy?.resize(s.width,s.height);}),this.ro.observe(this);}disconnectedCallback(){this.io?.disconnect(),this.ro?.disconnect(),this.buddy&&(this.buddy.canvas.remove(),this.buddy.dispose(),this.buddy=null),this.removeEventListener("pointerenter",this.onEnter),this.removeEventListener("pointerleave",this.onLeave),this.removeEventListener("click",this.onClick);}attributeChangedCallback(t,s,i){s!==i&&(t==="size"&&(this.style.setProperty("--moodi-size",Ke(i)),s!==null&&this.buddy?.bump()),t!=="aria-label"&&(this.buddy?.update(this.readState()),t==="mood"&&this.buddy&&this.syncLabel(this.buddy.currentMood)));}readState(){let t=this.getAttribute("shape"),s=this.getAttribute("mood"),i=parseFloat(this.getAttribute("speed")??"");return {shape:ce(t)?t:M.shape,mood:de(s)?s:M.mood,color:this.getAttribute("color")||M.color,faceColor:this.getAttribute("face-color")||void 0,cheekColor:this.getAttribute("cheek-color")||void 0,speed:Number.isFinite(i)?Math.max(0,i):M.speed,followCursor:N(this.getAttribute("follow-cursor"),M.followCursor),interactive:N(this.getAttribute("interactive"),M.interactive),shadow:N(this.getAttribute("shadow"),M.shadow),sound:N(this.getAttribute("sound"),M.sound)}}syncLabel(t){let s=this.getAttribute("aria-label");s&&!s.startsWith("moodi: ")||this.setAttribute("aria-label",`moodi: ${t}`);}react(t,s={}){this.buddy?.react(t,s.duration??1200);}get shape(){let t=this.getAttribute("shape");return ce(t)?t:M.shape}set shape(t){this.setAttribute("shape",t);}get mood(){let t=this.getAttribute("mood");return de(t)?t:M.mood}set mood(t){this.setAttribute("mood",t);}get color(){return this.getAttribute("color")||M.color}set color(t){this.setAttribute("color",t);}get faceColor(){return this.getAttribute("face-color")||void 0}set faceColor(t){t?this.setAttribute("face-color",t):this.removeAttribute("face-color");}get cheekColor(){return this.getAttribute("cheek-color")||void 0}set cheekColor(t){t?this.setAttribute("cheek-color",t):this.removeAttribute("cheek-color");}get size(){let t=this.getAttribute("size");return t?/^\d+(\.\d+)?$/.test(t)?Number(t):t:M.size}set size(t){this.setAttribute("size",String(t));}get speed(){return this.readState().speed}set speed(t){this.setAttribute("speed",String(t));}get followCursor(){return N(this.getAttribute("follow-cursor"),M.followCursor)}set followCursor(t){this.setAttribute("follow-cursor",String(t));}get interactive(){return N(this.getAttribute("interactive"),M.interactive)}set interactive(t){this.setAttribute("interactive",String(t));}get shadow(){return N(this.getAttribute("shadow"),M.shadow)}set shadow(t){this.setAttribute("shadow",String(t));}get sound(){return N(this.getAttribute("sound"),M.sound)}set sound(t){this.setAttribute("sound",String(t));}};Z.observedAttributes=Rt;function Ue(e="moodi-buddy"){typeof window>"u"||!("customElements"in window)||customElements.get(e)||customElements.define(e,class extends Z{});}Ue();exports.DEFAULTS=M;exports.MOODS=Ze;exports.MOOD_INFO=Bt;exports.MoodiBuddyElement=Z;exports.PALETTES=Ot;exports.SHAPES=Je;exports.SHAPE_LABELS=Lt;exports.defineMoodi=Ue;exports.isMood=de;exports.isShape=ce;
|
package/dist/index.d.cts
CHANGED
|
@@ -21,6 +21,8 @@ interface MoodiProps {
|
|
|
21
21
|
interactive?: boolean;
|
|
22
22
|
/** Soft ground shadow. Default `true`. */
|
|
23
23
|
shadow?: boolean;
|
|
24
|
+
/** A short sound effect on every mood change (and when it first appears). Default `false`. */
|
|
25
|
+
sound?: boolean;
|
|
24
26
|
}
|
|
25
27
|
interface MoodChangeDetail {
|
|
26
28
|
mood: Mood;
|
|
@@ -39,6 +41,7 @@ declare const DEFAULTS: {
|
|
|
39
41
|
followCursor: boolean;
|
|
40
42
|
interactive: boolean;
|
|
41
43
|
shadow: boolean;
|
|
44
|
+
sound: boolean;
|
|
42
45
|
};
|
|
43
46
|
declare const PALETTES: Readonly<Record<string, string>>;
|
|
44
47
|
declare const MOOD_INFO: Readonly<Record<Mood, {
|
|
@@ -51,7 +54,7 @@ declare const isMood: (v: unknown) => v is Mood;
|
|
|
51
54
|
|
|
52
55
|
declare const Base: typeof HTMLElement;
|
|
53
56
|
declare class MoodiBuddyElement extends Base {
|
|
54
|
-
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow"];
|
|
57
|
+
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow", "sound"];
|
|
55
58
|
private buddy;
|
|
56
59
|
private io?;
|
|
57
60
|
private ro?;
|
|
@@ -89,6 +92,8 @@ declare class MoodiBuddyElement extends Base {
|
|
|
89
92
|
set interactive(v: boolean);
|
|
90
93
|
get shadow(): boolean;
|
|
91
94
|
set shadow(v: boolean);
|
|
95
|
+
get sound(): boolean;
|
|
96
|
+
set sound(v: boolean);
|
|
92
97
|
}
|
|
93
98
|
/** Registers `<moodi-buddy>`. Safe to call more than once, no-op on the server. */
|
|
94
99
|
declare function defineMoodi(tag?: string): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ interface MoodiProps {
|
|
|
21
21
|
interactive?: boolean;
|
|
22
22
|
/** Soft ground shadow. Default `true`. */
|
|
23
23
|
shadow?: boolean;
|
|
24
|
+
/** A short sound effect on every mood change (and when it first appears). Default `false`. */
|
|
25
|
+
sound?: boolean;
|
|
24
26
|
}
|
|
25
27
|
interface MoodChangeDetail {
|
|
26
28
|
mood: Mood;
|
|
@@ -39,6 +41,7 @@ declare const DEFAULTS: {
|
|
|
39
41
|
followCursor: boolean;
|
|
40
42
|
interactive: boolean;
|
|
41
43
|
shadow: boolean;
|
|
44
|
+
sound: boolean;
|
|
42
45
|
};
|
|
43
46
|
declare const PALETTES: Readonly<Record<string, string>>;
|
|
44
47
|
declare const MOOD_INFO: Readonly<Record<Mood, {
|
|
@@ -51,7 +54,7 @@ declare const isMood: (v: unknown) => v is Mood;
|
|
|
51
54
|
|
|
52
55
|
declare const Base: typeof HTMLElement;
|
|
53
56
|
declare class MoodiBuddyElement extends Base {
|
|
54
|
-
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow"];
|
|
57
|
+
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow", "sound"];
|
|
55
58
|
private buddy;
|
|
56
59
|
private io?;
|
|
57
60
|
private ro?;
|
|
@@ -89,6 +92,8 @@ declare class MoodiBuddyElement extends Base {
|
|
|
89
92
|
set interactive(v: boolean);
|
|
90
93
|
get shadow(): boolean;
|
|
91
94
|
set shadow(v: boolean);
|
|
95
|
+
get sound(): boolean;
|
|
96
|
+
set sound(v: boolean);
|
|
92
97
|
}
|
|
93
98
|
/** Registers `<moodi-buddy>`. Safe to call more than once, no-op on the server. */
|
|
94
99
|
declare function defineMoodi(tag?: string): void;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {Scene,PerspectiveCamera,Group,Vector3,BufferGeometry,CanvasTexture,SRGBColorSpace,LinearMipmapLinearFilter,Mesh,HemisphereLight,DirectionalLight,MeshStandardMaterial,BufferAttribute,WebGLRenderer,SphereGeometry}from'three';var Ne=["ghost","pebble","blob","drop","bean","cube"],Xe=["idle","thinking","loading","listening","speaking","happy","surprised","sad","error","sleeping","love"],M={shape:"ghost",mood:"idle",color:"#6c5ce7",size:160,speed:1,followCursor:true,interactive:true,shadow:true},wt={Iris:"#6c5ce7",Peach:"#f2a07b",Sage:"#8fb996",Sky:"#8ec5e8",Lilac:"#b9a3e3",Butter:"#f3d36b",Coral:"#f07167",Ink:"#2b2d42",Cloud:"#ecebe8"},St={idle:{label:"Idle",description:"Breathing softly. Blinks now and then."},thinking:{label:"Thinking",description:"Eyes drift up. Little thoughts pop."},loading:{label:"Loading",description:"Half-lidded, wobbling patiently."},listening:{label:"Listening",description:"All eyes. Nods along to every word."},speaking:{label:"Speaking",description:"Chatty mouth, bouncing to the rhythm."},happy:{label:"Happy",description:"Bounces with joy. Cheeks glow."},surprised:{label:"Surprised",description:"Wide eyes, tiny o. Did not see that coming."},sad:{label:"Sad",description:"Slumps a little. Needs a minute."},error:{label:"Error",description:"Something broke. Shakes it off."},sleeping:{label:"Sleeping",description:"Deep, slow breaths. Do not disturb."},love:{label:"Love",description:"Heart eyes, floating on air."}},Ct={pebble:"Pebble",blob:"Blob",drop:"Drop",bean:"Bean",cube:"Cube",ghost:"Ghost"},re=t=>Ne.includes(t),ae=t=>Xe.includes(t);var z=null;function _(t,o){if(!t)return o;let e=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(t.trim());if(e)return Se(e[1]);if(z||(z=document.createElement("canvas").getContext("2d")),!z)return o;z.fillStyle="#010203",z.fillStyle=t;let s=String(z.fillStyle);if(s==="#010203"&&t.trim()!=="#010203")return o;if(s.startsWith("#"))return Se(s.slice(1));z.canvas.width=z.canvas.height=1,z.clearRect(0,0,1,1),z.fillRect(0,0,1,1);let i=z.getImageData(0,0,1,1).data;return [i[0]/255,i[1]/255,i[2]/255]}function Se(t){t.length===3&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]);let o=parseInt(t,16);return [(o>>16&255)/255,(o>>8&255)/255,(o&255)/255]}var O=t=>`rgb(${Math.round(t[0]*255)},${Math.round(t[1]*255)},${Math.round(t[2]*255)})`,P=(t,o,e)=>[t[0]+(o[0]-t[0])*e,t[1]+(o[1]-t[1])*e,t[2]+(o[2]-t[2])*e],ne=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4),he=t=>.2126*ne(t[0])+.7152*ne(t[1])+.0722*ne(t[2]),Ve=[.07,.06,.08],He=[1,.97,.93],$e=[1,.42,.48],U=t=>he(t)>.06?P(Ve,t,.06):He,J=t=>P(t,$e,.78);var $=["eyeOpen","eyeSize","lookX","lookY","lidCover","lidTilt","happyEye","closedEye","xEye","heartEye","mouthW","mouthCurve","mouthOpen","mouthRound","mouthSkew","mouthWave","blush"],L={eyeOpen:1,eyeSize:1,lookX:0,lookY:0,lidCover:0,lidTilt:0,happyEye:0,closedEye:0,xEye:0,heartEye:0,mouthW:.1,mouthCurve:.35,mouthOpen:0,mouthRound:0,mouthSkew:0,mouthWave:0,blush:.45},Z={idle:{...L},thinking:{...L,eyeOpen:.92,lookX:.7,lookY:-0.8,lidCover:.12,mouthW:.065,mouthCurve:0,mouthSkew:.8,blush:.3},loading:{...L,lidCover:.48,mouthW:.06,mouthCurve:.12,blush:.3},listening:{...L,eyeOpen:1.12,eyeSize:1.16,mouthW:.06,mouthCurve:.2,mouthOpen:.02},speaking:{...L,mouthW:.09,mouthCurve:.25},happy:{...L,happyEye:1,mouthW:.13,mouthCurve:1,mouthOpen:.08,blush:1},surprised:{...L,eyeOpen:1.25,eyeSize:1.22,mouthW:.06,mouthCurve:0,mouthOpen:.12,mouthRound:1,blush:.35},sad:{...L,eyeOpen:.95,lidCover:.32,lidTilt:.5,lookY:.45,mouthW:.08,mouthCurve:-0.75,blush:.2},error:{...L,xEye:1,mouthW:.1,mouthCurve:-0.2,mouthWave:1,blush:.15},sleeping:{...L,closedEye:1,mouthW:.04,mouthCurve:0,mouthOpen:.035,mouthRound:.7,blush:.55},love:{...L,heartEye:1,mouthW:.11,mouthCurve:.9,mouthOpen:.03,blush:1}},Ce={idle:1,thinking:.15,loading:0,listening:1,speaking:.9,happy:.6,surprised:.8,sad:.3,error:.3,sleeping:0,love:.7},de=t=>({...t}),je=.31,Ke=-0.04,Ue=.098,le=.128,Je=.2,ce=.05;function Te(t,o,e,s,i,a,l,d,u,p){let r=t.canvas.width;t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,r,r),t.setTransform(r/2,0,0,r/2,r/2,r/2),t.lineCap="round",t.lineJoin="round";let h=o.lookX*.075,c=o.lookY*.06;if(o.blush>.01){let m=r*.018;t.save(),"filter"in t&&(t.filter=`blur(${m}px)`);for(let y of [-1,1]){let v=y*.52+h*.4,n=.16+c*.4,f=t.createRadialGradient(v,n,0,v,n,.16);f.addColorStop(0,i),f.addColorStop(1,"rgba(0,0,0,0)"),t.globalAlpha=Math.min(1,o.blush)*.62,t.fillStyle=f,t.beginPath(),t.ellipse(v,n,.17,.12,0,0,Math.PI*2),t.fill();}t.restore();let b=Math.min(1,Math.max(0,(o.blush-.6)/.4));if(b>.01){t.save(),t.globalAlpha=b*.55,t.strokeStyle=i,t.lineWidth=.016,t.lineCap="round";for(let y of [-1,1]){let v=y*.52+h*.4,n=.16+c*.4;for(let f=-1;f<=1;f++){let F=v+f*.045;t.beginPath(),t.moveTo(F+.018,n-.035),t.lineTo(F-.018,n+.035),t.stroke();}}t.restore();}}t.fillStyle=s,t.strokeStyle=s,t.lineWidth=ce;let A=1-Math.min(1,o.happyEye+o.closedEye+o.xEye+o.heartEye);for(let m of [-1,1]){let b=m*je+h,y=Ke+c,v=Ue*o.eyeSize;if(A>.01){t.globalAlpha=A;let n=Math.max(.06,o.eyeOpen*e),f=le*o.eyeSize*n;if(t.save(),o.lidCover>.01||o.lidTilt>.01){let W=y-le*o.eyeSize+2*le*o.eyeSize*o.lidCover;t.translate(b,W),t.rotate(m*o.lidTilt*.6),t.beginPath(),t.rect(-1,0,2,2),t.clip(),t.rotate(-m*o.lidTilt*.6),t.translate(-b,-W);}t.shadowColor=s,t.shadowBlur=r*.035*a;let F=t.createRadialGradient(b,y-f*.2,0,b,y,Math.max(v,f));if(F.addColorStop(0,l),F.addColorStop(1,s),t.fillStyle=F,t.beginPath(),t.ellipse(b,y,v,f,0,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.fillStyle=s,n>.35){let x=Math.max(-1,Math.min(1,o.lookX)),W=Math.max(-1,Math.min(1,o.lookY)),ie=d==="#fff"?.3:.34;t.globalAlpha=A*Math.min(1,(n-.35)*3),t.fillStyle=d,t.beginPath(),t.arc(b-v*.1+x*v*.5,y-f*.16+W*f*.42,v*ie,0,Math.PI*2),t.fill(),t.fillStyle=s;}t.restore();}if(t.shadowColor=s,t.shadowBlur=r*.03*a,o.happyEye>.01){let n=.6+.4*o.happyEye;t.globalAlpha=Math.min(1,o.happyEye),t.beginPath(),t.arc(b,y+.045,v*1.15*n,Math.PI*1.12,Math.PI*1.88),t.stroke();}if(o.closedEye>.01){let n=.6+.4*o.closedEye;t.globalAlpha=Math.min(1,o.closedEye),t.beginPath(),t.moveTo(b-v*1.15*n,y+.01),t.quadraticCurveTo(b,y+.07*n,b+v*1.15*n,y+.01),t.stroke();}if(o.xEye>.01){let n=v*.95*(.6+.4*o.xEye);t.globalAlpha=Math.min(1,o.xEye),t.beginPath(),t.moveTo(b-n,y-n),t.lineTo(b+n,y+n),t.moveTo(b+n,y-n),t.lineTo(b-n,y+n),t.stroke();}o.heartEye>.01&&(t.globalAlpha=Math.min(1,o.heartEye),ue(t,b,y,v*1.35*(.5+.5*o.heartEye)),t.fill());}t.shadowBlur=0;let k=h*.6+o.mouthSkew*.07,w=Je+c*.6,T=Math.min(1,Math.max(0,o.mouthRound)),S=o.mouthW,R=t.createLinearGradient(k-S-.02,w,k+S+.02,w);R.addColorStop(0,p),R.addColorStop(.5,l),R.addColorStop(1,p),t.shadowColor=s,t.shadowBlur=r*.02*a;let B=(m,b,y,v)=>{t.fillStyle=u,t.fill(m),t.shadowBlur=0,b>.02&&(t.save(),t.clip(m),t.fillStyle=i,t.globalAlpha*=Math.min(1,(b-.02)*12),t.beginPath(),t.ellipse(k,y,v,b*.55,0,0,Math.PI*2),t.fill(),t.restore()),t.strokeStyle=R;let n=t.lineWidth;t.lineWidth=n*.6,t.stroke(m),t.lineWidth=n,t.shadowBlur=r*.02*a;};if(T<.99){t.globalAlpha=1-T;let m=18,b=f=>o.mouthCurve*.075*(1-f*f),y=(f,F)=>{let x=f/m*2-1,W=k+x*S,ie=w+b(x)+o.mouthSkew*x*.03+o.mouthWave*.018*Math.sin((x+1)*Math.PI*2)+F*(1-x*x);return [W,ie]},v=Math.max(0,o.mouthOpen),n=new Path2D;for(let f=0;f<=m;f++){let[F,x]=y(f,-v*.15);f?n.lineTo(F,x):n.moveTo(F,x);}if(v>.003){for(let f=m;f>=0;f--){let[F,x]=y(f,v);n.lineTo(F,x);}n.closePath(),B(n,v,y(m/2,v)[1],S*.55);}else t.strokeStyle=R,t.stroke(n);}if(T>.01){t.globalAlpha=T;let m=Math.max(.02,o.mouthOpen*.55+.01),b=new Path2D;b.ellipse(k,w+.01,m*.85,m,0,0,Math.PI*2),t.lineWidth=ce*.8,B(b,o.mouthOpen,w+.01+m*.8,m*.6),t.lineWidth=ce;}t.shadowBlur=0,t.globalAlpha=1;}function ue(t,o,e,s){t.beginPath(),t.moveTo(o,e+s*.85),t.bezierCurveTo(o-s*1.25,e+s*.05,o-s*.75,e-s*.95,o,e-s*.35),t.bezierCurveTo(o+s*.75,e-s*.95,o+s*1.25,e+s*.05,o,e+s*.85),t.closePath();}var te=()=>({y:0,rx:0,ry:0,rz:0,st:0,mouth:0,lookX:0,lookY:0}),{sin:g,cos:Ze,abs:Qe,max:Q,exp:et,PI:ee}=Math;function pe(t,o,e,s){switch(s.y=s.rx=s.ry=s.rz=s.st=s.mouth=s.lookX=s.lookY=0,t){case "idle":{let i=g(o*1.6);s.y=.015*i,s.st=.018*i;break}case "thinking":{s.rz=.07*g(o*1.1),s.y=.02*g(o*2.2),s.ry=.15,s.rx=-0.06;break}case "loading":{let i=o*2.4;s.rx=.12*g(i),s.rz=.12*Ze(i),s.ry=.22*g(i),s.y=.02*g(i*2),s.lookX=.55*g(i);break}case "listening":{let i=Q(0,g(o*3.2));s.rx=.09*i*i;let a=Qe(g(o*7.1)*.5+g(o*11.3)*.3+g(o*3.7)*.2);s.st=.03*a;break}case "speaking":{let i=Q(0,g(o*9)*.6+g(o*14.3)*.4)*(.65+.35*g(o*1.7));s.mouth=.11*i,s.st=.04*i,s.rz=.03*g(o*2.1);break}case "happy":{let i=o*1.35%1;s.y=.2*g(ee*i);let a=Q(0,1-i/.14)+Q(0,(i-.9)/.1);s.st=.08*g(ee*i)-.14*a,s.rz=.05*g(o*1.35*ee);break}case "surprised":{if(e<.45){let i=g(ee*e/.45);s.y=.16*i,s.st=.12*i;}s.rz=.01*g(o*38)*et(-e*2),s.rx=-0.06;break}case "sad":{let i=g(o*1.1);s.y=-0.06,s.rx=.2,s.st=-0.04+.015*i,s.rz=-0.04;break}case "error":{let i=e%2.8;if(i<.55){let a=1-i/.55;s.ry=.26*g(i*42)*a,s.rz=.05*g(i*42+1)*a;}s.st=-0.02;break}case "sleeping":{let i=g(o*1);s.st=.04*i,s.y=-0.03,s.rz=.1,s.rx=.1;break}case "love":{s.y=.07+.06*g(o*1.6),s.rz=.05*g(o*.8),s.st=.015*g(o*1.6+1);break}}return s}function Ae(t,o,e,s){for(let i of Object.keys(s))s[i]=t[i]+(o[i]-t[i])*e;return s}var Ee={thinking:"dots",sleeping:"zz",love:"hearts",listening:"waves",surprised:"bang"};var X=new Set,E=null,q=0,Y=0,oe=0,be,Pe=false,N="idle",I={x:0,y:0,lastMove:-1e9,known:false},G={reducedMotion:false};function ot(){if(Pe)return;Pe=true,addEventListener("pointermove",o=>{I.x=o.clientX,I.y=o.clientY,I.lastMove=performance.now(),I.known=true;},{passive:true}),document.addEventListener("visibilitychange",D);let t=matchMedia("(prefers-reduced-motion: reduce)");G.reducedMotion=t.matches,t.addEventListener?.("change",o=>G.reducedMotion=o.matches);}function Fe(t,o){return E||(E=new WebGLRenderer({antialias:true,alpha:true,powerPreference:"low-power",preserveDrawingBuffer:true}),E.setPixelRatio(1),E.setClearColor(0,0),E.setScissorTest(true),E.domElement.addEventListener("webglcontextlost",e=>{e.preventDefault(),E?.dispose(),E=null,q=Y=0,N="idle";})),(t>q||o>Y)&&(q=Math.max(q,t),Y=Math.max(Y,o),E.setSize(q,Y,false)),E}var Re=()=>Y;function Be(t,o){if(N==="ready")return true;if(N==="idle"&&E){N="compiling";let e=E;e.compileAsync(t,o).catch(()=>{}).then(()=>{e===E&&(N="ready"),D();});}return false}function ze(t){ot(),clearTimeout(be),X.add(t),D();}function Oe(t){X.delete(t),X.size===0&&(clearTimeout(be),be=setTimeout(()=>{X.size||!E||(E.dispose(),E.forceContextLoss(),E=null,q=Y=0,N="idle");},1500));}function D(){if(!oe){for(let t of X)if(t.active){oe=requestAnimationFrame(Le);return}}}var st=10,xe=new WeakMap,me=0;function Le(t){oe=0;let o=[...X].filter(a=>a.active);if(!o.length)return;let e=performance.now(),s=o.length,i=0;for(;i<s;i++){let a=o[(me+i)%s],l=xe.get(a)??t-16;if(xe.set(a,t),a.tick(Math.min(.05,Math.max(0,(t-l)/1e3)),t),performance.now()-e>st){i++;break}}me=(me+i)%s,oe=requestAnimationFrame(Le);}var j={pebble:{faceY:.02,faceScale:1.3,animated:false},blob:{faceY:.02,faceScale:1.28,animated:true},drop:{faceY:-0.24,faceScale:1.22,animated:false},bean:{faceY:.14,faceScale:.92,animated:false},cube:{faceY:0,faceScale:1.22,animated:false},ghost:{faceY:.22,faceScale:1.24,animated:true}};function Ie(t){let o=new SphereGeometry(1,t*2,Math.round(t*1.5)),e=o.getAttribute("position"),s=o.getIndex(),i=new Map,a=[],l=new Uint32Array(e.count);for(let r=0;r<e.count;r++){let h=e.getX(r),c=e.getY(r),C=e.getZ(r),A=`${Math.round(h*1e4)},${Math.round(c*1e4)},${Math.round(C*1e4)}`,k=i.get(A);if(k===void 0){k=a.length/3,i.set(A,k);let w=Math.hypot(h,c,C)||1;a.push(h/w,c/w,C/w);}l[r]=k;}let d=[];for(let r=0;r<s.count;r+=3){let h=l[s.getX(r)],c=l[s.getX(r+1)],C=l[s.getX(r+2)];h!==c&&c!==C&&h!==C&&d.push(h,c,C);}o.dispose();let u=new BufferGeometry,p=new Float32Array(a);return u.setAttribute("position",new BufferAttribute(new Float32Array(p),3)),u.setIndex(d),{geometry:u,dirs:p}}var nt=(t,o,e)=>{let s=Math.min(1,Math.max(0,(e-t)/(o-t)));return s*s*(3-2*s)};function fe(t,o,e,s,i,a,l){let d=o,u=e,p=s;switch(t){case "pebble":{d=o*1.1+.04*e,u=e*.92*(.95+.05*Math.tanh(3*e))+.03*o,p=s*.94;break}case "blob":{let r=.97+.045*Math.sin(2.1*o+1.3*i)*Math.sin(2.4*e+1.7*i+1)+.035*Math.sin(2.7*s+2.3*o+1.1*i);d=o*r,u=e*r,p=s*r;break}case "drop":{let r=Math.max(e,0),h=1-.8*Math.pow(r,1.6);d=o*h*.98+.1*r*r*r,u=(e<0?e*.9:e*1.32)-.12,p=s*h*.98;break}case "bean":{let r=e*.78+.42*Math.tanh(e*3);d=(o*.8+.14*(r*r/1.44)-.07)*.9,u=r*.88,p=s*.8*.9;break}case "cube":{let h=Math.pow(Math.pow(Math.abs(o),4.5)+Math.pow(Math.abs(e),4.5)+Math.pow(Math.abs(s),4.5),-0.2222222222222222);d=o*h*.84,u=e*h*.82,p=s*h*.84;break}case "ghost":{if(e>=0)d=o,u=e,p=s;else {let h=(Math.acos(Math.max(-1,e))-Math.PI/2)/(Math.PI/2),c=Math.hypot(o,s)||1,C=o/c,A=s/c,k=Math.atan2(s,o),w=.085*Math.sin(5*k+2.2*i),T=1.04,S=.13,R=-1.05+w,B;if(h<.45){let m=h/.45;B=1+.04*m,u=-m*(1.05-S)+w*nt(.45,1,m);}else if(h<.75){let m=(h-.45)/.3*(Math.PI/2);B=T-S+S*Math.cos(m),u=R+S-S*Math.sin(m);}else {let m=(h-.75)/.25;B=(T-S)*(1-m),u=R+.22*m*m*(3-2*m);}d=C*B,p=A*B;}d*=.9,u=(u+.1)*.9,p*=.9;break}}a[l]=d,a[l+1]=u,a[l+2]=p;}var ve=-0.85,De=new Map;function gt(t){let o=De.get(t);if(!o){let{geometry:e,dirs:s}=Ie(t);o={dirs:s,index:new Uint32Array(e.getIndex().array)},e.dispose(),De.set(t,o);}return o}var V=(t,o)=>1-Math.exp(-t*o),We=t=>1-Math.pow(1-t,3),se=class{constructor(){this.scene=new Scene;this.camera=new PerspectiveCamera(30,1,.1,50);this.group=new Group;this.v=new Vector3;this.geometry=new BufferGeometry;this.faceCanvas=document.createElement("canvas");this.uniforms={uFace:{value:null},uFaceY:{value:0},uFaceScale:{value:1.5}};this.state={...M};this.visible=false;this.connected=false;this.w=0;this.h=0;this.t=Math.random()*100;this.shapeFrom=M.shape;this.shapeTo=M.shape;this.morph=1;this.shapeDirty=true;this.shown=M.mood;this.prevShown=M.mood;this.tau=10;this.prevTau=10;this.blend=1;this.reaction=null;this.pose=te();this.poseA=te();this.poseB=te();this.face=de(Z.idle);this.target=de(Z.idle);this.drawn=new Float32Array($.length+7).fill(NaN);this.blink=1;this.blinkT=-1;this.nextBlink=2+Math.random()*3;this.body=_(M.color,[.95,.63,.48]);this.bodyT=this.body;this.faceC=U(this.body);this.faceCT=this.faceC;this.cheek=J(this.body);this.cheekT=this.cheek;this.look={x:0,y:0};this.wander={x:0,y:0,next:0};this.squash=0;this.squashV=0;this.pop=.6;this.popV=0;this.hover=0;this.hovering=false;this.overlay={dots:0,zz:0,hearts:0,waves:0,bang:0};this.needsFrame=true;this.css=[0,0];this.quality=1;this.cost=0;this.costFrames=0;this.lastFrame=[];this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),this.faceCtx=this.faceCanvas.getContext("2d"),this.faceCanvas.width=this.faceCanvas.height=256,this.faceTex=new CanvasTexture(this.faceCanvas),this.faceTex.colorSpace=SRGBColorSpace,this.faceTex.minFilter=LinearMipmapLinearFilter,this.faceTex.anisotropy=4,this.uniforms.uFace.value=this.faceTex,this.setSegments(24),this.material=this.createMaterial(),this.mesh=new Mesh(this.geometry,this.material),this.mesh.frustumCulled=false,this.mesh.position.y=-ve,this.group.add(this.mesh),this.scene.add(this.group),this.scene.add(new HemisphereLight(16774120,7234140,1.7));let o=new DirectionalLight(16777215,1.9);o.position.set(2.5,3.5,4);let e=new DirectionalLight(16770770,1.4);e.position.set(-3.5,2,-3);let s=new DirectionalLight(16773350,.45);s.position.set(-2,-1,3),this.scene.add(o,e,s),this.camera.position.set(0,.3,6.6),this.camera.lookAt(0,.12,0);}get active(){return this.visible&&this.connected&&this.w>0}createMaterial(){let o=new MeshStandardMaterial({roughness:.62,metalness:0});return o.color.setRGB(...this.body,SRGBColorSpace),o.customProgramCacheKey=()=>"moodi-1",o.onBeforeCompile=e=>{Object.assign(e.uniforms,this.uniforms),e.vertexShader=e.vertexShader.replace("#include <common>",`#include <common>
|
|
1
|
+
import {Scene,PerspectiveCamera,Group,Vector3,BufferGeometry,CanvasTexture,SRGBColorSpace,LinearMipmapLinearFilter,Mesh,HemisphereLight,DirectionalLight,MeshStandardMaterial,BufferAttribute,WebGLRenderer,SphereGeometry}from'three';var Je=["ghost","pebble","blob","drop","bean","cube"],Ze=["idle","thinking","loading","listening","speaking","happy","surprised","sad","error","sleeping","love"],M={shape:"ghost",mood:"idle",color:"#6c5ce7",size:160,speed:1,followCursor:true,interactive:true,shadow:true,sound:false},Ot={Iris:"#6c5ce7",Peach:"#f2a07b",Sage:"#8fb996",Sky:"#8ec5e8",Lilac:"#b9a3e3",Butter:"#f3d36b",Coral:"#f07167",Ink:"#2b2d42",Cloud:"#ecebe8"},Bt={idle:{label:"Idle",description:"Breathing softly. Blinks now and then."},thinking:{label:"Thinking",description:"Eyes drift up. Little thoughts pop."},loading:{label:"Loading",description:"Half-lidded, wobbling patiently."},listening:{label:"Listening",description:"All eyes. Nods along to every word."},speaking:{label:"Speaking",description:"Chatty mouth, bouncing to the rhythm."},happy:{label:"Happy",description:"Bounces with joy. Cheeks glow."},surprised:{label:"Surprised",description:"Wide eyes, tiny o. Did not see that coming."},sad:{label:"Sad",description:"Slumps a little. Needs a minute."},error:{label:"Error",description:"Something broke. Shakes it off."},sleeping:{label:"Sleeping",description:"Deep, slow breaths. Do not disturb."},love:{label:"Love",description:"Heart eyes, floating on air."}},Lt={pebble:"Pebble",blob:"Blob",drop:"Drop",bean:"Bean",cube:"Cube",ghost:"Ghost"},ce=e=>Je.includes(e),de=e=>Ze.includes(e);var L=null;function X(e,o){if(!e)return o;let t=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(e.trim());if(t)return xe(t[1]);if(L||(L=document.createElement("canvas").getContext("2d")),!L)return o;L.fillStyle="#010203",L.fillStyle=e;let s=String(L.fillStyle);if(s==="#010203"&&e.trim()!=="#010203")return o;if(s.startsWith("#"))return xe(s.slice(1));L.canvas.width=L.canvas.height=1,L.clearRect(0,0,1,1),L.fillRect(0,0,1,1);let i=L.getImageData(0,0,1,1).data;return [i[0]/255,i[1]/255,i[2]/255]}function xe(e){e.length===3&&(e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);let o=parseInt(e,16);return [(o>>16&255)/255,(o>>8&255)/255,(o&255)/255]}var G=e=>`rgb(${Math.round(e[0]*255)},${Math.round(e[1]*255)},${Math.round(e[2]*255)})`,x=(e,o,t)=>[e[0]+(o[0]-e[0])*t,e[1]+(o[1]-e[1])*t,e[2]+(o[2]-e[2])*t],ue=e=>e<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4),pe=e=>.2126*ue(e[0])+.7152*ue(e[1])+.0722*ue(e[2]),Qe=[.07,.06,.08],et=[1,.97,.93],tt=[1,.42,.48],Q=e=>pe(e)>.06?x(Qe,e,.06):et,ee=e=>x(e,tt,.78);var U=["eyeOpen","eyeSize","lookX","lookY","lidCover","lidTilt","happyEye","closedEye","xEye","heartEye","mouthW","mouthCurve","mouthOpen","mouthRound","mouthSkew","mouthWave","blush"],q={eyeOpen:1,eyeSize:1,lookX:0,lookY:0,lidCover:0,lidTilt:0,happyEye:0,closedEye:0,xEye:0,heartEye:0,mouthW:.1,mouthCurve:.35,mouthOpen:0,mouthRound:0,mouthSkew:0,mouthWave:0,blush:.45},te={idle:{...q},thinking:{...q,eyeOpen:.92,lookX:.7,lookY:-0.8,lidCover:.12,mouthW:.065,mouthCurve:0,mouthSkew:.8,blush:.3},loading:{...q,lidCover:.48,mouthW:.06,mouthCurve:.12,blush:.3},listening:{...q,eyeOpen:1.12,eyeSize:1.16,mouthW:.06,mouthCurve:.2,mouthOpen:.02},speaking:{...q,mouthW:.09,mouthCurve:.25},happy:{...q,happyEye:1,mouthW:.13,mouthCurve:1,mouthOpen:.08,blush:1},surprised:{...q,eyeOpen:1.25,eyeSize:1.22,mouthW:.06,mouthCurve:0,mouthOpen:.12,mouthRound:1,blush:.35},sad:{...q,eyeOpen:.95,lidCover:.32,lidTilt:.5,lookY:.45,mouthW:.08,mouthCurve:-0.75,blush:.2},error:{...q,xEye:1,mouthW:.1,mouthCurve:-0.2,mouthWave:1,blush:.15},sleeping:{...q,closedEye:1,mouthW:.04,mouthCurve:0,mouthOpen:.035,mouthRound:.7,blush:.55},love:{...q,heartEye:1,mouthW:.11,mouthCurve:.9,mouthOpen:.03,blush:1}},Fe={idle:1,thinking:.15,loading:0,listening:1,speaking:.9,happy:.6,surprised:.8,sad:.3,error:.3,sleeping:0,love:.7},be=e=>({...e}),ot=.31,st=-0.04,it=.098,me=.128,nt=.2,fe=.05;function ze(e,o,t,s,i,r,h,c,l,p){let n=e.canvas.width;e.setTransform(1,0,0,1,0,0),e.clearRect(0,0,n,n),e.setTransform(n/2,0,0,n/2,n/2,n/2),e.lineCap="round",e.lineJoin="round";let a=o.lookX*.075,u=o.lookY*.06;if(o.blush>.01){let m=n*.018;e.save(),"filter"in e&&(e.filter=`blur(${m}px)`);for(let g of [-1,1]){let b=g*.52+a,d=.16+u,y=e.createRadialGradient(b,d,0,b,d,.16);y.addColorStop(0,i),y.addColorStop(1,"rgba(0,0,0,0)"),e.globalAlpha=Math.min(1,o.blush)*.62,e.fillStyle=y,e.beginPath(),e.ellipse(b,d,.17,.12,0,0,Math.PI*2),e.fill();}e.restore();let f=Math.min(1,Math.max(0,(o.blush-.6)/.4));if(f>.01){e.save(),e.globalAlpha=f*.55,e.strokeStyle=i,e.lineWidth=.016,e.lineCap="round";for(let g of [-1,1]){let b=g*.52+a,d=.16+u;for(let y=-1;y<=1;y++){let P=b+y*.045;e.beginPath(),e.moveTo(P+.018,d-.035),e.lineTo(P-.018,d+.035),e.stroke();}}e.restore();}}e.fillStyle=s,e.strokeStyle=s,e.lineWidth=fe;let w=Math.min(1,o.happyEye+o.closedEye+o.xEye+o.heartEye),A=1-w;for(let m of [-1,1]){let f=m*ot+a,g=st+u,b=it*o.eyeSize;if(A>.01){e.globalAlpha=A;let d=Math.max(.06,o.eyeOpen*t),y=me*o.eyeSize*d;if(e.save(),o.lidCover>.01||o.lidTilt>.01){let W=g-me*o.eyeSize+2*me*o.eyeSize*o.lidCover;e.translate(f,W),e.rotate(m*o.lidTilt*.6),e.beginPath(),e.rect(-1,0,2,2),e.clip(),e.rotate(-m*o.lidTilt*.6),e.translate(-f,-W);}r>.5&&Re(e,f,g,b*2.1,y*1.9,s,.35*A);let P=e.createRadialGradient(f,g-y*.2,0,f,g,Math.max(b,y));if(P.addColorStop(0,h),P.addColorStop(1,s),e.fillStyle=P,e.beginPath(),e.ellipse(f,g,b,y,0,0,Math.PI*2),e.fill(),e.fillStyle=s,d>.35){let R=Math.max(-1,Math.min(1,o.lookX)),W=Math.max(-1,Math.min(1,o.lookY)),le=c==="#fff"?.3:.34;e.globalAlpha=A*Math.min(1,(d-.35)*3),e.fillStyle=c,e.beginPath(),e.arc(f-b*.1+R*b*.5,g-y*.16+W*y*.42,b*le,0,Math.PI*2),e.fill(),e.fillStyle=s;}e.restore();}if(r>.5&&w>.01&&Re(e,f,g,b*2,b*1.8,s,.3*w),e.fillStyle=s,o.happyEye>.01){let d=.6+.4*o.happyEye;e.globalAlpha=Math.min(1,o.happyEye),e.beginPath(),e.arc(f,g+.045,b*1.15*d,Math.PI*1.12,Math.PI*1.88),e.stroke();}if(o.closedEye>.01){let d=.6+.4*o.closedEye;e.globalAlpha=Math.min(1,o.closedEye),e.beginPath(),e.moveTo(f-b*1.15*d,g+.01),e.quadraticCurveTo(f,g+.07*d,f+b*1.15*d,g+.01),e.stroke();}if(o.xEye>.01){let d=b*.95*(.6+.4*o.xEye);e.globalAlpha=Math.min(1,o.xEye),e.beginPath(),e.moveTo(f-d,g-d),e.lineTo(f+d,g+d),e.moveTo(f+d,g-d),e.lineTo(f-d,g+d),e.stroke();}o.heartEye>.01&&(e.globalAlpha=Math.min(1,o.heartEye),ge(e,f,g,b*1.35*(.5+.5*o.heartEye)),e.fill());}let k=a+o.mouthSkew*.07,S=nt+u,E=Math.min(1,Math.max(0,o.mouthRound)),C=o.mouthW,O=e.createLinearGradient(k-C-.02,S,k+C+.02,S);O.addColorStop(0,p),O.addColorStop(.5,h),O.addColorStop(1,p);let B=(m,f,g,b)=>{e.fillStyle=l,m(),e.fill(),f>.02&&(e.save(),m(),e.clip(),e.fillStyle=i,e.globalAlpha*=Math.min(1,(f-.02)*12),e.beginPath(),e.ellipse(k,g,b,f*.55,0,0,Math.PI*2),e.fill(),e.restore()),e.strokeStyle=O;let d=e.lineWidth;e.lineWidth=d*.6,m(),e.stroke(),e.lineWidth=d;};if(E<.99){e.globalAlpha=1-E;let m=18,f=y=>o.mouthCurve*.075*(1-y*y),g=(y,P)=>{let R=y/m*2-1,W=k+R*C,le=S+f(R)+o.mouthSkew*R*.03+o.mouthWave*.018*Math.sin((R+1)*Math.PI*2)+P*(1-R*R);return [W,le]},b=Math.max(0,o.mouthOpen),d=()=>{for(let y=0;y<=m;y++){let[P,R]=g(y,-b*.15);y?e.lineTo(P,R):e.moveTo(P,R);}};b>.003?B(()=>{e.beginPath(),d();for(let P=m;P>=0;P--){let[R,W]=g(P,b);e.lineTo(R,W);}e.closePath();},b,g(m/2,b)[1],C*.55):(e.strokeStyle=O,e.beginPath(),d(),e.stroke());}if(E>.01){e.globalAlpha=E;let m=Math.max(.02,o.mouthOpen*.55+.01),f=()=>{e.beginPath(),e.ellipse(k,S+.01,m*.85,m,0,0,Math.PI*2);};e.lineWidth=fe*.8,B(f,o.mouthOpen,S+.01+m*.8,m*.6),e.lineWidth=fe;}e.globalAlpha=1;}function Re(e,o,t,s,i,r,h){let c=e.createRadialGradient(o,t,0,o,t,1);c.addColorStop(0,r),c.addColorStop(1,"rgba(0,0,0,0)"),e.save(),e.globalAlpha=h,e.translate(o,t),e.scale(s,i),e.translate(-o,-t),e.fillStyle=c,e.beginPath(),e.arc(o,t,1,0,Math.PI*2),e.fill(),e.restore();}function ge(e,o,t,s){e.beginPath(),e.moveTo(o,t+s*.85),e.bezierCurveTo(o-s*1.25,t+s*.05,o-s*.75,t-s*.95,o,t-s*.35),e.bezierCurveTo(o+s*.75,t-s*.95,o+s*1.25,t+s*.05,o,t+s*.85),e.closePath();}var ie=()=>({y:0,rx:0,ry:0,rz:0,st:0,mouth:0,lookX:0,lookY:0}),{sin:v,cos:rt,abs:at,max:oe,exp:ht,PI:se}=Math;function ye(e,o,t,s){switch(s.y=s.rx=s.ry=s.rz=s.st=s.mouth=s.lookX=s.lookY=0,e){case "idle":{let i=v(o*1.6);s.y=.015*i,s.st=.018*i;break}case "thinking":{s.rz=.07*v(o*1.1),s.y=.02*v(o*2.2),s.ry=.15,s.rx=-0.06;break}case "loading":{let i=o*2.4;s.rx=.12*v(i),s.rz=.12*rt(i),s.ry=.22*v(i),s.y=.02*v(i*2),s.lookX=.55*v(i);break}case "listening":{let i=oe(0,v(o*3.2));s.rx=.09*i*i;let r=at(v(o*7.1)*.5+v(o*11.3)*.3+v(o*3.7)*.2);s.st=.03*r;break}case "speaking":{let i=oe(0,v(o*9)*.6+v(o*14.3)*.4)*(.65+.35*v(o*1.7));s.mouth=.11*i,s.st=.04*i,s.rz=.03*v(o*2.1);break}case "happy":{let i=o*1.35%1;s.y=.2*v(se*i);let r=oe(0,1-i/.14)+oe(0,(i-.9)/.1);s.st=.08*v(se*i)-.14*r,s.rz=.05*v(o*1.35*se);break}case "surprised":{if(t<.45){let i=v(se*t/.45);s.y=.16*i,s.st=.12*i;}s.rz=.01*v(o*38)*ht(-t*2),s.rx=-0.06;break}case "sad":{let i=v(o*1.1);s.y=-0.06,s.rx=.2,s.st=-0.04+.015*i,s.rz=-0.04;break}case "error":{let i=t%2.8;if(i<.55){let r=1-i/.55;s.ry=.26*v(i*42)*r,s.rz=.05*v(i*42+1)*r;}s.st=-0.02;break}case "sleeping":{let i=v(o*1);s.st=.04*i,s.y=-0.03,s.rz=.1,s.rx=.1;break}case "love":{s.y=.07+.06*v(o*1.6),s.rz=.05*v(o*.8),s.st=.015*v(o*1.6+1);break}}return s}function Oe(e,o,t,s){for(let i of Object.keys(s))s[i]=e[i]+(o[i]-e[i])*t;return s}var Be={thinking:"dots",sleeping:"zz",love:"hearts",listening:"waves",surprised:"bang"};var j=new Set,T=null,H=0,V=0,ne=0,Me,Le=false,$="idle",D={x:0,y:0,lastMove:-1e9,known:false},z={reducedMotion:false,lowPower:false};function ct(){if(Le)return;Le=true,addEventListener("pointermove",o=>{D.x=o.clientX,D.y=o.clientY,D.lastMove=performance.now(),D.known=true;},{passive:true}),document.addEventListener("visibilitychange",_);let e=matchMedia("(prefers-reduced-motion: reduce)");z.reducedMotion=e.matches,e.addEventListener?.("change",o=>z.reducedMotion=o.matches);}function qe(e,o){return T||(T=new WebGLRenderer({antialias:true,alpha:true,powerPreference:"low-power",preserveDrawingBuffer:true}),T.setPixelRatio(1),T.setClearColor(0,0),z.lowPower=dt(T.getContext()),T.setScissorTest(true),T.domElement.addEventListener("webglcontextlost",t=>{t.preventDefault(),T?.dispose(),T=null,H=V=0,$="idle";})),(e>H||o>V)&&(H=Math.max(H,e),V=Math.max(V,o),T.setSize(H,V,false)),T}var Ie=()=>V;function dt(e){try{let o=e.getExtension("WEBGL_debug_renderer_info"),t=String(e.getParameter(o?o.UNMASKED_RENDERER_WEBGL:e.RENDERER));return /swiftshader|llvmpipe|softpipe|software|basic render/i.test(t)}catch{return false}}function Ye(e,o){if($==="ready")return true;if($==="idle"&&T){$="compiling";let t=T;t.compileAsync(e,o).catch(()=>{}).then(()=>{t===T&&($="ready"),_();});}return false}function We(e){ct(),clearTimeout(Me),j.add(e),_();}function De(e){j.delete(e),j.size===0&&(clearTimeout(Me),Me=setTimeout(()=>{j.size||!T||(T.dispose(),T.forceContextLoss(),T=null,H=V=0,$="idle");},1500));}function _(){if(!ne){for(let e of j)if(e.active){ne=requestAnimationFrame(_e);return}}}var ut=10,Ge=new WeakMap,ve=0;function _e(e){ne=0;let o=[...j].filter(r=>r.active);if(!o.length)return;let t=performance.now(),s=o.length,i=0;for(;i<s;i++){let r=o[(ve+i)%s],h=Ge.get(r)??e-16;if(Ge.set(r,e),r.tick(Math.min(.05,Math.max(0,(e-h)/1e3)),e),performance.now()-t>ut){i++;break}}ve=(ve+i)%s,ne=requestAnimationFrame(_e);}var J={pebble:{faceY:.02,faceScale:1.3,animated:false},blob:{faceY:.02,faceScale:1.28,animated:true},drop:{faceY:-0.24,faceScale:1.22,animated:false},bean:{faceY:.14,faceScale:.92,animated:false},cube:{faceY:0,faceScale:1.22,animated:false},ghost:{faceY:.22,faceScale:1.24,animated:true}};function Ne(e){let o=new SphereGeometry(1,e*2,Math.round(e*1.5)),t=o.getAttribute("position"),s=o.getIndex(),i=new Map,r=[],h=new Uint32Array(t.count);for(let n=0;n<t.count;n++){let a=t.getX(n),u=t.getY(n),w=t.getZ(n),A=`${Math.round(a*1e4)},${Math.round(u*1e4)},${Math.round(w*1e4)}`,k=i.get(A);if(k===void 0){k=r.length/3,i.set(A,k);let S=Math.hypot(a,u,w)||1;r.push(a/S,u/S,w/S);}h[n]=k;}let c=[];for(let n=0;n<s.count;n+=3){let a=h[s.getX(n)],u=h[s.getX(n+1)],w=h[s.getX(n+2)];a!==u&&u!==w&&a!==w&&c.push(a,u,w);}o.dispose();let l=new BufferGeometry,p=new Float32Array(r);return l.setAttribute("position",new BufferAttribute(new Float32Array(p),3)),l.setIndex(c),{geometry:l,dirs:p}}var bt=(e,o,t)=>{let s=Math.min(1,Math.max(0,(t-e)/(o-e)));return s*s*(3-2*s)};function we(e,o,t,s,i,r,h){let c=o,l=t,p=s;switch(e){case "pebble":{c=o*1.1+.04*t,l=t*.92*(.95+.05*Math.tanh(3*t))+.03*o,p=s*.94;break}case "blob":{let n=.97+.045*Math.sin(2.1*o+1.3*i)*Math.sin(2.4*t+1.7*i+1)+.035*Math.sin(2.7*s+2.3*o+1.1*i);c=o*n,l=t*n,p=s*n;break}case "drop":{let n=Math.max(t,0),a=1-.8*Math.pow(n,1.6);c=o*a*.98+.1*n*n*n,l=(t<0?t*.9:t*1.32)-.12,p=s*a*.98;break}case "bean":{let n=t*.78+.42*Math.tanh(t*3);c=(o*.8+.14*(n*n/1.44)-.07)*.9,l=n*.88,p=s*.8*.9;break}case "cube":{let a=Math.pow(Math.pow(Math.abs(o),4.5)+Math.pow(Math.abs(t),4.5)+Math.pow(Math.abs(s),4.5),-0.2222222222222222);c=o*a*.84,l=t*a*.82,p=s*a*.84;break}case "ghost":{if(t>=0)c=o,l=t,p=s;else {let a=(Math.acos(Math.max(-1,t))-Math.PI/2)/(Math.PI/2),u=Math.hypot(o,s)||1,w=o/u,A=s/u,k=Math.atan2(s,o),S=.085*Math.sin(5*k+2.2*i),E=1.04,C=.13,O=-1.05+S,B;if(a<.45){let m=a/.45;B=1+.04*m,l=-m*(1.05-C)+S*bt(.45,1,m);}else if(a<.75){let m=(a-.45)/.3*(Math.PI/2);B=E-C+C*Math.cos(m),l=O+C-C*Math.sin(m);}else {let m=(a-.75)/.25;B=(E-C)*(1-m),l=O+.22*m*m*(3-2*m);}c=w*B,p=A*B;}c*=.9,l=(l+.1)*.9,p*=.9;break}}r[h]=c,r[h+1]=l,r[h+2]=p;}var I=null,re=null;function gt(){if(typeof window>"u"||!("AudioContext"in window))return null;if(yt(),!I){I=new AudioContext;let e=I.createBiquadFilter();e.type="lowpass",e.frequency.value=3200,re=I.createGain(),re.gain.value=.55,re.connect(e).connect(I.destination);}return I.state==="suspended"&&I.resume(),I}function F(e,{freq:o,to:t,at:s=0,dur:i=.12,type:r="sine",gain:h=.1,wobble:c=0}){let l=e.currentTime+s,p=e.createOscillator(),n=e.createGain();if(p.type=r,p.frequency.setValueAtTime(o,l),t&&p.frequency.exponentialRampToValueAtTime(t,l+i),c){let a=e.createOscillator(),u=e.createGain();a.frequency.value=7,u.gain.value=c,a.connect(u).connect(p.frequency),a.start(l),a.stop(l+i+.05);}n.gain.setValueAtTime(1e-4,l),n.gain.exponentialRampToValueAtTime(h,l+Math.min(.012,i/4)),n.gain.exponentialRampToValueAtTime(1e-4,l+i),p.connect(n).connect(re),p.start(l),p.stop(l+i+.02);}var ke={idle:e=>F(e,{freq:520,to:780,dur:.09,gain:.08}),thinking:e=>[660,760,900].forEach((o,t)=>F(e,{freq:o,to:o*1.08,at:t*.1,dur:.07,type:"triangle",gain:.06})),loading:e=>[0,.14,.28].forEach(o=>F(e,{freq:940,at:o,dur:.04,gain:.05})),listening:e=>{F(e,{freq:523,dur:.12,type:"triangle",gain:.07}),F(e,{freq:784,at:.1,dur:.22,type:"triangle",gain:.07});},speaking:e=>[380,460,340,520,420].forEach((o,t)=>F(e,{freq:o,to:o*1.15,at:t*.075,dur:.06,type:"triangle",gain:.06})),happy:e=>[523,659,784,1047].forEach((o,t)=>F(e,{freq:o,at:t*.07,dur:.14,type:"triangle",gain:.07})),surprised:e=>F(e,{freq:280,to:980,dur:.18,gain:.09,wobble:30}),sad:e=>{F(e,{freq:560,to:300,dur:.5,gain:.07}),F(e,{freq:420,to:240,at:.12,dur:.5,gain:.04});},error:e=>[0,.14].forEach(o=>F(e,{freq:170,to:150,at:o,dur:.1,type:"sawtooth",gain:.045})),sleeping:e=>{F(e,{freq:150,to:115,dur:.6,gain:.07,wobble:6}),F(e,{freq:300,to:230,at:.05,dur:.5,gain:.02});},love:e=>[1319,1568,2093,1760].forEach((o,t)=>F(e,{freq:o,at:t*.08,dur:.22,gain:.04}))},Y=null,Ve=false;function yt(){if(Ve)return;Ve=true;let e=()=>{I&&I.resume().then(()=>{Y&&performance.now()<Y.until&&ke[Y.mood](I),Y=null;});};for(let o of ["pointerdown","touchend","keydown"])addEventListener(o,e,{passive:true});}function ae(e,o=false){let t=gt();if(t){if(t.state==="running"){Y=null,ke[e](t);return}Y={mood:e,until:performance.now()+(o?6e3:1200)},t.resume().then(()=>{Y&&performance.now()<Y.until&&ke[Y.mood](t),Y=null;});}}var Ae=-0.85,$e=new Map;function Pt(e){let o=$e.get(e);if(!o){let{geometry:t,dirs:s}=Ne(e);o={dirs:s,index:new Uint32Array(t.getIndex().array)},t.dispose(),$e.set(e,o);}return o}var K=(e,o)=>1-Math.exp(-e*o),je=e=>1-Math.pow(1-e,3),he=class{constructor(){this.scene=new Scene;this.camera=new PerspectiveCamera(30,1,.1,50);this.group=new Group;this.v=new Vector3;this.geometry=new BufferGeometry;this.faceCanvas=document.createElement("canvas");this.uniforms={uFace:{value:null},uFaceY:{value:0},uFaceScale:{value:1.5}};this.state={...M};this.visible=false;this.connected=false;this.w=0;this.h=0;this.t=Math.random()*100;this.shapeFrom=M.shape;this.shapeTo=M.shape;this.morph=1;this.shapeDirty=true;this.shown=M.mood;this.prevShown=M.mood;this.tau=10;this.prevTau=10;this.blend=1;this.reaction=null;this.pose=ie();this.poseA=ie();this.poseB=ie();this.face=be(te.idle);this.target=be(te.idle);this.drawn=new Float32Array(U.length+7).fill(NaN);this.blink=1;this.blinkT=-1;this.nextBlink=2+Math.random()*3;this.body=X(M.color,[.95,.63,.48]);this.bodyT=this.body;this.faceC=Q(this.body);this.faceCT=this.faceC;this.cheek=ee(this.body);this.cheekT=this.cheek;this.look={x:0,y:0};this.wander={x:0,y:0,next:0};this.squash=0;this.squashV=0;this.pop=.6;this.popV=0;this.hover=0;this.hovering=false;this.overlay={dots:0,zz:0,hearts:0,waves:0,bang:0};this.needsFrame=true;this.ready=false;this.css=[0,0];this.quality=1;this.lowPowerApplied=false;this.cost=0;this.costFrames=0;this.lastFrame=[];this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),this.faceCtx=this.faceCanvas.getContext("2d"),this.faceCanvas.width=this.faceCanvas.height=256,this.faceTex=new CanvasTexture(this.faceCanvas),this.faceTex.colorSpace=SRGBColorSpace,this.faceTex.minFilter=LinearMipmapLinearFilter,this.faceTex.anisotropy=4,this.uniforms.uFace.value=this.faceTex,this.setSegments(24),this.material=this.createMaterial(),this.mesh=new Mesh(this.geometry,this.material),this.mesh.frustumCulled=false,this.mesh.position.y=-Ae,this.group.add(this.mesh),this.scene.add(this.group),this.scene.add(new HemisphereLight(16774120,7234140,1.7));let o=new DirectionalLight(16777215,1.9);o.position.set(2.5,3.5,4);let t=new DirectionalLight(16770770,1.4);t.position.set(-3.5,2,-3);let s=new DirectionalLight(16773350,.45);s.position.set(-2,-1,3),this.scene.add(o,t,s),this.camera.position.set(0,.3,6.6),this.camera.lookAt(0,.12,0);}get active(){return this.visible&&this.connected&&this.w>0}createMaterial(){let o=new MeshStandardMaterial({roughness:.62,metalness:0});return o.color.setRGB(...this.body,SRGBColorSpace),o.customProgramCacheKey=()=>"moodi-1",o.onBeforeCompile=t=>{Object.assign(t.uniforms,this.uniforms),t.vertexShader=t.vertexShader.replace("#include <common>",`#include <common>
|
|
2
2
|
varying vec3 vFaceP;
|
|
3
3
|
varying vec3 vFaceN;`).replace("#include <begin_vertex>",`#include <begin_vertex>
|
|
4
4
|
vFaceP = position;
|
|
5
|
-
vFaceN = objectNormal;`),
|
|
5
|
+
vFaceN = objectNormal;`),t.fragmentShader=t.fragmentShader.replace("#include <common>",`#include <common>
|
|
6
6
|
varying vec3 vFaceP;
|
|
7
7
|
varying vec3 vFaceN;
|
|
8
8
|
uniform sampler2D uFace;
|
|
@@ -20,6 +20,9 @@ outgoingLight *= mix(0.62, 1.0, smoothstep(-0.95, 0.35, normalize(vFaceN).y));
|
|
|
20
20
|
// eyes and mouth glow a little: mostly unlit, so they read as light, not paint
|
|
21
21
|
float emis = smoothstep(0.75, 1.0, fc.a) * inb * smoothstep(0.12, 0.45, normalize(vFaceN).z);
|
|
22
22
|
outgoingLight = mix(outgoingLight, diffuseColor.rgb * 1.12, emis * 0.65);
|
|
23
|
-
#include <opaque_fragment>`);},o}setSegments(o){let e=gt(o);this.dirs!==e.dirs&&(this.dirs=e.dirs,this.geometry.dispose(),this.geometry=new BufferGeometry,this.geometry.setAttribute("position",new BufferAttribute(new Float32Array(e.dirs.length),3)),this.geometry.setIndex(new BufferAttribute(e.index,1)),this.mesh&&(this.mesh.geometry=this.geometry),this.shapeDirty=true);}connect(){this.connected=true,ze(this);}disconnect(){this.connected=false,Oe(this);}setVisible(o){this.visible=o,o&&D();}resize(o,e){this.css=[o,e];let s=Math.min(2,devicePixelRatio||1)*this.quality,i=Math.max(1,Math.round(o*s)),a=Math.max(1,Math.round(e*s));if(i===this.w&&a===this.h)return;this.w=this.canvas.width=i,this.h=this.canvas.height=a;let l=Math.min(i,a),d=Math.min(o,e);this.camera.position.set(0,.3,d<90?5.1:6.1),this.camera.lookAt(0,d<90?.02:.12,0),this.camera.aspect=i/a,this.camera.updateProjectionMatrix(),this.setSegments(l<160?16:l<400?24:32);let u=l<220?256:512;this.faceCanvas.width!==u&&(this.faceCanvas.width=this.faceCanvas.height=u,this.faceTex.dispose(),this.drawn.fill(NaN)),this.needsFrame=true,D();}update(o){let e=this.state;this.state={...e,...o};let s=this.state;s.shape!==this.shapeTo&&(this.shapeFrom=this.shapeTo,this.shapeTo=s.shape,this.morph=0,G.reducedMotion||(this.squashV-=2.2)),(s.color!==e.color||s.faceColor!==e.faceColor||s.cheekColor!==e.cheekColor)&&(this.bodyT=_(s.color,_(M.color,this.bodyT)),this.faceCT=s.faceColor?_(s.faceColor,U(this.bodyT)):U(this.bodyT),this.cheekT=s.cheekColor?_(s.cheekColor,J(this.bodyT)):J(this.bodyT)),s.mood!==e.mood&&!this.reaction&&this.show(s.mood),this.needsFrame=true,D();}react(o,e=1200){this.reaction={mood:o,left:e/1e3},this.show(o);}get currentMood(){return this.shown}dispose(){this.disconnect(),this.geometry.dispose(),this.material.dispose(),this.faceTex.dispose(),this.onMoodChange=void 0;}show(o){if(o===this.shown)return;let e=this.shown;this.prevShown=e,this.prevTau=this.tau,this.shown=o,this.tau=0,this.blend=0,G.reducedMotion||(this.squashV-=1.1),this.onMoodChange?.({mood:o,previous:e,reaction:!!this.reaction});}tick(o,e){let s=this.state,i=G.reducedMotion,a=o*Math.max(0,s.speed);this.t+=a,this.tau+=a,this.prevTau+=a,this.reaction&&(this.reaction.left-=o,this.reaction.left<=0&&(this.reaction=null,this.show(s.mood))),pe(this.shown,this.t,this.tau,this.poseB),this.blend<1?(this.blend=Math.min(1,this.blend+o/.45),pe(this.prevShown,this.t,this.prevTau,this.poseA),Ae(this.poseA,this.poseB,We(this.blend),this.pose)):Object.assign(this.pose,this.poseB);let l=this.pose;i&&(l.y=l.rx=l.ry=l.rz=l.st=l.lookX=0);let d=s.followCursor&&!i&&s.speed>0?Ce[this.shown]:0,u=0,p=0;if(d>0&&I.known){let n=this.canvas.getBoundingClientRect(),f=Math.max(200,n.width*.85);u=Math.max(-1,Math.min(1,(I.x-(n.left+n.width/2))/f)),p=Math.max(-1,Math.min(1,(I.y-(n.top+n.height*.45))/f)),u*=d,p*=d;}else if(this.shown==="idle"&&!i&&s.speed>0){if(this.t>this.wander.next){let n=Math.random()<.4;this.wander.x=n?0:(Math.random()*2-1)*.6,this.wander.y=n?0:(Math.random()*2-1)*.35,this.wander.next=this.t+1.8+Math.random()*2.6;}u=this.wander.x,p=this.wander.y;}let r=V(7,o);this.look.x+=(u-this.look.x)*r,this.look.y+=(p-this.look.y)*r;let h=Z[this.shown],c=this.target;for(let n of $)c[n]=h[n];c.lookX+=this.look.x+l.lookX,c.lookY+=this.look.y+l.lookY,c.mouthOpen+=l.mouth,c.eyeOpen+=this.hover*.08;let C=V(11,o);for(let n of $)this.face[n]+=(c[n]-this.face[n])*C;let A=V(16,o);if(this.face.lookX+=(c.lookX-this.face.lookX)*A,this.face.lookY+=(c.lookY-this.face.lookY)*A,this.face.mouthOpen+=(c.mouthOpen-this.face.mouthOpen)*A,this.nextBlink-=a,this.nextBlink<=0&&this.blinkT<0&&(this.blinkT=0,this.nextBlink=Math.random()<.2?.25:2.2+Math.random()*3.8),this.blinkT>=0){this.blinkT+=a;let n=this.blinkT/.16;this.blink=n>=1?1:1-Math.sin(Math.PI*n),n>=1&&(this.blinkT=-1);}let k=Math.min(o,1/30);this.squashV+=(-170*this.squash-11*this.squashV)*k,this.squash+=this.squashV*k,i&&(this.squash=this.squashV=0),i?this.pop=1:(this.popV+=(-140*(this.pop-1)-13*this.popV)*k,this.pop+=this.popV*k),this.hover+=((this.hovering&&s.interactive?1:0)-this.hover)*V(12,o);let w=V(9,o);this.body=P(this.body,this.bodyT,w),this.faceC=P(this.faceC,this.faceCT,w),this.cheek=P(this.cheek,this.cheekT,w),this.material.color.setRGB(this.body[0],this.body[1],this.body[2],SRGBColorSpace);let T=Ee[this.shown],S=V(6,o);for(let n of Object.keys(this.overlay))this.overlay[n]+=((T===n?1:0)-this.overlay[n])*S;this.morph<1&&(this.morph=Math.min(1,this.morph+o/(i?.2:.38)),this.shapeDirty=true);let R=j[this.shapeTo].animated||this.morph<1&&j[this.shapeFrom].animated;(this.shapeDirty||R&&a>0)&&this.rebuild();let B=this.group,m=(1+l.st+this.squash)*(1+this.hover*.04),b=1/Math.sqrt(Math.max(.3,1+l.st+this.squash))*(1+this.hover*.04);B.position.y=ve+l.y,B.rotation.set(l.rx+this.look.y*.22*(d>0?1:0),l.ry+this.look.x*.42,l.rz),B.scale.set(b*this.pop,m*this.pop,b*this.pop);let y=this.drawFaceIfNeeded();if(s.speed===0&&!y&&!this.needsFrame&&this.settled())return;let v=performance.now();this.render(l)&&(this.needsFrame=false,this.adapt(performance.now()-v));}adapt(o){this.cost=this.costFrames?this.cost*.9+o*.1:o,!(++this.costFrames<20||this.cost<9||this.quality<=.5)&&(this.quality=Math.max(.5,this.quality-.25),this.costFrames=0,this.resize(this.css[0],this.css[1]));}settled(){let o=this.group,e=this.overlay,s=[e.bang,o.position.y,o.rotation.x,o.rotation.y,o.rotation.z,o.scale.x,o.scale.y,...this.body,this.morph,e.dots,e.zz,e.hearts,e.waves],i=this.lastFrame,a=i.length===s.length;for(let l=0;a&&l<s.length;l++)a=Math.abs(s[l]-i[l])<1e-4;return this.lastFrame=s,a}rebuild(){let o=this.geometry.getAttribute("position"),e=o.array,s=this.dirs,i=We(this.morph),a=this.shapeFrom,l=this.shapeTo,d=new Float32Array(3);for(let r=0;r<s.length;r+=3)fe(l,s[r],s[r+1],s[r+2],this.t,e,r),i<1&&(fe(a,s[r],s[r+1],s[r+2],this.t,d,0),e[r]=d[0]+(e[r]-d[0])*i,e[r+1]=d[1]+(e[r+1]-d[1])*i,e[r+2]=d[2]+(e[r+2]-d[2])*i);o.needsUpdate=true,this.geometry.computeVertexNormals();let u=j[a],p=j[l];this.uniforms.uFaceY.value=u.faceY+(p.faceY-u.faceY)*i,this.uniforms.uFaceScale.value=u.faceScale+(p.faceScale-u.faceScale)*i,this.shapeDirty=false;}drawFaceIfNeeded(){let o=this.face,e=this.drawn,s=false,i=0;for(let d of $)Math.abs(e[i]-o[d])<.002||(s=true,e[i]=o[d]),i++;let a=[this.blink,...this.faceC,...this.cheek];for(let d of a)Math.abs(e[i]-d)<.002||(s=true,e[i]=d),i++;if(!s)return false;let l=he(this.faceC)>.45;return Te(this.faceCtx,o,this.blink,O(this.faceC),O(this.cheek),l?1:.3,O(P(this.faceC,[1,1,1],l?.6:.16)),l?O(P(this.body,[.04,.03,.06],.86)):"#fff",O(l?P(this.body,[.03,.02,.05],.78):P(this.faceC,[0,0,0],.35)),O(this.faceC)),this.faceTex.needsUpdate=true,true}project(o,e,s){let i=this.v.set(o,e,s).applyMatrix4(this.group.matrixWorld).project(this.camera);return [(i.x*.5+.5)*this.w,(-i.y*.5+.5)*this.h]}render(o){let{w:e,h:s,ctx:i}=this,a=Fe(e,s);if(!Be(this.scene,this.camera))return false;if(i.clearRect(0,0,e,s),this.group.updateMatrixWorld(),this.state.shadow){let l=this.v.set(0,ve-.08,0).project(this.camera),d=(l.x*.5+.5)*e,u=(-l.y*.5+.5)*s,p=Math.max(0,o.y)*3,r=Math.min(e,s)*.26*(1-p*.25)*this.pop,h=i.createRadialGradient(d,u,0,d,u,r),c=P(this.body,[.05,.03,.02],.75);h.addColorStop(0,`rgba(${c[0]*255|0},${c[1]*255|0},${c[2]*255|0},${.42*(1-p*.4)})`),h.addColorStop(1,"rgba(0,0,0,0)"),i.fillStyle=h,i.save(),i.translate(d,u),i.scale(1,.22),i.translate(-d,-u),i.beginPath(),i.arc(d,u,r,0,Math.PI*2),i.fill(),i.restore();}return a.setViewport(0,0,e,s),a.setScissor(0,0,e,s),a.render(this.scene,this.camera),i.drawImage(a.domElement,0,Re()-s,e,s,0,0,e,s),this.drawOverlays(),true}drawOverlays(){let o=this.overlay;if(o.dots+o.zz+o.hearts+o.waves+o.bang<.01)return;let{ctx:e}=this,s=this.t,i=G.reducedMotion,a=Math.min(this.w,this.h),l=P(this.body,[1,1,1],.22),d=O(l);if(e.save(),o.dots>.01){let[u,p]=this.project(.6,.92,.2),r=i?2:s*.55%1.6,h=[.014,.022,.052];for(let c=0;c<3;c++){let C=i?1:we(Me((r-c*.18)/.32)),A=i?0:Math.sin(s*2.2+c)*a*.006,k=u+[0,.05,.12][c]*a,w=p-[0,.05,.12][c]*a+A,T=h[c]*a*C;if(!(T<=.2)&&(e.globalAlpha=o.dots*.95,e.fillStyle=d,e.beginPath(),c<2?e.arc(k,w,T,0,Math.PI*2):yt(e,k,w,T),e.fill(),c===2&&C>.6)){e.fillStyle=O(P(this.body,[0,0,0],.35));for(let S=0;S<3;S++){let R=i?0:Math.max(0,Math.sin(s*7-S*.9))*T*.16;e.beginPath(),e.arc(k+(S-1)*T*.34,w-R,T*.1,0,Math.PI*2),e.fill();}}}}if(o.zz>.01){let[u,p]=this.project(.45,.85,.2);e.textAlign="center",e.textBaseline="middle",e.fillStyle=d;for(let r=0;r<3;r++){let h=i?.25+r*.25:(s*.33+r/3)%1,c=u+h*a*.17+Math.sin(h*7+r)*a*.018,C=p-ke(h)*a*.26;e.globalAlpha=o.zz*Math.min(1,h*5)*(1-h)*1.3,e.save(),e.translate(c,C),e.rotate(-0.25+Math.sin(h*5+r)*.22),e.font=`800 ${Math.round(a*(.05+.06*h))}px ui-rounded, system-ui, sans-serif`,e.fillText("z",0,0),e.restore();}}if(o.hearts>.01){let[u,p]=this.project(0,.8,.2),r=P(this.cheek,[1,.3,.45],.55);for(let h=0;h<4;h++){let c=i?.4:(s*.3+h/4)%1,C=h%2?1:-1,A=u+C*a*(.16+.07*(h>>1))+Math.sin(c*6+h)*a*.025,k=p-a*.02-ke(c)*a*.28,w=i?1:we(Me(c/.22)),T=a*(.026+.008*(h%3))*w;e.globalAlpha=o.hearts*(1-Math.max(0,(c-.6)/.4)),e.save(),e.translate(A,k),e.rotate(Math.sin(s*3+h)*.25);let S=e.createLinearGradient(0,-T,0,T);S.addColorStop(0,O(P(r,[1,1,1],.35))),S.addColorStop(1,O(r)),e.fillStyle=S,ue(e,0,0,T),e.fill(),e.restore();}}if(o.waves>.01){e.lineCap="round",e.strokeStyle=d;for(let u of [-1,1]){let[p,r]=this.project(u*1,.12,0);for(let h=0;h<3;h++){let c=i?.3+h*.2:(s*.8+h/3)%1,C=ke(c);e.globalAlpha=o.waves*Math.sin(Math.PI*c)*.9,e.lineWidth=a*(.016-.008*c),e.beginPath();let A=a*(.03+.11*C);u>0?e.arc(p-a*.03,r,A,-0.55,.55):e.arc(p+a*.03,r,A,Math.PI-.55,Math.PI+.55),e.stroke();}}}if(o.bang>.01){let[u,p]=this.project(.55,1,.2),r=i?1:we(Me(this.tau/.35));e.globalAlpha=o.bang,e.fillStyle=d,e.save(),e.translate(u,p),e.rotate(.18),e.scale(r,r),e.beginPath(),e.roundRect(-a*.016,-a*.1,a*.032,a*.07,a*.016),e.arc(0,a*.005,a*.018,0,Math.PI*2),e.fill(),e.restore();}e.restore();}},Me=t=>t<0?0:t>1?1:t,ke=t=>1-(1-t)*(1-t),we=t=>1+(1.9+1)*Math.pow(t-1,3)+1.9*Math.pow(t-1,2);function yt(t,o,e,s){t.ellipse(o,e,s,s*.72,0,0,Math.PI*2),t.moveTo(o-s*.35+s*.45,e-s*.38),t.arc(o-s*.35,e-s*.38,s*.45,0,Math.PI*2),t.moveTo(o+s*.3+s*.5,e-s*.32),t.arc(o+s*.3,e-s*.32,s*.5,0,Math.PI*2);}var vt=["shape","mood","color","face-color","cheek-color","size","speed","follow-cursor","interactive","shadow"],H=(t,o)=>t===null?o:t!=="false"&&t!=="0",_e=t=>t==null||t===""?`${M.size}px`:typeof t=="number"||/^\d+(\.\d+)?$/.test(String(t))?`${t}px`:String(t),Mt=`:host{display:inline-block;position:relative;cursor:pointer;width:var(--moodi-size);height:var(--moodi-size);vertical-align:middle;contain:layout paint style;-webkit-tap-highlight-color:transparent}
|
|
23
|
+
#include <opaque_fragment>`);},o}setSegments(o){let t=Pt(o);this.dirs!==t.dirs&&(this.dirs=t.dirs,this.geometry.dispose(),this.geometry=new BufferGeometry,this.geometry.setAttribute("position",new BufferAttribute(new Float32Array(t.dirs.length),3)),this.geometry.setIndex(new BufferAttribute(t.index,1)),this.mesh&&(this.mesh.geometry=this.geometry),this.shapeDirty=true);}connect(){this.connected=true,We(this);}disconnect(){this.connected=false,De(this);}setVisible(o){this.visible=o,o&&_();}resize(o,t){this.css=[o,t];let s=(z.lowPower?1:Math.min(2,devicePixelRatio||1))*this.quality,i=Math.max(1,Math.round(o*s)),r=Math.max(1,Math.round(t*s));if(i===this.w&&r===this.h)return;this.w=this.canvas.width=i,this.h=this.canvas.height=r;let h=Math.min(i,r),c=Math.min(o,t);this.camera.position.set(0,.3,c<90?5.1:6.1),this.camera.lookAt(0,c<90?.02:.12,0),this.camera.aspect=i/r,this.camera.updateProjectionMatrix(),this.setSegments(h<160?16:h<400?24:32);let l=h<220?256:512;this.faceCanvas.width!==l&&(this.faceCanvas.width=this.faceCanvas.height=l,this.faceTex.dispose(),this.drawn.fill(NaN)),this.needsFrame=true,_();}update(o){let t=this.state;this.state={...t,...o};let s=this.state;s.shape!==this.shapeTo&&(this.shapeFrom=this.shapeTo,this.shapeTo=s.shape,this.morph=0,z.reducedMotion||(this.squashV-=2.2)),(s.color!==t.color||s.faceColor!==t.faceColor||s.cheekColor!==t.cheekColor)&&(this.bodyT=X(s.color,X(M.color,this.bodyT)),this.faceCT=s.faceColor?X(s.faceColor,Q(this.bodyT)):Q(this.bodyT),this.cheekT=s.cheekColor?X(s.cheekColor,ee(this.bodyT)):ee(this.bodyT)),s.mood!==t.mood&&!this.reaction&&this.show(s.mood),s.sound&&!t.sound&&this.ready&&ae(this.shown),this.needsFrame=true,_();}bump(o=1){z.reducedMotion||(this.squashV-=1.6*o,this.needsFrame=true,_());}react(o,t=1200){this.reaction={mood:o,left:t/1e3},this.show(o);}get currentMood(){return this.shown}dispose(){this.disconnect(),this.geometry.dispose(),this.material.dispose(),this.faceTex.dispose(),this.onMoodChange=void 0;}show(o,t=true){if(o===this.shown)return;t&&this.state.sound&&this.ready&&ae(o);let s=this.shown;this.prevShown=s,this.prevTau=this.tau,this.shown=o,this.tau=0,this.blend=0,z.reducedMotion||(this.squashV-=1.1),this.onMoodChange?.({mood:o,previous:s,reaction:!!this.reaction});}tick(o,t){let s=this.state,i=z.reducedMotion,r=z.lowPower?0:o*Math.max(0,s.speed);this.t+=r,this.tau+=r,this.prevTau+=r,this.reaction&&(this.reaction.left-=o,this.reaction.left<=0&&(this.reaction=null,this.show(s.mood,false))),ye(this.shown,this.t,this.tau,this.poseB),this.blend<1?(this.blend=Math.min(1,this.blend+o/.45),ye(this.prevShown,this.t,this.prevTau,this.poseA),Oe(this.poseA,this.poseB,je(this.blend),this.pose)):Object.assign(this.pose,this.poseB);let h=this.pose;i&&(h.y=h.rx=h.ry=h.rz=h.st=h.lookX=0);let c=s.followCursor&&!i&&s.speed>0?Fe[this.shown]:0,l=0,p=0;if(c>0&&D.known){let d=this.canvas.getBoundingClientRect(),y=Math.max(200,d.width*.85);l=Math.max(-1,Math.min(1,(D.x-(d.left+d.width/2))/y)),p=Math.max(-1,Math.min(1,(D.y-(d.top+d.height*.45))/y)),l*=c,p*=c;}else if(this.shown==="idle"&&!i&&s.speed>0){if(this.t>this.wander.next){let d=Math.random()<.4;this.wander.x=d?0:(Math.random()*2-1)*.6,this.wander.y=d?0:(Math.random()*2-1)*.35,this.wander.next=this.t+1.8+Math.random()*2.6;}l=this.wander.x,p=this.wander.y;}let n=K(7,o);this.look.x+=(l-this.look.x)*n,this.look.y+=(p-this.look.y)*n;let a=te[this.shown],u=this.target;for(let d of U)u[d]=a[d];u.lookX+=this.look.x+h.lookX,u.lookY+=this.look.y+h.lookY,u.mouthOpen+=h.mouth,u.eyeOpen+=this.hover*.08;let w=K(11,o);for(let d of U)this.face[d]+=(u[d]-this.face[d])*w;let A=K(16,o);if(this.face.lookX+=(u.lookX-this.face.lookX)*A,this.face.lookY+=(u.lookY-this.face.lookY)*A,this.face.mouthOpen+=(u.mouthOpen-this.face.mouthOpen)*A,this.nextBlink-=r,this.nextBlink<=0&&this.blinkT<0&&(this.blinkT=0,this.nextBlink=Math.random()<.2?.25:2.2+Math.random()*3.8),this.blinkT>=0){this.blinkT+=r;let d=this.blinkT/.16;this.blink=d>=1?1:1-Math.sin(Math.PI*d),d>=1&&(this.blinkT=-1);}let k=Math.min(o,1/30);this.squashV+=(-170*this.squash-11*this.squashV)*k,this.squash+=this.squashV*k,i&&(this.squash=this.squashV=0),i?this.pop=1:(this.popV+=(-140*(this.pop-1)-13*this.popV)*k,this.pop+=this.popV*k),this.hover+=((this.hovering&&s.interactive?1:0)-this.hover)*K(12,o);let S=K(9,o);this.body=x(this.body,this.bodyT,S),this.faceC=x(this.faceC,this.faceCT,S),this.cheek=x(this.cheek,this.cheekT,S),this.material.color.setRGB(this.body[0],this.body[1],this.body[2],SRGBColorSpace);let E=Be[this.shown],C=K(6,o);for(let d of Object.keys(this.overlay))this.overlay[d]+=((E===d?1:0)-this.overlay[d])*C;this.morph<1&&(this.morph=Math.min(1,this.morph+o/(i?.2:.38)),this.shapeDirty=true);let O=J[this.shapeTo].animated||this.morph<1&&J[this.shapeFrom].animated;(this.shapeDirty||O&&r>0)&&this.rebuild();let B=this.group,m=(1+h.st+this.squash)*(1+this.hover*.04),f=1/Math.sqrt(Math.max(.3,1+h.st+this.squash))*(1+this.hover*.04);B.position.y=Ae+h.y,B.rotation.set(h.rx+this.look.y*.22*(c>0?1:0),h.ry+this.look.x*.42,h.rz),B.scale.set(f*this.pop,m*this.pop,f*this.pop);let g=this.drawFaceIfNeeded();if((s.speed===0||z.lowPower)&&!g&&!this.needsFrame&&this.settled())return;let b=performance.now();this.render(h)&&(this.needsFrame=false,this.ready||(this.ready=true,this.state.sound&&ae(this.shown,true),this.onReady?.(),this.onReady=void 0),this.adapt(performance.now()-b));}adapt(o){this.cost=this.costFrames?this.cost*.9+o*.1:o,!(++this.costFrames<20||this.cost<9||this.quality<=.5)&&(this.quality=Math.max(.5,this.quality-.25),this.costFrames=0,this.resize(this.css[0],this.css[1]));}settled(){let o=this.group,t=this.overlay,s=[t.bang,o.position.y,o.rotation.x,o.rotation.y,o.rotation.z,o.scale.x,o.scale.y,...this.body,this.morph,t.dots,t.zz,t.hearts,t.waves],i=this.lastFrame,r=i.length===s.length;for(let h=0;r&&h<s.length;h++)r=Math.abs(s[h]-i[h])<1e-4;return this.lastFrame=s,r}rebuild(){let o=this.geometry.getAttribute("position"),t=o.array,s=this.dirs,i=je(this.morph),r=this.shapeFrom,h=this.shapeTo,c=new Float32Array(3);for(let n=0;n<s.length;n+=3)we(h,s[n],s[n+1],s[n+2],this.t,t,n),i<1&&(we(r,s[n],s[n+1],s[n+2],this.t,c,0),t[n]=c[0]+(t[n]-c[0])*i,t[n+1]=c[1]+(t[n+1]-c[1])*i,t[n+2]=c[2]+(t[n+2]-c[2])*i);o.needsUpdate=true,this.geometry.computeVertexNormals();let l=J[r],p=J[h];this.uniforms.uFaceY.value=l.faceY+(p.faceY-l.faceY)*i,this.uniforms.uFaceScale.value=l.faceScale+(p.faceScale-l.faceScale)*i,this.shapeDirty=false;}drawFaceIfNeeded(){let o=this.face,t=this.drawn,s=false,i=0;for(let c of U)Math.abs(t[i]-o[c])<.002||(s=true,t[i]=o[c]),i++;let r=[this.blink,...this.faceC,...this.cheek];for(let c of r)Math.abs(t[i]-c)<.002||(s=true,t[i]=c),i++;if(!s)return false;let h=pe(this.faceC)>.45;return ze(this.faceCtx,o,this.blink,G(this.faceC),G(this.cheek),h?1:.3,G(x(this.faceC,[1,1,1],h?.6:.16)),h?G(x(this.body,[.04,.03,.06],.86)):"#fff",G(h?x(this.body,[.03,.02,.05],.78):x(this.faceC,[0,0,0],.35)),G(this.faceC)),this.faceTex.needsUpdate=true,true}project(o,t,s){let i=this.v.set(o,t,s).applyMatrix4(this.group.matrixWorld).project(this.camera);return [(i.x*.5+.5)*this.w,(-i.y*.5+.5)*this.h]}render(o){let{w:t,h:s,ctx:i}=this,r=qe(t,s);if(z.lowPower&&!this.lowPowerApplied){this.lowPowerApplied=true;let[h,c]=this.css;return this.w=this.h=0,this.resize(h,c),false}if(!Ye(this.scene,this.camera))return false;if(i.clearRect(0,0,t,s),this.group.updateMatrixWorld(),this.state.shadow){let h=this.v.set(0,Ae-.08,0).project(this.camera),c=(h.x*.5+.5)*t,l=(-h.y*.5+.5)*s,p=Math.max(0,o.y)*3,n=Math.min(t,s)*.26*(1-p*.25)*this.pop,a=i.createRadialGradient(c,l,0,c,l,n),u=x(this.body,[.05,.03,.02],.75);a.addColorStop(0,`rgba(${u[0]*255|0},${u[1]*255|0},${u[2]*255|0},${.42*(1-p*.4)})`),a.addColorStop(1,"rgba(0,0,0,0)"),i.fillStyle=a,i.save(),i.translate(c,l),i.scale(1,.22),i.translate(-c,-l),i.beginPath(),i.arc(c,l,n,0,Math.PI*2),i.fill(),i.restore();}return r.setViewport(0,0,t,s),r.setScissor(0,0,t,s),r.render(this.scene,this.camera),i.drawImage(r.domElement,0,Ie()-s,t,s,0,0,t,s),this.drawOverlays(),true}drawOverlays(){let o=this.overlay;if(o.dots+o.zz+o.hearts+o.waves+o.bang<.01)return;let{ctx:t}=this,s=this.t,i=z.reducedMotion,r=Math.min(this.w,this.h),h=x(this.body,[1,1,1],.22),c=G(h);if(t.save(),o.dots>.01){let[l,p]=this.project(.6,.92,.2),n=i?2:s*.55%1.6,a=[.014,.022,.052];for(let u=0;u<3;u++){let w=i?1:Pe(Ee((n-u*.18)/.32)),A=i?0:Math.sin(s*2.2+u)*r*.006,k=l+[0,.05,.12][u]*r,S=p-[0,.05,.12][u]*r+A,E=a[u]*r*w;if(!(E<=.2)&&(t.globalAlpha=o.dots*.95,t.fillStyle=c,t.beginPath(),u<2?t.arc(k,S,E,0,Math.PI*2):xt(t,k,S,E),t.fill(),u===2&&w>.6)){t.fillStyle=G(x(this.body,[0,0,0],.35));for(let C=0;C<3;C++){let O=i?0:Math.max(0,Math.sin(s*7-C*.9))*E*.16;t.beginPath(),t.arc(k+(C-1)*E*.34,S-O,E*.1,0,Math.PI*2),t.fill();}}}}if(o.zz>.01){let[l,p]=this.project(.45,.85,.2);t.textAlign="center",t.textBaseline="middle",t.fillStyle=c;for(let n=0;n<3;n++){let a=i?.25+n*.25:(s*.33+n/3)%1,u=l+a*r*.17+Math.sin(a*7+n)*r*.018,w=p-Te(a)*r*.26;t.globalAlpha=o.zz*Math.min(1,a*5)*(1-a)*1.3,t.save(),t.translate(u,w),t.rotate(-0.25+Math.sin(a*5+n)*.22),t.font=`800 ${Math.round(r*(.05+.06*a))}px ui-rounded, system-ui, sans-serif`,t.fillText("z",0,0),t.restore();}}if(o.hearts>.01){let[l,p]=this.project(0,.8,.2),n=x(this.cheek,[1,.3,.45],.55);for(let a=0;a<4;a++){let u=i?.4:(s*.3+a/4)%1,w=a%2?1:-1,A=l+w*r*(.16+.07*(a>>1))+Math.sin(u*6+a)*r*.025,k=p-r*.02-Te(u)*r*.28,S=i?1:Pe(Ee(u/.22)),E=r*(.026+.008*(a%3))*S;t.globalAlpha=o.hearts*(1-Math.max(0,(u-.6)/.4)),t.save(),t.translate(A,k),t.rotate(Math.sin(s*3+a)*.25);let C=t.createLinearGradient(0,-E,0,E);C.addColorStop(0,G(x(n,[1,1,1],.35))),C.addColorStop(1,G(n)),t.fillStyle=C,ge(t,0,0,E),t.fill(),t.restore();}}if(o.waves>.01){t.lineCap="round",t.strokeStyle=c;for(let l of [-1,1]){let[p,n]=this.project(l*1,.12,0);for(let a=0;a<3;a++){let u=i?.3+a*.2:(s*.8+a/3)%1,w=Te(u);t.globalAlpha=o.waves*Math.sin(Math.PI*u)*.9,t.lineWidth=r*(.016-.008*u),t.beginPath();let A=r*(.03+.11*w);l>0?t.arc(p-r*.03,n,A,-0.55,.55):t.arc(p+r*.03,n,A,Math.PI-.55,Math.PI+.55),t.stroke();}}}if(o.bang>.01){let[l,p]=this.project(.55,1,.2),n=i?1:Pe(Ee(this.tau/.35));t.globalAlpha=o.bang,t.fillStyle=c,t.save(),t.translate(l,p),t.rotate(.18),t.scale(n,n),t.beginPath(),t.roundRect(-r*.016,-r*.1,r*.032,r*.07,r*.016),t.arc(0,r*.005,r*.018,0,Math.PI*2),t.fill(),t.restore();}t.restore();}},Ee=e=>e<0?0:e>1?1:e,Te=e=>1-(1-e)*(1-e),Pe=e=>1+(1.9+1)*Math.pow(e-1,3)+1.9*Math.pow(e-1,2);function xt(e,o,t,s){e.ellipse(o,t,s,s*.72,0,0,Math.PI*2),e.moveTo(o-s*.35+s*.45,t-s*.38),e.arc(o-s*.35,t-s*.38,s*.45,0,Math.PI*2),e.moveTo(o+s*.3+s*.5,t-s*.32),e.arc(o+s*.3,t-s*.32,s*.5,0,Math.PI*2);}var Rt=["shape","mood","color","face-color","cheek-color","size","speed","follow-cursor","interactive","shadow","sound"],N=(e,o)=>e===null?o:e!=="false"&&e!=="0",Ke=e=>e==null||e===""?`${M.size}px`:typeof e=="number"||/^\d+(\.\d+)?$/.test(String(e))?`${e}px`:String(e),Ft=`:host{display:inline-block;position:relative;cursor:pointer;width:var(--moodi-size);height:var(--moodi-size);vertical-align:middle;contain:layout paint style;-webkit-tap-highlight-color:transparent}
|
|
24
24
|
:host([interactive="false"]){pointer-events:none}
|
|
25
|
-
canvas{display:block;width:100%;height:100%}
|
|
25
|
+
canvas{position:absolute;inset:0;display:block;width:100%;height:100%}
|
|
26
|
+
slot{display:block;width:100%;height:100%}
|
|
27
|
+
::slotted(img){display:block;width:100%;height:100%;object-fit:contain}
|
|
28
|
+
:host([ready]) slot{display:none}`,zt=typeof HTMLElement>"u"?class{}:HTMLElement,Z=class extends zt{constructor(){super();this.buddy=null;this.onEnter=()=>{this.buddy&&(this.buddy.hovering=true);};this.onLeave=()=>{this.buddy&&(this.buddy.hovering=false);};this.onClick=()=>{this.buddy?.state.interactive&&this.react(Math.random()<.5?"happy":"surprised",{duration:1100});};this.root=this.attachShadow({mode:"open"});let t=document.createElement("style");t.textContent=Ft,this.root.append(t),this.style.setProperty("--moodi-size",Ke(this.getAttribute("size")));}connectedCallback(){if(this.hasAttribute("role")||this.setAttribute("role","img"),!this.buddy){let t=this.buddy=new he;t.onMoodChange=s=>{this.syncLabel(s.mood),this.dispatchEvent(new CustomEvent("moodchange",{detail:s,bubbles:true}));},t.onReady=()=>{this.setAttribute("ready",""),this.dispatchEvent(new Event("ready"));},this.root.append(t.canvas),t.update(this.readState()),this.addEventListener("pointerenter",this.onEnter),this.addEventListener("pointerleave",this.onLeave),this.addEventListener("click",this.onClick);}this.syncLabel(this.buddy.currentMood),this.buddy.connect(),this.io=new IntersectionObserver(([t])=>this.buddy?.setVisible(t.isIntersecting),{rootMargin:"64px"}),this.io.observe(this),this.ro=new ResizeObserver(([t])=>{let s=t.contentRect;this.buddy?.resize(s.width,s.height);}),this.ro.observe(this);}disconnectedCallback(){this.io?.disconnect(),this.ro?.disconnect(),this.buddy&&(this.buddy.canvas.remove(),this.buddy.dispose(),this.buddy=null),this.removeEventListener("pointerenter",this.onEnter),this.removeEventListener("pointerleave",this.onLeave),this.removeEventListener("click",this.onClick);}attributeChangedCallback(t,s,i){s!==i&&(t==="size"&&(this.style.setProperty("--moodi-size",Ke(i)),s!==null&&this.buddy?.bump()),t!=="aria-label"&&(this.buddy?.update(this.readState()),t==="mood"&&this.buddy&&this.syncLabel(this.buddy.currentMood)));}readState(){let t=this.getAttribute("shape"),s=this.getAttribute("mood"),i=parseFloat(this.getAttribute("speed")??"");return {shape:ce(t)?t:M.shape,mood:de(s)?s:M.mood,color:this.getAttribute("color")||M.color,faceColor:this.getAttribute("face-color")||void 0,cheekColor:this.getAttribute("cheek-color")||void 0,speed:Number.isFinite(i)?Math.max(0,i):M.speed,followCursor:N(this.getAttribute("follow-cursor"),M.followCursor),interactive:N(this.getAttribute("interactive"),M.interactive),shadow:N(this.getAttribute("shadow"),M.shadow),sound:N(this.getAttribute("sound"),M.sound)}}syncLabel(t){let s=this.getAttribute("aria-label");s&&!s.startsWith("moodi: ")||this.setAttribute("aria-label",`moodi: ${t}`);}react(t,s={}){this.buddy?.react(t,s.duration??1200);}get shape(){let t=this.getAttribute("shape");return ce(t)?t:M.shape}set shape(t){this.setAttribute("shape",t);}get mood(){let t=this.getAttribute("mood");return de(t)?t:M.mood}set mood(t){this.setAttribute("mood",t);}get color(){return this.getAttribute("color")||M.color}set color(t){this.setAttribute("color",t);}get faceColor(){return this.getAttribute("face-color")||void 0}set faceColor(t){t?this.setAttribute("face-color",t):this.removeAttribute("face-color");}get cheekColor(){return this.getAttribute("cheek-color")||void 0}set cheekColor(t){t?this.setAttribute("cheek-color",t):this.removeAttribute("cheek-color");}get size(){let t=this.getAttribute("size");return t?/^\d+(\.\d+)?$/.test(t)?Number(t):t:M.size}set size(t){this.setAttribute("size",String(t));}get speed(){return this.readState().speed}set speed(t){this.setAttribute("speed",String(t));}get followCursor(){return N(this.getAttribute("follow-cursor"),M.followCursor)}set followCursor(t){this.setAttribute("follow-cursor",String(t));}get interactive(){return N(this.getAttribute("interactive"),M.interactive)}set interactive(t){this.setAttribute("interactive",String(t));}get shadow(){return N(this.getAttribute("shadow"),M.shadow)}set shadow(t){this.setAttribute("shadow",String(t));}get sound(){return N(this.getAttribute("sound"),M.sound)}set sound(t){this.setAttribute("sound",String(t));}};Z.observedAttributes=Rt;function Ue(e="moodi-buddy"){typeof window>"u"||!("customElements"in window)||customElements.get(e)||customElements.define(e,class extends Z{});}Ue();export{M as DEFAULTS,Ze as MOODS,Bt as MOOD_INFO,Z as MoodiBuddyElement,Ot as PALETTES,Je as SHAPES,Lt as SHAPE_LABELS,Ue as defineMoodi,de as isMood,ce as isShape};
|
package/dist/react.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
"use strict";var
|
|
2
|
+
"use strict";var R=Object.create;var c=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var N=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var q=(e,o)=>{for(var t in o)c(e,t,{get:o[t],enumerable:!0})},y=(e,o,t,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let d of F(o))!j.call(e,d)&&d!==t&&c(e,d,{get:()=>o[d],enumerable:!(i=O(o,d))||i.enumerable});return e};var W=(e,o,t)=>(t=e!=null?R(N(e)):{},y(o||!e||!e.__esModule?c(t,"default",{value:e,enumerable:!0}):t,e)),_=e=>y(c({},"__esModule",{value:!0}),e);var X={};q(X,{DEFAULTS:()=>$,MOODS:()=>S,MOOD_INFO:()=>G,Moodi:()=>V,PALETTES:()=>z,SHAPES:()=>M,SHAPE_LABELS:()=>U,isMood:()=>K,isShape:()=>J,loadMoodi:()=>l});module.exports=_(X);var n=require("react");var M=["ghost","pebble","blob","drop","bean","cube"],S=["idle","thinking","loading","listening","speaking","happy","surprised","sad","error","sleeping","love"],$={shape:"ghost",mood:"idle",color:"#6c5ce7",size:160,speed:1,followCursor:!0,interactive:!0,shadow:!0,sound:!1},z={Iris:"#6c5ce7",Peach:"#f2a07b",Sage:"#8fb996",Sky:"#8ec5e8",Lilac:"#b9a3e3",Butter:"#f3d36b",Coral:"#f07167",Ink:"#2b2d42",Cloud:"#ecebe8"},G={idle:{label:"Idle",description:"Breathing softly. Blinks now and then."},thinking:{label:"Thinking",description:"Eyes drift up. Little thoughts pop."},loading:{label:"Loading",description:"Half-lidded, wobbling patiently."},listening:{label:"Listening",description:"All eyes. Nods along to every word."},speaking:{label:"Speaking",description:"Chatty mouth, bouncing to the rhythm."},happy:{label:"Happy",description:"Bounces with joy. Cheeks glow."},surprised:{label:"Surprised",description:"Wide eyes, tiny o. Did not see that coming."},sad:{label:"Sad",description:"Slumps a little. Needs a minute."},error:{label:"Error",description:"Something broke. Shakes it off."},sleeping:{label:"Sleeping",description:"Deep, slow breaths. Do not disturb."},love:{label:"Love",description:"Heart eyes, floating on air."}},U={pebble:"Pebble",blob:"Blob",drop:"Drop",bean:"Bean",cube:"Cube",ghost:"Ghost"},J=e=>M.includes(e),K=e=>S.includes(e);var E=null,v=!1,l=()=>E??(E=import("moodi").then(e=>e.defineMoodi()));function Q(e){if(E||e==="eager")return void l();if(e==="idle"){typeof requestIdleCallback=="function"?requestIdleCallback(()=>l(),{timeout:1200}):setTimeout(l,1);return}if(v)return;v=!0;let o=["pointermove","pointerdown","touchstart","keydown","wheel","scroll"],t=()=>{o.forEach(i=>removeEventListener(i,t)),l()};o.forEach(i=>addEventListener(i,t,{passive:!0,once:!0}))}var u=e=>e===void 0?void 0:String(e),k=e=>e===void 0?"160px":typeof e=="number"||/^\d+(\.\d+)?$/.test(e)?`${e}px`:e,V=(0,n.forwardRef)(function({shape:o,mood:t,color:i,faceColor:d,cheekColor:w,size:a,speed:b,followCursor:x,interactive:L,shadow:C,sound:P,onMoodChange:g,onReady:s,poster:h,loading:f="idle",className:B,style:D,...A},H){let p=(0,n.useRef)(null);(0,n.useImperativeHandle)(H,()=>p.current,[]),(0,n.useEffect)(()=>{Q(f)},[f]),(0,n.useEffect)(()=>{let r=p.current;if(!(!r||!s))return r.hasAttribute("ready")?void s():(r.addEventListener("ready",s,{once:!0}),()=>r.removeEventListener("ready",s))},[s]),(0,n.useEffect)(()=>{let r=p.current;if(!r||!g)return;let m=I=>g(I.detail);return r.addEventListener("moodchange",m),()=>r.removeEventListener("moodchange",m)},[g]);let T=h?(0,n.createElement)("img",{src:h,alt:"","aria-hidden":!0,decoding:"async",draggable:!1,style:{display:"block",width:"100%",height:"100%",objectFit:"contain"}}):null;return(0,n.createElement)("moodi-buddy",{ref:p,className:B,shape:o,mood:t,color:i,"face-color":d,"cheek-color":w,size:a===void 0?void 0:String(a),speed:b===void 0?void 0:String(b),"follow-cursor":u(x),interactive:u(L),shadow:u(C),sound:u(P),style:{display:"inline-block",width:k(a),height:k(a),verticalAlign:"middle",...D},suppressHydrationWarning:!0,...A},T)});0&&(module.exports={DEFAULTS,MOODS,MOOD_INFO,Moodi,PALETTES,SHAPES,SHAPE_LABELS,isMood,isShape,loadMoodi});
|
package/dist/react.d.cts
CHANGED
|
@@ -24,6 +24,8 @@ interface MoodiProps {
|
|
|
24
24
|
interactive?: boolean;
|
|
25
25
|
/** Soft ground shadow. Default `true`. */
|
|
26
26
|
shadow?: boolean;
|
|
27
|
+
/** A short sound effect on every mood change (and when it first appears). Default `false`. */
|
|
28
|
+
sound?: boolean;
|
|
27
29
|
}
|
|
28
30
|
interface MoodChangeDetail {
|
|
29
31
|
mood: Mood;
|
|
@@ -42,6 +44,7 @@ declare const DEFAULTS: {
|
|
|
42
44
|
followCursor: boolean;
|
|
43
45
|
interactive: boolean;
|
|
44
46
|
shadow: boolean;
|
|
47
|
+
sound: boolean;
|
|
45
48
|
};
|
|
46
49
|
declare const PALETTES: Readonly<Record<string, string>>;
|
|
47
50
|
declare const MOOD_INFO: Readonly<Record<Mood, {
|
|
@@ -54,7 +57,7 @@ declare const isMood: (v: unknown) => v is Mood;
|
|
|
54
57
|
|
|
55
58
|
declare const Base: typeof HTMLElement;
|
|
56
59
|
declare class MoodiBuddyElement extends Base {
|
|
57
|
-
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow"];
|
|
60
|
+
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow", "sound"];
|
|
58
61
|
private buddy;
|
|
59
62
|
private io?;
|
|
60
63
|
private ro?;
|
|
@@ -92,15 +95,27 @@ declare class MoodiBuddyElement extends Base {
|
|
|
92
95
|
set interactive(v: boolean);
|
|
93
96
|
get shadow(): boolean;
|
|
94
97
|
set shadow(v: boolean);
|
|
98
|
+
get sound(): boolean;
|
|
99
|
+
set sound(v: boolean);
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
type MoodiLoading = 'idle' | 'interaction' | 'eager';
|
|
97
103
|
interface MoodiReactProps extends MoodiProps, Omit<HTMLAttributes<HTMLElement>, keyof MoodiProps | 'children'> {
|
|
98
104
|
onMoodChange?: (detail: MoodChangeDetail) => void;
|
|
105
|
+
/** Fires once the first 3D frame is on screen. */
|
|
106
|
+
onReady?: () => void;
|
|
107
|
+
/**
|
|
108
|
+
* Image shown until the first 3D frame (and while three.js loads). A pre-rendered
|
|
109
|
+
* still of the same buddy makes the swap invisible.
|
|
110
|
+
*/
|
|
111
|
+
poster?: string;
|
|
112
|
+
/**
|
|
113
|
+
* When to load three.js: `'idle'` (default) after the page settles, `'interaction'`
|
|
114
|
+
* on the first pointer, touch, key or scroll (pair it with `poster`), `'eager'` now.
|
|
115
|
+
*/
|
|
116
|
+
loading?: MoodiLoading;
|
|
99
117
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Loads three.js and registers the element, in the browser only. Waits for an idle
|
|
102
|
-
* moment first so the page paints and hydrates before the 3D work starts.
|
|
103
|
-
*/
|
|
118
|
+
/** Loads three.js and registers the element. Browser only; safe to call many times. */
|
|
104
119
|
declare const loadMoodi: () => Promise<unknown>;
|
|
105
120
|
/**
|
|
106
121
|
* `<Moodi />` renders a `<moodi-buddy>` element. three.js is loaded lazily on the
|
|
@@ -108,4 +123,4 @@ declare const loadMoodi: () => Promise<unknown>;
|
|
|
108
123
|
*/
|
|
109
124
|
declare const Moodi: react.ForwardRefExoticComponent<MoodiReactProps & react.RefAttributes<MoodiBuddyElement | null>>;
|
|
110
125
|
|
|
111
|
-
export { DEFAULTS, MOODS, MOOD_INFO, type Mood, type MoodChangeDetail, Moodi, MoodiBuddyElement, type MoodiProps, type MoodiReactProps, PALETTES, SHAPES, SHAPE_LABELS, type Shape, isMood, isShape, loadMoodi };
|
|
126
|
+
export { DEFAULTS, MOODS, MOOD_INFO, type Mood, type MoodChangeDetail, Moodi, MoodiBuddyElement, type MoodiLoading, type MoodiProps, type MoodiReactProps, PALETTES, SHAPES, SHAPE_LABELS, type Shape, isMood, isShape, loadMoodi };
|
package/dist/react.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ interface MoodiProps {
|
|
|
24
24
|
interactive?: boolean;
|
|
25
25
|
/** Soft ground shadow. Default `true`. */
|
|
26
26
|
shadow?: boolean;
|
|
27
|
+
/** A short sound effect on every mood change (and when it first appears). Default `false`. */
|
|
28
|
+
sound?: boolean;
|
|
27
29
|
}
|
|
28
30
|
interface MoodChangeDetail {
|
|
29
31
|
mood: Mood;
|
|
@@ -42,6 +44,7 @@ declare const DEFAULTS: {
|
|
|
42
44
|
followCursor: boolean;
|
|
43
45
|
interactive: boolean;
|
|
44
46
|
shadow: boolean;
|
|
47
|
+
sound: boolean;
|
|
45
48
|
};
|
|
46
49
|
declare const PALETTES: Readonly<Record<string, string>>;
|
|
47
50
|
declare const MOOD_INFO: Readonly<Record<Mood, {
|
|
@@ -54,7 +57,7 @@ declare const isMood: (v: unknown) => v is Mood;
|
|
|
54
57
|
|
|
55
58
|
declare const Base: typeof HTMLElement;
|
|
56
59
|
declare class MoodiBuddyElement extends Base {
|
|
57
|
-
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow"];
|
|
60
|
+
static observedAttributes: readonly ["shape", "mood", "color", "face-color", "cheek-color", "size", "speed", "follow-cursor", "interactive", "shadow", "sound"];
|
|
58
61
|
private buddy;
|
|
59
62
|
private io?;
|
|
60
63
|
private ro?;
|
|
@@ -92,15 +95,27 @@ declare class MoodiBuddyElement extends Base {
|
|
|
92
95
|
set interactive(v: boolean);
|
|
93
96
|
get shadow(): boolean;
|
|
94
97
|
set shadow(v: boolean);
|
|
98
|
+
get sound(): boolean;
|
|
99
|
+
set sound(v: boolean);
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
type MoodiLoading = 'idle' | 'interaction' | 'eager';
|
|
97
103
|
interface MoodiReactProps extends MoodiProps, Omit<HTMLAttributes<HTMLElement>, keyof MoodiProps | 'children'> {
|
|
98
104
|
onMoodChange?: (detail: MoodChangeDetail) => void;
|
|
105
|
+
/** Fires once the first 3D frame is on screen. */
|
|
106
|
+
onReady?: () => void;
|
|
107
|
+
/**
|
|
108
|
+
* Image shown until the first 3D frame (and while three.js loads). A pre-rendered
|
|
109
|
+
* still of the same buddy makes the swap invisible.
|
|
110
|
+
*/
|
|
111
|
+
poster?: string;
|
|
112
|
+
/**
|
|
113
|
+
* When to load three.js: `'idle'` (default) after the page settles, `'interaction'`
|
|
114
|
+
* on the first pointer, touch, key or scroll (pair it with `poster`), `'eager'` now.
|
|
115
|
+
*/
|
|
116
|
+
loading?: MoodiLoading;
|
|
99
117
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Loads three.js and registers the element, in the browser only. Waits for an idle
|
|
102
|
-
* moment first so the page paints and hydrates before the 3D work starts.
|
|
103
|
-
*/
|
|
118
|
+
/** Loads three.js and registers the element. Browser only; safe to call many times. */
|
|
104
119
|
declare const loadMoodi: () => Promise<unknown>;
|
|
105
120
|
/**
|
|
106
121
|
* `<Moodi />` renders a `<moodi-buddy>` element. three.js is loaded lazily on the
|
|
@@ -108,4 +123,4 @@ declare const loadMoodi: () => Promise<unknown>;
|
|
|
108
123
|
*/
|
|
109
124
|
declare const Moodi: react.ForwardRefExoticComponent<MoodiReactProps & react.RefAttributes<MoodiBuddyElement | null>>;
|
|
110
125
|
|
|
111
|
-
export { DEFAULTS, MOODS, MOOD_INFO, type Mood, type MoodChangeDetail, Moodi, MoodiBuddyElement, type MoodiProps, type MoodiReactProps, PALETTES, SHAPES, SHAPE_LABELS, type Shape, isMood, isShape, loadMoodi };
|
|
126
|
+
export { DEFAULTS, MOODS, MOOD_INFO, type Mood, type MoodChangeDetail, Moodi, MoodiBuddyElement, type MoodiLoading, type MoodiProps, type MoodiReactProps, PALETTES, SHAPES, SHAPE_LABELS, type Shape, isMood, isShape, loadMoodi };
|
package/dist/react.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{createElement as
|
|
2
|
+
import{createElement as f,forwardRef as I,useEffect as c,useImperativeHandle as R,useRef as O}from"react";var H=["ghost","pebble","blob","drop","bean","cube"],T=["idle","thinking","loading","listening","speaking","happy","surprised","sad","error","sleeping","love"],N={shape:"ghost",mood:"idle",color:"#6c5ce7",size:160,speed:1,followCursor:!0,interactive:!0,shadow:!0,sound:!1},j={Iris:"#6c5ce7",Peach:"#f2a07b",Sage:"#8fb996",Sky:"#8ec5e8",Lilac:"#b9a3e3",Butter:"#f3d36b",Coral:"#f07167",Ink:"#2b2d42",Cloud:"#ecebe8"},q={idle:{label:"Idle",description:"Breathing softly. Blinks now and then."},thinking:{label:"Thinking",description:"Eyes drift up. Little thoughts pop."},loading:{label:"Loading",description:"Half-lidded, wobbling patiently."},listening:{label:"Listening",description:"All eyes. Nods along to every word."},speaking:{label:"Speaking",description:"Chatty mouth, bouncing to the rhythm."},happy:{label:"Happy",description:"Bounces with joy. Cheeks glow."},surprised:{label:"Surprised",description:"Wide eyes, tiny o. Did not see that coming."},sad:{label:"Sad",description:"Slumps a little. Needs a minute."},error:{label:"Error",description:"Something broke. Shakes it off."},sleeping:{label:"Sleeping",description:"Deep, slow breaths. Do not disturb."},love:{label:"Love",description:"Heart eyes, floating on air."}},W={pebble:"Pebble",blob:"Blob",drop:"Drop",bean:"Bean",cube:"Cube",ghost:"Ghost"},_=e=>H.includes(e),$=e=>T.includes(e);var M=null,m=!1,l=()=>M??(M=import("moodi").then(e=>e.defineMoodi()));function F(e){if(M||e==="eager")return void l();if(e==="idle"){typeof requestIdleCallback=="function"?requestIdleCallback(()=>l(),{timeout:1200}):setTimeout(l,1);return}if(m)return;m=!0;let i=["pointermove","pointerdown","touchstart","keydown","wheel","scroll"],r=()=>{i.forEach(t=>removeEventListener(t,r)),l()};i.forEach(t=>addEventListener(t,r,{passive:!0,once:!0}))}var a=e=>e===void 0?void 0:String(e),y=e=>e===void 0?"160px":typeof e=="number"||/^\d+(\.\d+)?$/.test(e)?`${e}px`:e,U=I(function({shape:i,mood:r,color:t,faceColor:S,cheekColor:v,size:d,speed:u,followCursor:k,interactive:E,shadow:w,sound:x,onMoodChange:p,onReady:n,poster:g,loading:b="idle",className:L,style:C,...P},B){let s=O(null);R(B,()=>s.current,[]),c(()=>{F(b)},[b]),c(()=>{let o=s.current;if(!(!o||!n))return o.hasAttribute("ready")?void n():(o.addEventListener("ready",n,{once:!0}),()=>o.removeEventListener("ready",n))},[n]),c(()=>{let o=s.current;if(!o||!p)return;let h=A=>p(A.detail);return o.addEventListener("moodchange",h),()=>o.removeEventListener("moodchange",h)},[p]);let D=g?f("img",{src:g,alt:"","aria-hidden":!0,decoding:"async",draggable:!1,style:{display:"block",width:"100%",height:"100%",objectFit:"contain"}}):null;return f("moodi-buddy",{ref:s,className:L,shape:i,mood:r,color:t,"face-color":S,"cheek-color":v,size:d===void 0?void 0:String(d),speed:u===void 0?void 0:String(u),"follow-cursor":a(k),interactive:a(E),shadow:a(w),sound:a(x),style:{display:"inline-block",width:y(d),height:y(d),verticalAlign:"middle",...C},suppressHydrationWarning:!0,...P},D)});export{N as DEFAULTS,T as MOODS,q as MOOD_INFO,U as Moodi,j as PALETTES,H as SHAPES,W as SHAPE_LABELS,$ as isMood,_ as isShape,l as loadMoodi};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moodi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Tiny 3D mood buddies for your UI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"url": "git+https://github.com/korneXD/moodi.git",
|
|
62
62
|
"directory": "packages/moodi"
|
|
63
63
|
},
|
|
64
|
-
"homepage": "https://
|
|
64
|
+
"homepage": "https://www.getmoodi.dev",
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "tsup",
|
|
67
67
|
"dev": "tsup --watch",
|