@gui-chat-plugin/avatar 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # @gui-chat-plugin/avatar
2
+
3
+ [![npm version](https://badge.fury.io/js/@gui-chat-plugin%2Favatar.svg)](https://www.npmjs.com/package/@gui-chat-plugin/avatar)
4
+
5
+ A 3D avatar plugin for [MulmoChat](https://github.com/receptron/MulmoChat) - displays a VRM avatar that lip-syncs during voice conversations.
6
+
7
+ ## What this plugin does
8
+
9
+ - Displays a 3D VRM avatar in the chat interface
10
+ - Automatic lip-sync when the AI is speaking
11
+ - Body animations (nodding, gestures) during conversation
12
+ - Emotion expressions (happy, sad, angry, surprised)
13
+ - Action gestures (nod, shake, wave, think, bow)
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ yarn add @gui-chat-plugin/avatar
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Vue Implementation (for MulmoChat)
24
+
25
+ ```typescript
26
+ // In src/tools/index.ts
27
+ import Plugin from "@gui-chat-plugin/avatar/vue";
28
+
29
+ const pluginList = [
30
+ // ... other plugins
31
+ Plugin,
32
+ ];
33
+
34
+ // In src/main.ts
35
+ import "@gui-chat-plugin/avatar/style.css";
36
+ ```
37
+
38
+ ### React Implementation
39
+
40
+ ```typescript
41
+ import Plugin from "@gui-chat-plugin/avatar/react";
42
+ import "@gui-chat-plugin/avatar/style.css";
43
+
44
+ // Named exports
45
+ import { plugin, View, Preview } from "@gui-chat-plugin/avatar/react";
46
+ ```
47
+
48
+ ### Core Only (Framework-agnostic)
49
+
50
+ ```typescript
51
+ import { pluginCore, TOOL_NAME } from "@gui-chat-plugin/avatar";
52
+ // or
53
+ import pluginCore from "@gui-chat-plugin/avatar";
54
+ ```
55
+
56
+ ## Package Exports
57
+
58
+ | Export | Description |
59
+ |--------|-------------|
60
+ | `@gui-chat-plugin/avatar` | Core (framework-agnostic) |
61
+ | `@gui-chat-plugin/avatar/vue` | Vue implementation with UI components |
62
+ | `@gui-chat-plugin/avatar/react` | React implementation with UI components |
63
+ | `@gui-chat-plugin/avatar/style.css` | Tailwind CSS styles |
64
+
65
+ ## Development
66
+
67
+ ```bash
68
+ # Install dependencies
69
+ yarn install
70
+
71
+ # Start dev server - Vue (http://localhost:5173/)
72
+ yarn dev
73
+
74
+ # Start dev server - React (http://localhost:5173/)
75
+ yarn dev:react
76
+
77
+ # Build
78
+ yarn build
79
+
80
+ # Type check
81
+ yarn typecheck
82
+
83
+ # Lint
84
+ yarn lint
85
+ ```
86
+
87
+ ## Test Prompts
88
+
89
+ Try these prompts to test the plugin:
90
+
91
+ 1. "Display an avatar"
92
+ 2. "Nod your head"
93
+ 3. "Show a happy expression"
94
+ 4. "Wave at me"
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Avatar Tool Definition (Schema)
3
+ *
4
+ * Defines the tool interface for LLM function calling.
5
+ * The avatar displays a 3D VRM model that lip-syncs with AI speech.
6
+ */
7
+ import type { ToolDefinition } from "gui-chat-protocol";
8
+ export declare const TOOL_NAME = "receptron_avatar";
9
+ export declare const TOOL_DEFINITION: ToolDefinition;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Avatar Plugin Core Exports
3
+ *
4
+ * Framework-agnostic types and plugin logic.
5
+ * Import from "@gui-chat-plugin/avatar/core"
6
+ */
7
+ export type { AvatarEmotion, AvatarData, AvatarArgs } from "./types";
8
+ export { pluginCore, executeAvatar } from "./plugin";
9
+ export { TOOL_NAME, TOOL_DEFINITION } from "./definition";
10
+ export { SAMPLES } from "./samples";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Avatar Plugin Core (Framework-agnostic)
3
+ *
4
+ * Contains the plugin logic without UI components.
5
+ * Can be used by any framework (Vue, React, etc.)
6
+ */
7
+ import type { ToolPluginCore, ToolContext, ToolResult } from "gui-chat-protocol";
8
+ import type { AvatarData, AvatarArgs } from "./types";
9
+ export declare const executeAvatar: (_context: ToolContext, args: AvatarArgs) => Promise<ToolResult<AvatarData, never>>;
10
+ export declare const pluginCore: ToolPluginCore<AvatarData, never, AvatarArgs>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Avatar Sample Data
3
+ */
4
+ import type { ToolSample } from "gui-chat-protocol";
5
+ export declare const SAMPLES: ToolSample[];
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Avatar Plugin Types
3
+ *
4
+ * Avatar-specific type definitions only.
5
+ * Common types should be imported directly from gui-chat-protocol.
6
+ */
7
+ /** Emotion types for avatar expression */
8
+ export type AvatarEmotion = "neutral" | "happy" | "sad" | "angry" | "surprised";
9
+ /** Action types for avatar gestures */
10
+ export type AvatarAction = "none" | "nod" | "shake" | "wave" | "think" | "bow";
11
+ /** Avatar data stored in result.data (for View, not visible to LLM) */
12
+ export interface AvatarData {
13
+ avatarUrl?: string;
14
+ emotion: AvatarEmotion;
15
+ action: AvatarAction;
16
+ /** Timestamp to trigger action even if same action is repeated */
17
+ actionTimestamp?: number;
18
+ }
19
+ /** Arguments passed to the avatar tool from LLM */
20
+ export interface AvatarArgs {
21
+ avatarUrl?: string;
22
+ emotion?: AvatarEmotion;
23
+ action?: AvatarAction;
24
+ }
package/dist/core.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n="receptron_avatar",o={type:"function",name:n,description:"Display and control a 3D avatar that speaks and reacts during conversation. The avatar automatically lip-syncs when you speak. Call this tool to change the avatar's emotion or trigger actions/gestures. Example: When the user says something funny, call with emotion='happy'. When agreeing, call with action='nod'. When greeting, call with action='wave'.",parameters:{type:"object",properties:{avatarUrl:{type:"string",description:"URL to a VRM avatar file. If not provided, a default avatar will be used. Only needed on first call."},emotion:{type:"string",enum:["neutral","happy","sad","angry","surprised"],description:"The emotion to display. Use 'happy' when pleased or joking, 'sad' when sympathizing, 'angry' when frustrated, 'surprised' when amazed. Defaults to neutral."},action:{type:"string",enum:["none","nod","shake","wave","think","bow"],description:"Trigger a gesture. 'nod' for agreement, 'shake' for disagreement, 'wave' for greeting/goodbye, 'think' when pondering, 'bow' for formal greeting."}},required:[]}},r=[{name:"Default Avatar",args:{}},{name:"Happy Avatar",args:{emotion:"happy"}},{name:"Sad Avatar",args:{emotion:"sad"}}],c="https://pixiv.github.io/three-vrm/packages/three-vrm/examples/models/VRM1_Constraint_Twist_Sample.vrm",i=async(d,s)=>{const{avatarUrl:l,emotion:t="neutral",action:e="none"}=s,g={avatarUrl:l||c,emotion:t,action:e,actionTimestamp:e!=="none"?Date.now():void 0},a=[];t!=="neutral"&&a.push(`emotion: ${t}`),e!=="none"&&a.push(`action: ${e}`);const p=a.length>0?`Avatar updated (${a.join(", ")})`:"Avatar displayed";return{toolName:n,message:p,data:g,updating:!0,instructions:"The avatar is reacting. Continue the conversation naturally. You can call this tool again anytime to change emotion or trigger gestures."}},u={toolDefinition:o,execute:i,generatingMessage:"Loading avatar...",isEnabled:()=>!0,samples:r};exports.SAMPLES=r;exports.TOOL_DEFINITION=o;exports.TOOL_NAME=n;exports.executeAvatar=i;exports.pluginCore=u;
package/dist/core.js ADDED
@@ -0,0 +1,72 @@
1
+ const n = "receptron_avatar", l = {
2
+ type: "function",
3
+ name: n,
4
+ description: "Display and control a 3D avatar that speaks and reacts during conversation. The avatar automatically lip-syncs when you speak. Call this tool to change the avatar's emotion or trigger actions/gestures. Example: When the user says something funny, call with emotion='happy'. When agreeing, call with action='nod'. When greeting, call with action='wave'.",
5
+ parameters: {
6
+ type: "object",
7
+ properties: {
8
+ avatarUrl: {
9
+ type: "string",
10
+ description: "URL to a VRM avatar file. If not provided, a default avatar will be used. Only needed on first call."
11
+ },
12
+ emotion: {
13
+ type: "string",
14
+ enum: ["neutral", "happy", "sad", "angry", "surprised"],
15
+ description: "The emotion to display. Use 'happy' when pleased or joking, 'sad' when sympathizing, 'angry' when frustrated, 'surprised' when amazed. Defaults to neutral."
16
+ },
17
+ action: {
18
+ type: "string",
19
+ enum: ["none", "nod", "shake", "wave", "think", "bow"],
20
+ description: "Trigger a gesture. 'nod' for agreement, 'shake' for disagreement, 'wave' for greeting/goodbye, 'think' when pondering, 'bow' for formal greeting."
21
+ }
22
+ },
23
+ required: []
24
+ }
25
+ }, p = [
26
+ {
27
+ name: "Default Avatar",
28
+ args: {}
29
+ },
30
+ {
31
+ name: "Happy Avatar",
32
+ args: {
33
+ emotion: "happy"
34
+ }
35
+ },
36
+ {
37
+ name: "Sad Avatar",
38
+ args: {
39
+ emotion: "sad"
40
+ }
41
+ }
42
+ ], g = "https://pixiv.github.io/three-vrm/packages/three-vrm/examples/models/VRM1_Constraint_Twist_Sample.vrm", c = async (h, o) => {
43
+ const { avatarUrl: r, emotion: t = "neutral", action: a = "none" } = o, i = {
44
+ avatarUrl: r || g,
45
+ emotion: t,
46
+ action: a,
47
+ actionTimestamp: a !== "none" ? Date.now() : void 0
48
+ }, e = [];
49
+ t !== "neutral" && e.push(`emotion: ${t}`), a !== "none" && e.push(`action: ${a}`);
50
+ const s = e.length > 0 ? `Avatar updated (${e.join(", ")})` : "Avatar displayed";
51
+ return {
52
+ toolName: n,
53
+ message: s,
54
+ data: i,
55
+ updating: !0,
56
+ // Update existing avatar instead of creating new
57
+ instructions: "The avatar is reacting. Continue the conversation naturally. You can call this tool again anytime to change emotion or trigger gestures."
58
+ };
59
+ }, d = {
60
+ toolDefinition: l,
61
+ execute: c,
62
+ generatingMessage: "Loading avatar...",
63
+ isEnabled: () => !0,
64
+ samples: p
65
+ };
66
+ export {
67
+ p as SAMPLES,
68
+ l as TOOL_DEFINITION,
69
+ n as TOOL_NAME,
70
+ c as executeAvatar,
71
+ d as pluginCore
72
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./core.cjs");exports.SAMPLES=e.SAMPLES;exports.TOOL_DEFINITION=e.TOOL_DEFINITION;exports.TOOL_NAME=e.TOOL_NAME;exports.default=e.pluginCore;exports.executeAvatar=e.executeAvatar;exports.pluginCore=e.pluginCore;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * MulmoChat Quiz Plugin
3
+ *
4
+ * Default export is the framework-agnostic core.
5
+ * For Vue implementation, import from "@mulmochat-plugin/quiz/vue"
6
+ *
7
+ * @example Default (Core - framework-agnostic)
8
+ * ```typescript
9
+ * import { pluginCore, TOOL_NAME, QuizData } from "@mulmochat-plugin/quiz";
10
+ * ```
11
+ *
12
+ * @example Vue implementation
13
+ * ```typescript
14
+ * import QuizPlugin from "@mulmochat-plugin/quiz/vue";
15
+ * import "@mulmochat-plugin/quiz/style.css";
16
+ * ```
17
+ */
18
+ export * from "./core";
19
+ export { pluginCore as default } from "./core";
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { SAMPLES as O, TOOL_DEFINITION as a, TOOL_NAME as o, pluginCore as t, executeAvatar as u, pluginCore as l } from "./core.js";
2
+ export {
3
+ O as SAMPLES,
4
+ a as TOOL_DEFINITION,
5
+ o as TOOL_NAME,
6
+ t as default,
7
+ u as executeAvatar,
8
+ l as pluginCore
9
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Avatar Preview Component (React)
3
+ */
4
+ import React from "react";
5
+ import type { PreviewComponentProps } from "gui-chat-protocol";
6
+ import type { AvatarData } from "../core/types";
7
+ type PreviewProps = PreviewComponentProps<AvatarData, never>;
8
+ export declare function Preview({ result }: PreviewProps): React.JSX.Element;
9
+ export default Preview;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Avatar View Component (React)
3
+ */
4
+ import React from "react";
5
+ import type { ViewComponentProps } from "gui-chat-protocol";
6
+ import type { AvatarData } from "../core/types";
7
+ type ViewProps = ViewComponentProps<AvatarData, never>;
8
+ export declare function View({ selectedResult, isAudioPlaying }: ViewProps): React.JSX.Element;
9
+ export default View;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Avatar Plugin - React Implementation
3
+ *
4
+ * Full React plugin with UI components.
5
+ * Import from "@gui-chat-plugin/avatar/react"
6
+ */
7
+ import "../style.css";
8
+ import type { ToolPluginReact } from "gui-chat-protocol/react";
9
+ import type { AvatarData, AvatarArgs } from "../core/types";
10
+ import { View } from "./View";
11
+ import { Preview } from "./Preview";
12
+ /**
13
+ * Avatar plugin instance with React components
14
+ */
15
+ export declare const plugin: ToolPluginReact<AvatarData, never, AvatarArgs>;
16
+ export type { AvatarEmotion, AvatarData, AvatarArgs } from "../core/types";
17
+ export { pluginCore, executeAvatar } from "../core/plugin";
18
+ export { TOOL_NAME, TOOL_DEFINITION } from "../core/definition";
19
+ export { SAMPLES } from "../core/samples";
20
+ export { View, Preview };
21
+ declare const _default: {
22
+ plugin: ToolPluginReact<AvatarData, never, AvatarArgs, import("gui-chat-protocol/react").InputHandler, Record<string, unknown>>;
23
+ };
24
+ export default _default;
package/dist/react.cjs ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const v=require("./three-vrm.module-FTOSkzpf.cjs"),B=require("./core.cjs"),a=require("react");var X={exports:{}},Y={};var K;function ie(){if(K)return Y;K=1;var d=Symbol.for("react.transitional.element"),b=Symbol.for("react.fragment");function l(z,u,g){var M=null;if(g!==void 0&&(M=""+g),u.key!==void 0&&(M=""+u.key),"key"in u){g={};for(var E in u)E!=="key"&&(g[E]=u[E])}else g=u;return u=g.ref,{$$typeof:d,type:z,key:M,ref:u!==void 0?u:null,props:g}}return Y.Fragment=b,Y.jsx=l,Y.jsxs=l,Y}var $={};var ee;function se(){return ee||(ee=1,process.env.NODE_ENV!=="production"&&(function(){function d(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===Q?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case j:return"Fragment";case q:return"Profiler";case U:return"StrictMode";case h:return"Suspense";case L:return"SuspenseList";case n:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case W:return"Portal";case c:return e.displayName||"Context";case G:return(e._context.displayName||"Context")+".Consumer";case m:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case t:return r=e.displayName||null,r!==null?r:d(e.type)||"Memo";case y:r=e._payload,e=e._init;try{return d(e(r))}catch{}}return null}function b(e){return""+e}function l(e){try{b(e);var r=!1}catch{r=!0}if(r){r=console;var o=r.error,i=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return o.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",i),b(e)}}function z(e){if(e===j)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===y)return"<...>";try{var r=d(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function u(){var e=O.A;return e===null?null:e.getOwner()}function g(){return Error("react-stack-top-frame")}function M(e){if(R.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function E(e,r){function o(){_||(_=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",r))}o.isReactWarning=!0,Object.defineProperty(e,"key",{get:o,configurable:!0})}function x(){var e=d(this.type);return w[e]||(w[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function f(e,r,o,i,J,H){var s=o.ref;return e={$$typeof:I,type:e,key:r,props:o,_owner:i},(s!==void 0?s:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:x}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:J}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:H}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function k(e,r,o,i,J,H){var s=r.children;if(s!==void 0)if(i)if(P(s)){for(i=0;i<s.length;i++)D(s[i]);Object.freeze&&Object.freeze(s)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else D(s);if(R.call(r,"key")){s=d(e);var F=Object.keys(r).filter(function(ae){return ae!=="key"});i=0<F.length?"{key: someKey, "+F.join(": ..., ")+": ...}":"{key: someKey}",V[s+i]||(F=0<F.length?"{"+F.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
2
+ let props = %s;
3
+ <%s {...props} />
4
+ React keys must be passed directly to JSX without using spread:
5
+ let props = %s;
6
+ <%s key={someKey} {...props} />`,i,s,F,s),V[s+i]=!0)}if(s=null,o!==void 0&&(l(o),s=""+o),M(r)&&(l(r.key),s=""+r.key),"key"in r){o={};for(var Z in r)Z!=="key"&&(o[Z]=r[Z])}else o=r;return s&&E(o,typeof e=="function"?e.displayName||e.name||"Unknown":e),f(e,s,o,u(),J,H)}function D(e){C(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===y&&(e._payload.status==="fulfilled"?C(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function C(e){return typeof e=="object"&&e!==null&&e.$$typeof===I}var S=a,I=Symbol.for("react.transitional.element"),W=Symbol.for("react.portal"),j=Symbol.for("react.fragment"),U=Symbol.for("react.strict_mode"),q=Symbol.for("react.profiler"),G=Symbol.for("react.consumer"),c=Symbol.for("react.context"),m=Symbol.for("react.forward_ref"),h=Symbol.for("react.suspense"),L=Symbol.for("react.suspense_list"),t=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),n=Symbol.for("react.activity"),Q=Symbol.for("react.client.reference"),O=S.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,R=Object.prototype.hasOwnProperty,P=Array.isArray,N=console.createTask?console.createTask:function(){return null};S={react_stack_bottom_frame:function(e){return e()}};var _,w={},A=S.react_stack_bottom_frame.bind(S,g)(),T=N(z(g)),V={};$.Fragment=j,$.jsx=function(e,r,o){var i=1e4>O.recentlyCreatedOwnerStacks++;return k(e,r,o,!1,i?Error("react-stack-top-frame"):A,i?N(z(e)):T)},$.jsxs=function(e,r,o){var i=1e4>O.recentlyCreatedOwnerStacks++;return k(e,r,o,!0,i?Error("react-stack-top-frame"):A,i?N(z(e)):T)}})()),$}var re;function le(){return re||(re=1,process.env.NODE_ENV==="production"?X.exports=ie():X.exports=se()),X.exports}var p=le();function te({selectedResult:d,isAudioPlaying:b}){const l=a.useRef(null),[z,u]=a.useState(!1),[g,M]=a.useState(null),E=a.useRef(null),x=a.useRef(null),f=a.useRef(null),k=a.useRef(null),D=a.useRef(null),C=a.useRef(null),S=a.useRef(0),I=a.useRef(b);a.useEffect(()=>{I.current=b},[b]);const W=a.useCallback(()=>{if(!l.current)return;const c=l.current,m=c.clientWidth,h=c.clientHeight;E.current=new v.Scene,x.current=new v.PerspectiveCamera(30,m/h,.1,100),x.current.position.set(0,1.3,1.5),x.current.lookAt(0,1.2,0),f.current=new v.WebGLRenderer({antialias:!0,alpha:!0}),f.current.setSize(m,h),f.current.setPixelRatio(window.devicePixelRatio),f.current.outputColorSpace=v.SRGBColorSpace,c.appendChild(f.current.domElement);const L=new v.AmbientLight(16777215,.6);E.current.add(L);const t=new v.DirectionalLight(16777215,.8);t.position.set(1,1,1),E.current.add(t),D.current=new v.Clock},[]),j=a.useCallback(()=>{if(!l.current||!x.current||!f.current)return;const c=l.current.clientWidth,m=l.current.clientHeight;x.current.aspect=c/m,x.current.updateProjectionMatrix(),f.current.setSize(c,m)},[]),U=a.useCallback(()=>{C.current=requestAnimationFrame(U);const c=D.current,m=f.current,h=E.current,L=x.current,t=k.current;if(!c||!m||!h||!L)return;const y=c.getDelta(),n=c.getElapsedTime();if(t){if(t.update(y),I.current){S.current+=y*10;const O=(Math.sin(S.current)+1)*.3;t.expressionManager?.setValue(v.VRMExpressionPresetName.Aa,O);const R=t.humanoid?.getNormalizedBoneNode("head"),P=t.humanoid?.getNormalizedBoneNode("neck"),N=t.humanoid?.getNormalizedBoneNode("spine");S.current<.2&&console.log("[Avatar Debug] Body animation - Bones found:",{head:!!R,neck:!!P,spine:!!N,humanoid:!!t.humanoid}),R&&(R.rotation.x=Math.sin(n*2)*.08,R.rotation.z=Math.sin(n*1.5)*.05),P&&(P.rotation.y=Math.sin(n*.8)*.05),N&&(N.rotation.z=Math.sin(n*.5)*.03);const _=t.humanoid?.getNormalizedBoneNode("leftShoulder"),w=t.humanoid?.getNormalizedBoneNode("rightShoulder"),A=t.humanoid?.getNormalizedBoneNode("leftUpperArm"),T=t.humanoid?.getNormalizedBoneNode("rightUpperArm"),V=t.humanoid?.getNormalizedBoneNode("leftLowerArm"),e=t.humanoid?.getNormalizedBoneNode("rightLowerArm");_&&(_.rotation.z=.1),w&&(w.rotation.z=-.1),A&&(A.rotation.z=-.8+Math.sin(n*1.2)*.1,A.rotation.x=.4+Math.sin(n*.9)*.15,A.rotation.y=Math.sin(n*.7)*.1),T&&(T.rotation.z=.8+Math.sin(n*1.3)*.1,T.rotation.x=.4+Math.sin(n*1)*.15,T.rotation.y=Math.sin(n*.8)*.1),V&&(V.rotation.y=-1-Math.sin(n*1.5)*.2,V.rotation.z=Math.sin(n*1.1)*.1),e&&(e.rotation.y=1+Math.sin(n*1.4)*.2,e.rotation.z=Math.sin(n*1.2)*.1)}else{S.current=0,t.expressionManager?.setValue(v.VRMExpressionPresetName.Aa,0);const O=t.humanoid?.getNormalizedBoneNode("spine"),R=t.humanoid?.getNormalizedBoneNode("head");Math.floor(n)%5===0&&n-Math.floor(n)<y&&console.log("[Avatar Debug] Idle animation - Bones found:",{head:!!R,neck:!!t.humanoid?.getNormalizedBoneNode("neck"),spine:!!O}),O&&(O.rotation.x=Math.sin(n*.8)*.02),R&&(R.rotation.x=Math.sin(n*.3)*.03,R.rotation.z=Math.sin(n*.2)*.02);const P=t.humanoid?.getNormalizedBoneNode("leftShoulder"),N=t.humanoid?.getNormalizedBoneNode("rightShoulder"),_=t.humanoid?.getNormalizedBoneNode("leftUpperArm"),w=t.humanoid?.getNormalizedBoneNode("rightUpperArm"),A=t.humanoid?.getNormalizedBoneNode("leftLowerArm"),T=t.humanoid?.getNormalizedBoneNode("rightLowerArm");P&&(P.rotation.z=.1),N&&(N.rotation.z=-.1),_&&(_.rotation.z=-.9,_.rotation.x=.2,_.rotation.y=0),w&&(w.rotation.z=.9,w.rotation.x=.2,w.rotation.y=0),A&&(A.rotation.y=-.8),T&&(T.rotation.y=.8)}n%4<.1?t.expressionManager?.setValue(v.VRMExpressionPresetName.Blink,1):t.expressionManager?.setValue(v.VRMExpressionPresetName.Blink,0)}m.render(h,L)},[]),q=a.useCallback(async c=>{const m=E.current;if(m){u(!0),M(null),k.current&&(m.remove(k.current.scene),k.current=null);try{const h=new v.GLTFLoader;h.register(y=>new v.VRMLoaderPlugin(y));const t=(await h.loadAsync(c)).userData.vrm;if(!t)throw new Error("Failed to load VRM data");m.add(t.scene),k.current=t,u(!1)}catch(h){console.error("Failed to load VRM:",h),M(`Failed to load avatar: ${h instanceof Error?h.message:"Unknown error"}`),u(!1)}}},[]),G=a.useCallback(()=>{C.current!==null&&(cancelAnimationFrame(C.current),C.current=null),window.removeEventListener("resize",j),f.current&&l.current&&(l.current.removeChild(f.current.domElement),f.current.dispose(),f.current=null),k.current=null,E.current=null,x.current=null,D.current=null},[j]);return a.useEffect(()=>(W(),window.addEventListener("resize",j),U(),()=>{G()}),[W,j,U,G]),a.useEffect(()=>{if(d?.toolName===B.TOOL_NAME&&d.data){const c=d.data;c.avatarUrl&&q(c.avatarUrl)}},[d,q]),p.jsxs("div",{className:"w-full h-full bg-gradient-to-b from-slate-800 to-slate-900 relative",children:[p.jsx("div",{ref:l,className:"w-full h-full"}),z&&p.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-slate-900/80",children:p.jsx("div",{className:"text-white text-lg",children:"Loading Avatar..."})}),g&&p.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-slate-900/80",children:p.jsx("div",{className:"text-red-400 text-lg text-center p-4",children:g})}),b&&p.jsx("div",{className:"absolute top-4 right-4 px-3 py-1 bg-green-600 text-white text-sm rounded-full",children:"Speaking..."})]})}function ne({result:d}){const l=d.data?.emotion||"neutral";return p.jsx("div",{className:"p-3 bg-gradient-to-br from-slate-700 to-slate-800 rounded-md",children:p.jsxs("div",{className:"flex flex-col items-center gap-2",children:[p.jsx("div",{className:"w-12 h-12 bg-slate-600 rounded-full flex items-center justify-center",children:p.jsx("svg",{className:"w-8 h-8 text-slate-300",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:p.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"})})}),p.jsx("div",{className:"text-sm font-semibold text-white text-center",children:"3D Avatar"}),l!=="neutral"&&p.jsx("div",{className:"text-xs px-2 py-0.5 bg-blue-500 text-white rounded-full",children:l})]})})}const oe={...B.pluginCore,ViewComponent:te,PreviewComponent:ne},ce={plugin:oe};exports.SAMPLES=B.SAMPLES;exports.TOOL_DEFINITION=B.TOOL_DEFINITION;exports.TOOL_NAME=B.TOOL_NAME;exports.executeAvatar=B.executeAvatar;exports.pluginCore=B.pluginCore;exports.Preview=ne;exports.View=te;exports.default=ce;exports.plugin=oe;