@botuyo/chat-widget-standalone 1.0.58 → 1.0.60
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/dist/Avatar3D-LfNJe183.js +9 -0
- package/dist/Avatar3D-LfNJe183.js.map +1 -0
- package/dist/botuyo-chat.es.js +18 -18
- package/dist/botuyo-chat.es.js.map +1 -1
- package/dist/botuyo-chat.umd.js +1 -1
- package/dist/botuyo-chat.umd.js.map +1 -1
- package/dist/{chunk-audio-DHSLSWCh.js → chunk-audio-CjO94O6M.js} +2 -2
- package/dist/{chunk-audio-DHSLSWCh.js.map → chunk-audio-CjO94O6M.js.map} +1 -1
- package/dist/{chunk-chat-ui-CSLZEQt6.js → chunk-chat-ui-D94hKAOR.js} +41 -41
- package/dist/chunk-chat-ui-D94hKAOR.js.map +1 -0
- package/dist/{chunk-gallery-DLFbfIkz.js → chunk-gallery-54UV-aGf.js} +2 -2
- package/dist/{chunk-gallery-DLFbfIkz.js.map → chunk-gallery-54UV-aGf.js.map} +1 -1
- package/dist/src/chat-widget/components/ChatWindow.d.ts +1 -3
- package/dist/src/chat-widget/components/ChatWindow.d.ts.map +1 -1
- package/dist/src/chat-widget/components/ChatWindow.stories.d.ts +0 -4
- package/dist/src/chat-widget/components/ChatWindow.stories.d.ts.map +1 -1
- package/dist/src/chat-widget/hooks/useWidgetTheme.d.ts +1 -1
- package/dist/src/chat-widget/hooks/useWidgetTheme.d.ts.map +1 -1
- package/dist/src/chat-widget/types/index.d.ts +9 -11
- package/dist/src/chat-widget/types/index.d.ts.map +1 -1
- package/dist/src/chat-widget/utils/theme.d.ts +9 -3
- package/dist/src/chat-widget/utils/theme.d.ts.map +1 -1
- package/dist/src/chat-widget/utils/theme.examples.d.ts +2 -2
- package/dist/src/chat-widget/utils/theme.examples.d.ts.map +1 -1
- package/dist/standalone.d.ts.map +1 -1
- package/dist/stats-umd.html +1 -1
- package/dist/stats.html +1 -1
- package/package.json +1 -1
- package/dist/Avatar3D-B2RkgErq.js +0 -9
- package/dist/Avatar3D-B2RkgErq.js.map +0 -1
- package/dist/chunk-chat-ui-CSLZEQt6.js.map +0 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useMemo as r,useEffect as n,useRef as o}from"react";import{Canvas as a,useThree as i,useFrame as s}from"@react-three/fiber";import{VRMLoaderPlugin as c,VRMExpressionPresetName as l}from"@pixiv/three-vrm";import*as u from"three";import{GLTFLoader as p}from"three/examples/jsm/loaders/GLTFLoader.js";const d={default:{expressions:{[l.Neutral]:1}},happy:{expressions:{[l.Happy]:.8}},angry:{expressions:{[l.Angry]:.7},headRotation:[.05,0,0]},sorry:{expressions:{[l.Sad]:.6},headRotation:[-.08,0,0]},confused:{expressions:{[l.Sad]:.3},headRotation:[0,0,.08]},love:{expressions:{[l.Happy]:.9,[l.Relaxed]:.4}},thinking:{expressions:{[l.Neutral]:.6},headRotation:[.06,-.1,0]},writing:{expressions:{[l.Neutral]:.8},headRotation:[-.05,0,0]},wink:{expressions:{[l.Happy]:.5,[l.Blink]:.9}}};function h({url:e,emotion:t,callState:r,audioLevel:a}){const h=o(null),m=o(null),f=o(null),g=o(0),x=o(1),M=o(0),y=o(3.5),w=o(0),v=o({}),k=o({}),b=o(new u.Euler(0,0,0)),A=o(0),{scene:S,camera:V}=i();return n(()=>{const t=new p;return t.register(e=>new c(e)),t.load(e,e=>{const t=e.userData?.vrm;if(t){h.current=t,t.scene.rotation.y=Math.PI;const e=(new u.Box3).setFromObject(t.scene),r=e.getCenter(new u.Vector3),n=e.getSize(new u.Vector3).y;t.scene.position.y=-r.y-.05*n,A.current=t.scene.position.y,S.add(t.scene)}else{console.info("[Avatar3D] Plain GLB model detected, using fallback animations");const t=e.scene;t.updateMatrixWorld(!0);const r=(new u.Box3).setFromObject(t),n=r.getCenter(new u.Vector3),o=r.getSize(new u.Vector3),a=new u.Group;t.position.sub(n),a.add(t),g.current=0,m.current=a,A.current=0,x.current=1,S.add(a);const i=V.fov*(Math.PI/180),s=.35*o.y,c=.5*o.y/Math.tan(i/2);if(V.position.set(0,s,-c),V.lookAt(0,s,0),e.animations.length>0){const r=new u.AnimationMixer(t);f.current=r,e.animations.forEach(e=>{r.clipAction(e).play()})}}},void 0,e=>{console.error("[Avatar3D] Failed to load model:",e)}),()=>{h.current&&(S.remove(h.current.scene),h.current=null),m.current&&(S.remove(m.current),m.current=null),f.current&&(f.current.stopAllAction(),f.current=null)}},[e,S]),n(()=>{const e=d[t||"default"]||d.default;k.current=e.expressions,e.headRotation?b.current.set(...e.headRotation):b.current.set(0,0,0)},[t]),s((e,t)=>{const n=Math.min(t,.1);w.current+=1.2*n;const o=m.current;if(o){f.current&&f.current.update(n);const e=.003*Math.sin(w.current);o.position.y=A.current+e;const t=.02*Math.sin(.3*w.current);if(o.rotation.y=g.current+t,"speaking"===r&&a>.01){const e=1+.03*a,t=x.current*e,r=o.scale.x;o.scale.setScalar(u.MathUtils.lerp(r,t,.1))}else{const e=o.scale.x;Math.abs(e-x.current)>.001&&o.scale.setScalar(u.MathUtils.lerp(e,x.current,.05))}return}const i=h.current;if(!i)return;const s=.003*Math.sin(w.current);if(i.scene.position.y=A.current+s,M.current+=n,M.current>=y.current){const e=(M.current-y.current)/.15;e<1?i.expressionManager?.setValue(l.Blink,e):e<2?i.expressionManager?.setValue(l.Blink,2-e):(i.expressionManager?.setValue(l.Blink,0),M.current=0,y.current=4*Math.random()+2)}const c=4*n,p=/* @__PURE__ */new Set([...Object.keys(v.current),...Object.keys(k.current)]);for(const r of p){const e=v.current[r]||0,t=k.current[r]||0,n=u.MathUtils.lerp(e,t,c);v.current[r]=n,r===l.Blink&&M.current>=y.current||i.expressionManager?.setValue(r,n)}if("speaking"===r&&a>.01){const e=Math.min(1.5*a,.8),t=i.expressionManager?.getValue(l.Aa)||0,r=u.MathUtils.lerp(t,e,8*n);i.expressionManager?.setValue(l.Aa,r)}else{const e=i.expressionManager?.getValue(l.Aa)||0;e>.01&&i.expressionManager?.setValue(l.Aa,.9*e)}if(i.humanoid){const e=i.humanoid.getNormalizedBoneNode("head");if(e){const t=.01*Math.sin(.3*w.current),r=.005*Math.cos(.2*w.current),o=b.current.x+t,a=b.current.y,i=b.current.z+r;e.rotation.x=u.MathUtils.lerp(e.rotation.x,o,3*n),e.rotation.y=u.MathUtils.lerp(e.rotation.y,a,3*n),e.rotation.z=u.MathUtils.lerp(e.rotation.z,i,3*n)}}if("listening"===r&&i.humanoid){const e=i.humanoid.getNormalizedBoneNode("head");e&&(e.rotation.z=u.MathUtils.lerp(e.rotation.z,.03,2*n))}i.update(n)}),null}function m(){const{camera:e}=i();return n(()=>{e.position.set(0,.1,.6),e.lookAt(0,.1,0)},[e]),null}function f({modelUrl:n,emotion:o,callState:i,audioLevel:s,primaryColor:c,size:l}){const u=r(()=>{switch(i){case"speaking":return.6;case"listening":return.4;case"thinking":return.3;default:return.15}},[i]);/* @__PURE__ */
|
|
2
|
+
return e("div",{className:"relative flex flex-col items-center gap-4",children:/* @__PURE__ */e("div",{className:"relative rounded-full overflow-hidden",style:{width:`${l}px`,height:`${l}px`,boxShadow:"listening"===i||"speaking"===i?`0 0 0 3px ${c}50, 0 0 30px ${c}${Math.floor(255*u).toString(16).padStart(2,"0")}, 0 0 60px ${c}20`:`0 0 0 2px ${c}25`,transition:"box-shadow 0.5s ease"},children:/* @__PURE__ */t(a,{style:{width:"100%",height:"100%",background:"transparent"},gl:{alpha:!0,antialias:!0,preserveDrawingBuffer:!1},camera:{fov:30,near:.01,far:10},dpr:[1,2],children:[
|
|
3
|
+
/* @__PURE__ */e(m,{}),
|
|
4
|
+
/* @__PURE__ */e("ambientLight",{intensity:.6}),
|
|
5
|
+
/* @__PURE__ */e("directionalLight",{position:[1,2,3],intensity:.8,color:"#ffffff"}),
|
|
6
|
+
/* @__PURE__ */e("directionalLight",{position:[-1,1,-1],intensity:.3,color:"#b4c6e7"}),
|
|
7
|
+
/* @__PURE__ */e("pointLight",{position:[0,.5,-.5],intensity:2*u,color:c,distance:3}),
|
|
8
|
+
/* @__PURE__ */e(h,{url:n,emotion:o,callState:i,audioLevel:s})]})})})}export{f as default};
|
|
9
|
+
//# sourceMappingURL=Avatar3D-LfNJe183.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Avatar3D-LfNJe183.js","sources":["../src/chat-widget/components/Avatar3D.tsx"],"sourcesContent":["/**\r\n * @package @botuyo/chat-widget\r\n * Avatar3D Component — Lazy-loaded 3D avatar for voice calls\r\n *\r\n * Uses React Three Fiber + @pixiv/three-vrm to render VRM models\r\n * with emotion-driven blendshapes and idle animations.\r\n *\r\n * This component is loaded via React.lazy() — it adds 0KB to the\r\n * main bundle. Three.js + VRM are only downloaded when a tenant\r\n * has avatar3dUrl configured.\r\n */\r\n\r\n// React Three Fiber types are resolved via tsconfig\r\n'use client'\r\n\r\nimport { useRef, useEffect, useMemo } from 'react'\r\nimport { Canvas, useFrame, useThree } from '@react-three/fiber'\r\nimport { VRMLoaderPlugin, VRMExpressionPresetName, VRM } from '@pixiv/three-vrm'\r\nimport * as THREE from 'three'\r\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\r\n\r\n// ═══════════════════════════════════════\r\n// TYPES\r\n// ═══════════════════════════════════════\r\n\r\ntype CallState = 'idle' | 'connecting' | 'listening' | 'speaking' | 'thinking'\r\n\r\ninterface Avatar3DProps {\r\n modelUrl: string\r\n emotion: string | null\r\n callState: CallState\r\n audioLevel: number\r\n primaryColor: string\r\n size: number\r\n}\r\n\r\n// ═══════════════════════════════════════\r\n// EMOTION → VRM BLENDSHAPE MAPPING\r\n// ═══════════════════════════════════════\r\n\r\ninterface EmotionConfig {\r\n expressions: Record<string, number> // VRM expression name → weight 0-1\r\n headRotation?: [number, number, number] // euler XYZ in radians\r\n}\r\n\r\nconst EMOTION_MAP: Record<string, EmotionConfig> = {\r\n default: {\r\n expressions: { [VRMExpressionPresetName.Neutral]: 1 },\r\n },\r\n happy: {\r\n expressions: { [VRMExpressionPresetName.Happy]: 0.8 },\r\n },\r\n angry: {\r\n expressions: { [VRMExpressionPresetName.Angry]: 0.7 },\r\n headRotation: [0.05, 0, 0],\r\n },\r\n sorry: {\r\n expressions: { [VRMExpressionPresetName.Sad]: 0.6 },\r\n headRotation: [-0.08, 0, 0],\r\n },\r\n confused: {\r\n expressions: { [VRMExpressionPresetName.Sad]: 0.3 },\r\n headRotation: [0, 0, 0.08],\r\n },\r\n love: {\r\n expressions: { [VRMExpressionPresetName.Happy]: 0.9, [VRMExpressionPresetName.Relaxed]: 0.4 },\r\n },\r\n thinking: {\r\n expressions: { [VRMExpressionPresetName.Neutral]: 0.6 },\r\n headRotation: [0.06, -0.1, 0],\r\n },\r\n writing: {\r\n expressions: { [VRMExpressionPresetName.Neutral]: 0.8 },\r\n headRotation: [-0.05, 0, 0],\r\n },\r\n wink: {\r\n expressions: { [VRMExpressionPresetName.Happy]: 0.5, [VRMExpressionPresetName.Blink]: 0.9 },\r\n },\r\n}\r\n\r\n// ═══════════════════════════════════════\r\n// VRM MODEL COMPONENT (inside Canvas)\r\n// ═══════════════════════════════════════\r\n\r\ninterface VRMModelProps {\r\n url: string\r\n emotion: string | null\r\n callState: CallState\r\n audioLevel: number\r\n}\r\n\r\nfunction VRMModel({ url, emotion, callState, audioLevel }: VRMModelProps) {\r\n const vrmRef = useRef<VRM | null>(null)\r\n const glbSceneRef = useRef<THREE.Group | null>(null)\r\n const mixerRef = useRef<THREE.AnimationMixer | null>(null)\r\n const baseRotationY = useRef(0)\r\n const baseScale = useRef(1)\r\n const blinkTimer = useRef(0)\r\n const nextBlinkAt = useRef(3.5) // Initial value, randomized in useFrame\r\n const breathPhase = useRef(0)\r\n const currentExpressions = useRef<Record<string, number>>({})\r\n const targetExpressions = useRef<Record<string, number>>({})\r\n const headTarget = useRef(new THREE.Euler(0, 0, 0))\r\n const baseY = useRef(0)\r\n const { scene, camera } = useThree()\r\n\r\n // Load model using Three.js GLTFLoader (with VRM plugin for VRM models)\r\n useEffect(() => {\r\n const loader = new GLTFLoader()\r\n loader.register((parser) => new VRMLoaderPlugin(parser) as any)\r\n\r\n loader.load(\r\n url,\r\n (gltf) => {\r\n const vrm = (gltf as any).userData?.vrm as VRM | undefined\r\n if (vrm) {\r\n // ── VRM MODEL PATH ──\r\n vrmRef.current = vrm\r\n vrm.scene.rotation.y = Math.PI\r\n const box = new THREE.Box3().setFromObject(vrm.scene)\r\n const center = box.getCenter(new THREE.Vector3())\r\n const height = box.getSize(new THREE.Vector3()).y\r\n vrm.scene.position.y = -center.y - height * 0.05\r\n baseY.current = vrm.scene.position.y\r\n scene.add(vrm.scene)\r\n } else {\r\n // ── PLAIN GLB FALLBACK ──\r\n console.info('[Avatar3D] Plain GLB model detected, using fallback animations')\r\n const model = gltf.scene\r\n\r\n // Force world matrix update so bbox is accurate\r\n model.updateMatrixWorld(true)\r\n const box = new THREE.Box3().setFromObject(model)\r\n const center = box.getCenter(new THREE.Vector3())\r\n const size = box.getSize(new THREE.Vector3())\r\n\r\n // Create pivot wrapper for proper rotation around geometric center\r\n const pivot = new THREE.Group()\r\n\r\n // Center model geometry at pivot origin\r\n model.position.sub(center)\r\n pivot.add(model)\r\n\r\n // Don't rotate the model — move the CAMERA to the front instead\r\n baseRotationY.current = 0\r\n\r\n glbSceneRef.current = pivot\r\n baseY.current = 0\r\n baseScale.current = 1\r\n scene.add(pivot)\r\n\r\n // Frame the HEAD: camera at -Z (model faces -Z based on tests)\r\n const fov = (camera as THREE.PerspectiveCamera).fov * (Math.PI / 180)\r\n const headY = size.y * 0.35\r\n const cameraZ = (size.y * 0.5) / Math.tan(fov / 2)\r\n // Camera on the -Z side to see the model's FRONT\r\n camera.position.set(0, headY, -cameraZ)\r\n camera.lookAt(0, headY, 0)\r\n\r\n // Play embedded animations if available\r\n if (gltf.animations.length > 0) {\r\n const mixer = new THREE.AnimationMixer(model)\r\n mixerRef.current = mixer\r\n gltf.animations.forEach((clip) => {\r\n mixer.clipAction(clip).play()\r\n })\r\n }\r\n }\r\n },\r\n undefined,\r\n (error) => {\r\n console.error('[Avatar3D] Failed to load model:', error)\r\n },\r\n )\r\n\r\n return () => {\r\n if (vrmRef.current) {\r\n scene.remove(vrmRef.current.scene)\r\n vrmRef.current = null\r\n }\r\n if (glbSceneRef.current) {\r\n scene.remove(glbSceneRef.current)\r\n glbSceneRef.current = null\r\n }\r\n if (mixerRef.current) {\r\n mixerRef.current.stopAllAction()\r\n mixerRef.current = null\r\n }\r\n }\r\n }, [url, scene])\r\n\r\n // Update target expressions when emotion changes (VRM only)\r\n useEffect(() => {\r\n const config = EMOTION_MAP[emotion || 'default'] || EMOTION_MAP.default\r\n targetExpressions.current = config.expressions\r\n if (config.headRotation) {\r\n headTarget.current.set(...config.headRotation)\r\n } else {\r\n headTarget.current.set(0, 0, 0)\r\n }\r\n }, [emotion])\r\n\r\n // Animation loop\r\n useFrame((_, delta) => {\r\n const dt = Math.min(delta, 0.1) // Cap delta to avoid jumps\r\n breathPhase.current += dt * 1.2\r\n\r\n // ── GLB FALLBACK ANIMATION ──\r\n const glb = glbSceneRef.current\r\n if (glb) {\r\n // Update animation mixer if present\r\n if (mixerRef.current) {\r\n mixerRef.current.update(dt)\r\n }\r\n\r\n // Gentle breathing bob\r\n const breathOffset = Math.sin(breathPhase.current) * 0.003\r\n glb.position.y = baseY.current + breathOffset\r\n\r\n // Subtle idle rotation (relative to base facing direction)\r\n const idleSway = Math.sin(breathPhase.current * 0.3) * 0.02\r\n glb.rotation.y = baseRotationY.current + idleSway\r\n\r\n // Audio-reactive scale pulse when speaking\r\n if (callState === 'speaking' && audioLevel > 0.01) {\r\n const pulse = 1 + audioLevel * 0.03\r\n const targetScale = baseScale.current * pulse\r\n const currentScale = glb.scale.x\r\n glb.scale.setScalar(THREE.MathUtils.lerp(currentScale, targetScale, 0.1))\r\n } else {\r\n // Return to base scale smoothly\r\n const currentScale = glb.scale.x\r\n if (Math.abs(currentScale - baseScale.current) > 0.001) {\r\n glb.scale.setScalar(THREE.MathUtils.lerp(currentScale, baseScale.current, 0.05))\r\n }\r\n }\r\n\r\n return // Skip VRM-specific code\r\n }\r\n\r\n // ── VRM ANIMATION (original code) ──\r\n const vrm = vrmRef.current\r\n if (!vrm) return\r\n\r\n // ── BREATHING ──\r\n const breathOffset = Math.sin(breathPhase.current) * 0.003\r\n vrm.scene.position.y = baseY.current + breathOffset\r\n\r\n // ── BLINKING ──\r\n blinkTimer.current += dt\r\n if (blinkTimer.current >= nextBlinkAt.current) {\r\n const blinkProgress = (blinkTimer.current - nextBlinkAt.current) / 0.15\r\n if (blinkProgress < 1) {\r\n vrm.expressionManager?.setValue(VRMExpressionPresetName.Blink, blinkProgress)\r\n } else if (blinkProgress < 2) {\r\n vrm.expressionManager?.setValue(VRMExpressionPresetName.Blink, 2 - blinkProgress)\r\n } else {\r\n vrm.expressionManager?.setValue(VRMExpressionPresetName.Blink, 0)\r\n blinkTimer.current = 0\r\n nextBlinkAt.current = Math.random() * 4 + 2\r\n }\r\n }\r\n\r\n // ── EMOTION BLENDING ──\r\n const lerpSpeed = 4 * dt\r\n const allKeys = new Set([\r\n ...Object.keys(currentExpressions.current),\r\n ...Object.keys(targetExpressions.current),\r\n ])\r\n\r\n for (const key of allKeys) {\r\n const current = currentExpressions.current[key] || 0\r\n const target = targetExpressions.current[key] || 0\r\n const next = THREE.MathUtils.lerp(current, target, lerpSpeed)\r\n currentExpressions.current[key] = next\r\n\r\n // Don't override blink during active blink animation\r\n if (key === VRMExpressionPresetName.Blink && blinkTimer.current >= nextBlinkAt.current) continue\r\n\r\n vrm.expressionManager?.setValue(key, next)\r\n }\r\n\r\n // ── SPEAKING MOUTH ──\r\n if (callState === 'speaking' && audioLevel > 0.01) {\r\n const mouthTarget = Math.min(audioLevel * 1.5, 0.8)\r\n const currentMouth = vrm.expressionManager?.getValue(VRMExpressionPresetName.Aa) || 0\r\n const smoothMouth = THREE.MathUtils.lerp(currentMouth, mouthTarget, 8 * dt)\r\n vrm.expressionManager?.setValue(VRMExpressionPresetName.Aa, smoothMouth)\r\n } else {\r\n const currentMouth = vrm.expressionManager?.getValue(VRMExpressionPresetName.Aa) || 0\r\n if (currentMouth > 0.01) {\r\n vrm.expressionManager?.setValue(VRMExpressionPresetName.Aa, currentMouth * 0.9)\r\n }\r\n }\r\n\r\n // ── HEAD MOVEMENT ──\r\n if (vrm.humanoid) {\r\n const head = vrm.humanoid.getNormalizedBoneNode('head')\r\n if (head) {\r\n const idleSwayX = Math.sin(breathPhase.current * 0.3) * 0.01\r\n const idleSwayZ = Math.cos(breathPhase.current * 0.2) * 0.005\r\n\r\n const targetX = headTarget.current.x + idleSwayX\r\n const targetY = headTarget.current.y\r\n const targetZ = headTarget.current.z + idleSwayZ\r\n\r\n head.rotation.x = THREE.MathUtils.lerp(head.rotation.x, targetX, 3 * dt)\r\n head.rotation.y = THREE.MathUtils.lerp(head.rotation.y, targetY, 3 * dt)\r\n head.rotation.z = THREE.MathUtils.lerp(head.rotation.z, targetZ, 3 * dt)\r\n }\r\n }\r\n\r\n // ── CALL STATE REACTIONS ──\r\n if (callState === 'listening' && vrm.humanoid) {\r\n const head = vrm.humanoid.getNormalizedBoneNode('head')\r\n if (head) {\r\n head.rotation.z = THREE.MathUtils.lerp(head.rotation.z, 0.03, 2 * dt)\r\n }\r\n }\r\n\r\n // Update VRM (required for expression changes to take effect)\r\n vrm.update(dt)\r\n })\r\n\r\n return null\r\n}\r\n\r\n// ═══════════════════════════════════════\r\n// AUTO CAMERA FRAMING\r\n// ═══════════════════════════════════════\r\n\r\nfunction CameraSetup() {\r\n const { camera } = useThree()\r\n useEffect(() => {\r\n // Default camera position — will be overridden by model loader for GLB\r\n // For VRM: fixed head-level framing\r\n camera.position.set(0, 0.1, 0.6)\r\n camera.lookAt(0, 0.1, 0)\r\n }, [camera])\r\n return null\r\n}\r\n\r\n// ═══════════════════════════════════════\r\n// MAIN AVATAR 3D COMPONENT (exported)\r\n// ═══════════════════════════════════════\r\n\r\nexport default function Avatar3D({\r\n modelUrl,\r\n emotion,\r\n callState,\r\n audioLevel,\r\n primaryColor,\r\n size,\r\n}: Avatar3DProps) {\r\n const glowIntensity = useMemo(() => {\r\n switch (callState) {\r\n case 'speaking': return 0.6\r\n case 'listening': return 0.4\r\n case 'thinking': return 0.3\r\n default: return 0.15\r\n }\r\n }, [callState])\r\n\r\n const isActive = callState === 'listening' || callState === 'speaking'\r\n\r\n return (\r\n <div className=\"relative flex flex-col items-center gap-4\">\r\n {/* Glow ring behind canvas */}\r\n <div\r\n className=\"relative rounded-full overflow-hidden\"\r\n style={{\r\n width: `${size}px`,\r\n height: `${size}px`,\r\n boxShadow: isActive\r\n ? `0 0 0 3px ${primaryColor}50, 0 0 30px ${primaryColor}${Math.floor(glowIntensity * 255).toString(16).padStart(2, '0')}, 0 0 60px ${primaryColor}20`\r\n : `0 0 0 2px ${primaryColor}25`,\r\n transition: 'box-shadow 0.5s ease',\r\n }}\r\n >\r\n <Canvas\r\n style={{ width: '100%', height: '100%', background: 'transparent' }}\r\n gl={{ alpha: true, antialias: true, preserveDrawingBuffer: false }}\r\n camera={{ fov: 30, near: 0.01, far: 10 }}\r\n dpr={[1, 2]}\r\n >\r\n <CameraSetup />\r\n\r\n {/* Lighting — soft studio setup */}\r\n {/* @ts-ignore R3F IntrinsicElements conflict */}\r\n <ambientLight intensity={0.6} />\r\n {/* @ts-ignore */}\r\n <directionalLight position={[1, 2, 3]} intensity={0.8} color=\"#ffffff\" />\r\n {/* @ts-ignore */}\r\n <directionalLight position={[-1, 1, -1]} intensity={0.3} color=\"#b4c6e7\" />\r\n\r\n {/* Rim light for depth — matches primary color */}\r\n {/* @ts-ignore */}\r\n <pointLight\r\n position={[0, 0.5, -0.5]}\r\n intensity={glowIntensity * 2}\r\n color={primaryColor}\r\n distance={3}\r\n />\r\n\r\n <VRMModel\r\n url={modelUrl}\r\n emotion={emotion}\r\n callState={callState}\r\n audioLevel={audioLevel}\r\n />\r\n </Canvas>\r\n </div>\r\n </div>\r\n )\r\n}\r\n"],"names":["EMOTION_MAP","default","expressions","VRMExpressionPresetName","Neutral","happy","Happy","angry","Angry","headRotation","sorry","Sad","confused","love","Relaxed","thinking","writing","wink","Blink","VRMModel","url","emotion","callState","audioLevel","vrmRef","useRef","glbSceneRef","mixerRef","baseRotationY","baseScale","blinkTimer","nextBlinkAt","breathPhase","currentExpressions","targetExpressions","headTarget","THREE","Euler","baseY","scene","camera","useThree","useEffect","loader","GLTFLoader","register","parser","VRMLoaderPlugin","load","gltf","vrm","userData","current","rotation","y","Math","PI","box","Box3","setFromObject","center","getCenter","Vector3","height","getSize","position","add","console","info","model","updateMatrixWorld","size","pivot","Group","sub","fov","headY","cameraZ","tan","set","lookAt","animations","length","mixer","AnimationMixer","forEach","clip","clipAction","play","error","remove","stopAllAction","config","useFrame","_","delta","dt","min","glb","update","breathOffset","sin","idleSway","pulse","targetScale","currentScale","scale","x","setScalar","MathUtils","lerp","abs","blinkProgress","expressionManager","setValue","random","lerpSpeed","allKeys","Set","Object","keys","key","target","next","mouthTarget","currentMouth","getValue","Aa","smoothMouth","humanoid","head","getNormalizedBoneNode","idleSwayX","idleSwayZ","cos","targetX","targetY","targetZ","z","CameraSetup","Avatar3D","modelUrl","primaryColor","glowIntensity","useMemo","jsx","className","children","style","width","boxShadow","floor","toString","padStart","transition","jsxs","Canvas","background","gl","alpha","antialias","preserveDrawingBuffer","near","far","dpr","intensity","color","distance"],"mappings":"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"}
|