@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.
@@ -0,0 +1,7 @@
1
+ import type { ToolResult } from "gui-chat-protocol";
2
+ type __VLS_Props = {
3
+ result: ToolResult;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const _default: typeof __VLS_export;
7
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import type { ToolResult } from "gui-chat-protocol";
2
+ type __VLS_Props = {
3
+ selectedResult: ToolResult;
4
+ sendTextMessage: (text?: string) => void;
5
+ isAudioPlaying?: boolean;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Avatar Plugin - Vue Implementation
3
+ *
4
+ * Full Vue plugin with UI components.
5
+ * Import from "@gui-chat-plugin/avatar/vue"
6
+ */
7
+ import "../style.css";
8
+ import type { ToolPlugin } from "gui-chat-protocol/vue";
9
+ import type { AvatarData, AvatarArgs } from "../core/types";
10
+ import View from "./View.vue";
11
+ import Preview from "./Preview.vue";
12
+ /**
13
+ * Avatar plugin instance with Vue components
14
+ */
15
+ export declare const plugin: ToolPlugin<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: ToolPlugin<AvatarData, never, AvatarArgs, import("gui-chat-protocol/vue").InputHandler, Record<string, unknown>>;
23
+ };
24
+ export default _default;
package/dist/vue.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const a=require("./three-vrm.module-FTOSkzpf.cjs"),z=require("./core.cjs"),o=require("vue"),q={class:"size-full bg-gradient-to-b from-slate-800 to-slate-900 relative"},H={key:0,class:"absolute inset-0 flex items-center justify-center bg-slate-900/80"},G={key:1,class:"absolute inset-0 flex items-center justify-center bg-slate-900/80"},W={class:"text-red-400 text-lg text-center p-4"},J={key:2,class:"absolute top-4 right-4 px-3 py-1 bg-green-600 text-white text-sm rounded-full"},T=o.defineComponent({__name:"View",props:{selectedResult:{},sendTextMessage:{type:Function},isAudioPlaying:{type:Boolean}},setup(V){const v=V,p=o.ref(null),M=o.ref(!1),E=o.ref(null);let r=null,N=null,l=null,t=null,y=null,w=null,k=0,b="neutral",P="none",S=0,_=0,L="";const F={neutral:null,happy:a.VRMExpressionPresetName.Happy,sad:a.VRMExpressionPresetName.Sad,angry:a.VRMExpressionPresetName.Angry,surprised:a.VRMExpressionPresetName.Surprised};function j(){if(!p.value)return;const n=p.value,e=n.clientWidth,B=n.clientHeight;r=new a.Scene,N=new a.PerspectiveCamera(30,e/B,.1,100),N.position.set(0,1.3,1.5),N.lookAt(0,1.2,0),l=new a.WebGLRenderer({antialias:!0,alpha:!0}),l.setSize(e,B),l.setPixelRatio(window.devicePixelRatio),l.outputColorSpace=a.SRGBColorSpace,n.appendChild(l.domElement);const h=new a.AmbientLight(16777215,.6);r.add(h);const d=new a.DirectionalLight(16777215,.8);d.position.set(1,1,1),r.add(d),y=new a.Clock,window.addEventListener("resize",D),C()}function D(){if(!p.value||!N||!l)return;const n=p.value.clientWidth,e=p.value.clientHeight;N.aspect=n/e,N.updateProjectionMatrix(),l.setSize(n,e)}async function U(n){if(r){M.value=!0,E.value=null,t&&(r.remove(t.scene),t=null);try{const e=new a.GLTFLoader;e.register(d=>new a.VRMLoaderPlugin(d));const h=(await e.loadAsync(n)).userData.vrm;if(!h)throw new Error("Failed to load VRM data");r.add(h.scene),t=h,console.log("[Avatar Debug] VRM loaded successfully"),console.log("[Avatar Debug] VRM humanoid:",!!h.humanoid),console.log("[Avatar Debug] VRM expressionManager:",!!h.expressionManager),M.value=!1}catch(e){console.error("Failed to load VRM:",e),E.value=`Failed to load avatar: ${e instanceof Error?e.message:"Unknown error"}`,M.value=!1}}}let R=0;function C(){if(w=requestAnimationFrame(C),!y||!l||!r||!N)return;const n=y.getDelta(),e=y.getElapsedTime();if(R++,R%300===0&&console.log("[Avatar Debug] animate() running, frame:",R,"currentVrm:",!!t,"isAudioPlaying:",v.isAudioPlaying),t){if(t.update(n),v.isAudioPlaying){k+=n*10;const d=(Math.sin(k)+1)*.3;t.expressionManager?.setValue(a.VRMExpressionPresetName.Aa,d);const m=t.humanoid?.getNormalizedBoneNode("head"),c=t.humanoid?.getNormalizedBoneNode("neck"),i=t.humanoid?.getNormalizedBoneNode("spine");k<.2&&console.log("[Avatar Debug] Body animation - Bones found:",{head:!!m,neck:!!c,spine:!!i,humanoid:!!t.humanoid}),m&&(m.rotation.x=Math.sin(e*2)*.08,m.rotation.z=Math.sin(e*1.5)*.05),c&&(c.rotation.y=Math.sin(e*.8)*.05),i&&(i.rotation.z=Math.sin(e*.5)*.03);const s=t.humanoid?.getNormalizedBoneNode("leftShoulder"),g=t.humanoid?.getNormalizedBoneNode("rightShoulder"),f=t.humanoid?.getNormalizedBoneNode("leftUpperArm"),x=t.humanoid?.getNormalizedBoneNode("rightUpperArm"),u=t.humanoid?.getNormalizedBoneNode("leftLowerArm"),A=t.humanoid?.getNormalizedBoneNode("rightLowerArm");s&&(s.rotation.z=.1),g&&(g.rotation.z=-.1),f&&(f.rotation.z=-.8+Math.sin(e*1.2)*.1,f.rotation.x=.4+Math.sin(e*.9)*.15,f.rotation.y=Math.sin(e*.7)*.1),x&&(x.rotation.z=.8+Math.sin(e*1.3)*.1,x.rotation.x=.4+Math.sin(e*1)*.15,x.rotation.y=Math.sin(e*.8)*.1),u&&(u.rotation.y=-1-Math.sin(e*1.5)*.2,u.rotation.z=Math.sin(e*1.1)*.1),A&&(A.rotation.y=1+Math.sin(e*1.4)*.2,A.rotation.z=Math.sin(e*1.2)*.1)}else{k=0,t.expressionManager?.setValue(a.VRMExpressionPresetName.Aa,0);const d=t.humanoid?.getNormalizedBoneNode("spine"),m=t.humanoid?.getNormalizedBoneNode("head");Math.floor(e)%5===0&&e-Math.floor(e)<n&&console.log("[Avatar Debug] Idle animation - Bones found:",{head:!!m,neck:!!t.humanoid?.getNormalizedBoneNode("neck"),spine:!!d}),d&&(d.rotation.x=Math.sin(e*.8)*.02),m&&(m.rotation.x=Math.sin(e*.3)*.03,m.rotation.z=Math.sin(e*.2)*.02);const c=t.humanoid?.getNormalizedBoneNode("leftShoulder"),i=t.humanoid?.getNormalizedBoneNode("rightShoulder"),s=t.humanoid?.getNormalizedBoneNode("leftUpperArm"),g=t.humanoid?.getNormalizedBoneNode("rightUpperArm"),f=t.humanoid?.getNormalizedBoneNode("leftLowerArm"),x=t.humanoid?.getNormalizedBoneNode("rightLowerArm");c&&(c.rotation.z=.1),i&&(i.rotation.z=-.1),s&&(s.rotation.z=-.9,s.rotation.x=.2,s.rotation.y=0),g&&(g.rotation.z=.9,g.rotation.x=.2,g.rotation.y=0),f&&(f.rotation.y=-.8),x&&(x.rotation.y=.8)}e%4<.1?t.expressionManager?.setValue(a.VRMExpressionPresetName.Blink,1):t.expressionManager?.setValue(a.VRMExpressionPresetName.Blink,0);const h=F[b];if(t.expressionManager?.setValue(a.VRMExpressionPresetName.Happy,0),t.expressionManager?.setValue(a.VRMExpressionPresetName.Sad,0),t.expressionManager?.setValue(a.VRMExpressionPresetName.Angry,0),t.expressionManager?.setValue(a.VRMExpressionPresetName.Surprised,0),h&&t.expressionManager?.setValue(h,.8),P!=="none"){const d=e-S,m=1.5;if(d<m){const c=d/m,i=t.humanoid?.getNormalizedBoneNode("head"),s=t.humanoid?.getNormalizedBoneNode("rightUpperArm"),g=t.humanoid?.getNormalizedBoneNode("rightLowerArm"),f=t.humanoid?.getNormalizedBoneNode("spine"),u=(A=>A<.5?2*A*A:1-Math.pow(-2*A+2,2)/2)(c<.5?c*2:(1-c)*2);switch(P){case"nod":i&&(i.rotation.x=Math.sin(c*Math.PI*3)*.2);break;case"shake":i&&(i.rotation.y=Math.sin(c*Math.PI*4)*.3);break;case"wave":s&&(s.rotation.z=1.5*u,s.rotation.x=-.3*u),g&&(g.rotation.y=.5+Math.sin(c*Math.PI*6)*.3);break;case"think":i&&(i.rotation.x=.1*u,i.rotation.z=.1*u),s&&(s.rotation.z=.3*u,s.rotation.x=.8*u),g&&(g.rotation.y=1.5*u);break;case"bow":f&&(f.rotation.x=.4*u),i&&(i.rotation.x=.2*u);break}}else P="none"}}l.render(r,N)}function $(){w!==null&&(cancelAnimationFrame(w),w=null),window.removeEventListener("resize",D),l&&p.value&&(p.value.removeChild(l.domElement),l.dispose(),l=null),t&&(t=null),r=null,N=null,y=null}return o.watch(()=>v.selectedResult,n=>{if(n?.toolName===z.TOOL_NAME&&n.data){const e=n.data;if(e.avatarUrl&&e.avatarUrl!==L&&(console.log("[Avatar Debug] VRM URL changed:",e.avatarUrl),L=e.avatarUrl,U(e.avatarUrl)),e.emotion&&e.emotion!==b&&(console.log("[Avatar Debug] Emotion changed to:",e.emotion),b=e.emotion),e.action&&e.action!=="none"){const B=e.actionTimestamp||0;B>_&&(console.log("[Avatar Debug] Action triggered:",e.action),P=e.action,S=y?.getElapsedTime()||0,_=B)}}},{deep:!0}),o.watch(()=>v.isAudioPlaying,n=>{console.log("[Avatar Debug] Plugin received isAudioPlaying:",n)}),o.onMounted(()=>{if(console.log("[Avatar Debug] View component MOUNTED"),j(),v.selectedResult?.toolName===z.TOOL_NAME&&v.selectedResult.data){const n=v.selectedResult.data;n.avatarUrl&&(console.log("[Avatar Debug] Initial VRM load:",n.avatarUrl),L=n.avatarUrl,U(n.avatarUrl)),n.emotion&&(b=n.emotion)}}),o.onUnmounted(()=>{$()}),(n,e)=>(o.openBlock(),o.createElementBlock("div",q,[o.createElementVNode("div",{ref_key:"containerRef",ref:p,class:"size-full"},null,512),M.value?(o.openBlock(),o.createElementBlock("div",H,[...e[0]||(e[0]=[o.createElementVNode("div",{class:"text-white text-lg"},"Loading Avatar...",-1)])])):o.createCommentVNode("",!0),E.value?(o.openBlock(),o.createElementBlock("div",G,[o.createElementVNode("div",W,o.toDisplayString(E.value),1)])):o.createCommentVNode("",!0),V.isAudioPlaying?(o.openBlock(),o.createElementBlock("div",J," Speaking... ")):o.createCommentVNode("",!0)]))}}),K={class:"p-3 bg-gradient-to-br from-slate-700 to-slate-800 rounded-md"},Q={class:"flex flex-col items-center gap-2"},X={key:0,class:"text-xs px-2 py-0.5 bg-blue-500 text-white rounded-full"},O=o.defineComponent({__name:"Preview",props:{result:{}},setup(V){const v=V,p=o.computed(()=>v.result.data),M=o.computed(()=>p.value?.emotion||"neutral");return(E,r)=>(o.openBlock(),o.createElementBlock("div",K,[o.createElementVNode("div",Q,[r[0]||(r[0]=o.createElementVNode("div",{class:"size-12 bg-slate-600 rounded-full flex items-center justify-center"},[o.createElementVNode("svg",{class:"size-8 text-slate-300",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[o.createElementVNode("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"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"})])],-1)),r[1]||(r[1]=o.createElementVNode("div",{class:"text-sm font-semibold text-white text-center"}," 3D Avatar ",-1)),M.value&&M.value!=="neutral"?(o.openBlock(),o.createElementBlock("div",X,o.toDisplayString(M.value),1)):o.createCommentVNode("",!0)])]))}}),I={...z.pluginCore,viewComponent:T,previewComponent:O},Y={plugin:I};exports.SAMPLES=z.SAMPLES;exports.TOOL_DEFINITION=z.TOOL_DEFINITION;exports.TOOL_NAME=z.TOOL_NAME;exports.executeAvatar=z.executeAvatar;exports.pluginCore=z.pluginCore;exports.Preview=O;exports.View=T;exports.default=Y;exports.plugin=I;
package/dist/vue.js ADDED
@@ -0,0 +1,207 @@
1
+ import { S as K, P as Q, W as X, a as Y, A as Z, D as ee, G as te, V as oe, b as m, C as ne } from "./three-vrm.module-DJ7DURJm.js";
2
+ import { TOOL_NAME as O, pluginCore as ae } from "./core.js";
3
+ import { SAMPLES as ye, TOOL_DEFINITION as Be, executeAvatar as be } from "./core.js";
4
+ import { defineComponent as G, ref as R, watch as j, onMounted as ie, onUnmounted as re, openBlock as y, createElementBlock as B, createElementVNode as N, createCommentVNode as U, toDisplayString as H, computed as $ } from "vue";
5
+ const se = { class: "size-full bg-gradient-to-b from-slate-800 to-slate-900 relative" }, le = {
6
+ key: 0,
7
+ class: "absolute inset-0 flex items-center justify-center bg-slate-900/80"
8
+ }, de = {
9
+ key: 1,
10
+ class: "absolute inset-0 flex items-center justify-center bg-slate-900/80"
11
+ }, ce = { class: "text-red-400 text-lg text-center p-4" }, ue = {
12
+ key: 2,
13
+ class: "absolute top-4 right-4 px-3 py-1 bg-green-600 text-white text-sm rounded-full"
14
+ }, me = /* @__PURE__ */ G({
15
+ __name: "View",
16
+ props: {
17
+ selectedResult: {},
18
+ sendTextMessage: { type: Function },
19
+ isAudioPlaying: { type: Boolean }
20
+ },
21
+ setup(k) {
22
+ const f = k, g = R(null), x = R(!1), b = R(null);
23
+ let a = null, v = null, r = null, t = null, w = null, L = null, V = 0, S = "neutral", D = "none", E = 0, C = 0, _ = "";
24
+ const W = {
25
+ neutral: null,
26
+ happy: m.Happy,
27
+ sad: m.Sad,
28
+ angry: m.Angry,
29
+ surprised: m.Surprised
30
+ };
31
+ function q() {
32
+ if (!g.value) return;
33
+ const o = g.value, e = o.clientWidth, z = o.clientHeight;
34
+ a = new K(), v = new Q(30, e / z, 0.1, 100), v.position.set(0, 1.3, 1.5), v.lookAt(0, 1.2, 0), r = new X({ antialias: !0, alpha: !0 }), r.setSize(e, z), r.setPixelRatio(window.devicePixelRatio), r.outputColorSpace = Y, o.appendChild(r.domElement);
35
+ const h = new Z(16777215, 0.6);
36
+ a.add(h);
37
+ const s = new ee(16777215, 0.8);
38
+ s.position.set(1, 1, 1), a.add(s), w = new ne(), window.addEventListener("resize", T), I();
39
+ }
40
+ function T() {
41
+ if (!g.value || !v || !r) return;
42
+ const o = g.value.clientWidth, e = g.value.clientHeight;
43
+ v.aspect = o / e, v.updateProjectionMatrix(), r.setSize(o, e);
44
+ }
45
+ async function F(o) {
46
+ if (a) {
47
+ x.value = !0, b.value = null, t && (a.remove(t.scene), t = null);
48
+ try {
49
+ const e = new te();
50
+ e.register((s) => new oe(s));
51
+ const h = (await e.loadAsync(o)).userData.vrm;
52
+ if (!h)
53
+ throw new Error("Failed to load VRM data");
54
+ a.add(h.scene), t = h, console.log("[Avatar Debug] VRM loaded successfully"), console.log("[Avatar Debug] VRM humanoid:", !!h.humanoid), console.log("[Avatar Debug] VRM expressionManager:", !!h.expressionManager), x.value = !1;
55
+ } catch (e) {
56
+ console.error("Failed to load VRM:", e), b.value = `Failed to load avatar: ${e instanceof Error ? e.message : "Unknown error"}`, x.value = !1;
57
+ }
58
+ }
59
+ }
60
+ let P = 0;
61
+ function I() {
62
+ if (L = requestAnimationFrame(I), !w || !r || !a || !v) return;
63
+ const o = w.getDelta(), e = w.getElapsedTime();
64
+ if (P++, P % 300 === 0 && console.log("[Avatar Debug] animate() running, frame:", P, "currentVrm:", !!t, "isAudioPlaying:", f.isAudioPlaying), t) {
65
+ if (t.update(o), f.isAudioPlaying) {
66
+ V += o * 10;
67
+ const s = (Math.sin(V) + 1) * 0.3;
68
+ t.expressionManager?.setValue(m.Aa, s);
69
+ const c = t.humanoid?.getNormalizedBoneNode("head"), l = t.humanoid?.getNormalizedBoneNode("neck"), n = t.humanoid?.getNormalizedBoneNode("spine");
70
+ V < 0.2 && console.log("[Avatar Debug] Body animation - Bones found:", {
71
+ head: !!c,
72
+ neck: !!l,
73
+ spine: !!n,
74
+ humanoid: !!t.humanoid
75
+ }), c && (c.rotation.x = Math.sin(e * 2) * 0.08, c.rotation.z = Math.sin(e * 1.5) * 0.05), l && (l.rotation.y = Math.sin(e * 0.8) * 0.05), n && (n.rotation.z = Math.sin(e * 0.5) * 0.03);
76
+ const i = t.humanoid?.getNormalizedBoneNode("leftShoulder"), u = t.humanoid?.getNormalizedBoneNode("rightShoulder"), p = t.humanoid?.getNormalizedBoneNode("leftUpperArm"), A = t.humanoid?.getNormalizedBoneNode("rightUpperArm"), d = t.humanoid?.getNormalizedBoneNode("leftLowerArm"), M = t.humanoid?.getNormalizedBoneNode("rightLowerArm");
77
+ i && (i.rotation.z = 0.1), u && (u.rotation.z = -0.1), p && (p.rotation.z = -0.8 + Math.sin(e * 1.2) * 0.1, p.rotation.x = 0.4 + Math.sin(e * 0.9) * 0.15, p.rotation.y = Math.sin(e * 0.7) * 0.1), A && (A.rotation.z = 0.8 + Math.sin(e * 1.3) * 0.1, A.rotation.x = 0.4 + Math.sin(e * 1) * 0.15, A.rotation.y = Math.sin(e * 0.8) * 0.1), d && (d.rotation.y = -1 - Math.sin(e * 1.5) * 0.2, d.rotation.z = Math.sin(e * 1.1) * 0.1), M && (M.rotation.y = 1 + Math.sin(e * 1.4) * 0.2, M.rotation.z = Math.sin(e * 1.2) * 0.1);
78
+ } else {
79
+ V = 0, t.expressionManager?.setValue(m.Aa, 0);
80
+ const s = t.humanoid?.getNormalizedBoneNode("spine"), c = t.humanoid?.getNormalizedBoneNode("head");
81
+ Math.floor(e) % 5 === 0 && e - Math.floor(e) < o && console.log("[Avatar Debug] Idle animation - Bones found:", {
82
+ head: !!c,
83
+ neck: !!t.humanoid?.getNormalizedBoneNode("neck"),
84
+ spine: !!s
85
+ }), s && (s.rotation.x = Math.sin(e * 0.8) * 0.02), c && (c.rotation.x = Math.sin(e * 0.3) * 0.03, c.rotation.z = Math.sin(e * 0.2) * 0.02);
86
+ const l = t.humanoid?.getNormalizedBoneNode("leftShoulder"), n = t.humanoid?.getNormalizedBoneNode("rightShoulder"), i = t.humanoid?.getNormalizedBoneNode("leftUpperArm"), u = t.humanoid?.getNormalizedBoneNode("rightUpperArm"), p = t.humanoid?.getNormalizedBoneNode("leftLowerArm"), A = t.humanoid?.getNormalizedBoneNode("rightLowerArm");
87
+ l && (l.rotation.z = 0.1), n && (n.rotation.z = -0.1), i && (i.rotation.z = -0.9, i.rotation.x = 0.2, i.rotation.y = 0), u && (u.rotation.z = 0.9, u.rotation.x = 0.2, u.rotation.y = 0), p && (p.rotation.y = -0.8), A && (A.rotation.y = 0.8);
88
+ }
89
+ e % 4 < 0.1 ? t.expressionManager?.setValue(m.Blink, 1) : t.expressionManager?.setValue(m.Blink, 0);
90
+ const h = W[S];
91
+ if (t.expressionManager?.setValue(m.Happy, 0), t.expressionManager?.setValue(m.Sad, 0), t.expressionManager?.setValue(m.Angry, 0), t.expressionManager?.setValue(m.Surprised, 0), h && t.expressionManager?.setValue(h, 0.8), D !== "none") {
92
+ const s = e - E, c = 1.5;
93
+ if (s < c) {
94
+ const l = s / c, n = t.humanoid?.getNormalizedBoneNode("head"), i = t.humanoid?.getNormalizedBoneNode("rightUpperArm"), u = t.humanoid?.getNormalizedBoneNode("rightLowerArm"), p = t.humanoid?.getNormalizedBoneNode("spine"), d = ((M) => M < 0.5 ? 2 * M * M : 1 - Math.pow(-2 * M + 2, 2) / 2)(l < 0.5 ? l * 2 : (1 - l) * 2);
95
+ switch (D) {
96
+ case "nod":
97
+ n && (n.rotation.x = Math.sin(l * Math.PI * 3) * 0.2);
98
+ break;
99
+ case "shake":
100
+ n && (n.rotation.y = Math.sin(l * Math.PI * 4) * 0.3);
101
+ break;
102
+ case "wave":
103
+ i && (i.rotation.z = 1.5 * d, i.rotation.x = -0.3 * d), u && (u.rotation.y = 0.5 + Math.sin(l * Math.PI * 6) * 0.3);
104
+ break;
105
+ case "think":
106
+ n && (n.rotation.x = 0.1 * d, n.rotation.z = 0.1 * d), i && (i.rotation.z = 0.3 * d, i.rotation.x = 0.8 * d), u && (u.rotation.y = 1.5 * d);
107
+ break;
108
+ case "bow":
109
+ p && (p.rotation.x = 0.4 * d), n && (n.rotation.x = 0.2 * d);
110
+ break;
111
+ }
112
+ } else
113
+ D = "none";
114
+ }
115
+ }
116
+ r.render(a, v);
117
+ }
118
+ function J() {
119
+ L !== null && (cancelAnimationFrame(L), L = null), window.removeEventListener("resize", T), r && g.value && (g.value.removeChild(r.domElement), r.dispose(), r = null), t && (t = null), a = null, v = null, w = null;
120
+ }
121
+ return j(
122
+ () => f.selectedResult,
123
+ (o) => {
124
+ if (o?.toolName === O && o.data) {
125
+ const e = o.data;
126
+ if (e.avatarUrl && e.avatarUrl !== _ && (console.log("[Avatar Debug] VRM URL changed:", e.avatarUrl), _ = e.avatarUrl, F(e.avatarUrl)), e.emotion && e.emotion !== S && (console.log("[Avatar Debug] Emotion changed to:", e.emotion), S = e.emotion), e.action && e.action !== "none") {
127
+ const z = e.actionTimestamp || 0;
128
+ z > C && (console.log("[Avatar Debug] Action triggered:", e.action), D = e.action, E = w?.getElapsedTime() || 0, C = z);
129
+ }
130
+ }
131
+ },
132
+ { deep: !0 }
133
+ ), j(
134
+ () => f.isAudioPlaying,
135
+ (o) => {
136
+ console.log("[Avatar Debug] Plugin received isAudioPlaying:", o);
137
+ }
138
+ ), ie(() => {
139
+ if (console.log("[Avatar Debug] View component MOUNTED"), q(), f.selectedResult?.toolName === O && f.selectedResult.data) {
140
+ const o = f.selectedResult.data;
141
+ o.avatarUrl && (console.log("[Avatar Debug] Initial VRM load:", o.avatarUrl), _ = o.avatarUrl, F(o.avatarUrl)), o.emotion && (S = o.emotion);
142
+ }
143
+ }), re(() => {
144
+ J();
145
+ }), (o, e) => (y(), B("div", se, [
146
+ N("div", {
147
+ ref_key: "containerRef",
148
+ ref: g,
149
+ class: "size-full"
150
+ }, null, 512),
151
+ x.value ? (y(), B("div", le, [...e[0] || (e[0] = [
152
+ N("div", { class: "text-white text-lg" }, "Loading Avatar...", -1)
153
+ ])])) : U("", !0),
154
+ b.value ? (y(), B("div", de, [
155
+ N("div", ce, H(b.value), 1)
156
+ ])) : U("", !0),
157
+ k.isAudioPlaying ? (y(), B("div", ue, " Speaking... ")) : U("", !0)
158
+ ]));
159
+ }
160
+ }), ge = { class: "p-3 bg-gradient-to-br from-slate-700 to-slate-800 rounded-md" }, he = { class: "flex flex-col items-center gap-2" }, pe = {
161
+ key: 0,
162
+ class: "text-xs px-2 py-0.5 bg-blue-500 text-white rounded-full"
163
+ }, fe = /* @__PURE__ */ G({
164
+ __name: "Preview",
165
+ props: {
166
+ result: {}
167
+ },
168
+ setup(k) {
169
+ const f = k, g = $(() => f.result.data), x = $(() => g.value?.emotion || "neutral");
170
+ return (b, a) => (y(), B("div", ge, [
171
+ N("div", he, [
172
+ a[0] || (a[0] = N("div", { class: "size-12 bg-slate-600 rounded-full flex items-center justify-center" }, [
173
+ N("svg", {
174
+ class: "size-8 text-slate-300",
175
+ fill: "none",
176
+ stroke: "currentColor",
177
+ viewBox: "0 0 24 24"
178
+ }, [
179
+ N("path", {
180
+ "stroke-linecap": "round",
181
+ "stroke-linejoin": "round",
182
+ "stroke-width": "2",
183
+ d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
184
+ })
185
+ ])
186
+ ], -1)),
187
+ a[1] || (a[1] = N("div", { class: "text-sm font-semibold text-white text-center" }, " 3D Avatar ", -1)),
188
+ x.value && x.value !== "neutral" ? (y(), B("div", pe, H(x.value), 1)) : U("", !0)
189
+ ])
190
+ ]));
191
+ }
192
+ }), ve = {
193
+ ...ae,
194
+ viewComponent: me,
195
+ previewComponent: fe
196
+ }, Ne = { plugin: ve };
197
+ export {
198
+ fe as Preview,
199
+ ye as SAMPLES,
200
+ Be as TOOL_DEFINITION,
201
+ O as TOOL_NAME,
202
+ me as View,
203
+ Ne as default,
204
+ be as executeAvatar,
205
+ ve as plugin,
206
+ ae as pluginCore
207
+ };
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@gui-chat-plugin/avatar",
3
+ "version": "0.0.1",
4
+ "description": "3D Avatar Plugin for GUIChat - VRM avatar with lip-sync",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./core": {
16
+ "types": "./dist/core/index.d.ts",
17
+ "import": "./dist/core.js",
18
+ "require": "./dist/core.cjs"
19
+ },
20
+ "./vue": {
21
+ "types": "./dist/vue/index.d.ts",
22
+ "import": "./dist/vue.js",
23
+ "require": "./dist/vue.cjs"
24
+ },
25
+ "./react": {
26
+ "types": "./dist/react/index.d.ts",
27
+ "import": "./dist/react.js",
28
+ "require": "./dist/react.cjs"
29
+ },
30
+ "./style.css": "./dist/style.css"
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "scripts": {
36
+ "dev": "vite",
37
+ "dev:react": "vite --config vite.config.react.ts",
38
+ "build": "vite build && vue-tsc -p tsconfig.build.json --emitDeclarationOnly && tsc -p tsconfig.react.json --emitDeclarationOnly",
39
+ "typecheck": "vue-tsc --noEmit && tsc -p tsconfig.react.json --noEmit",
40
+ "lint": "eslint src demo"
41
+ },
42
+ "peerDependencies": {
43
+ "react": "^18.0.0 || ^19.0.0",
44
+ "react-dom": "^18.0.0 || ^19.0.0",
45
+ "vue": "^3.5.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "vue": {
49
+ "optional": true
50
+ },
51
+ "react": {
52
+ "optional": true
53
+ },
54
+ "react-dom": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "devDependencies": {
59
+ "@tailwindcss/vite": "^4.1.18",
60
+ "@types/react": "^19.0.0",
61
+ "@types/react-dom": "^19.0.0",
62
+ "@types/three": "^0.174.0",
63
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
64
+ "@typescript-eslint/parser": "^8.53.0",
65
+ "@vitejs/plugin-react": "^4.5.0",
66
+ "@vitejs/plugin-vue": "^6.0.3",
67
+ "eslint": "^9.39.2",
68
+ "eslint-plugin-vue": "^10.6.2",
69
+ "globals": "^17.0.0",
70
+ "openai": "^6.16.0",
71
+ "react": "^19.0.0",
72
+ "react-dom": "^19.0.0",
73
+ "tailwindcss": "^4.1.18",
74
+ "typescript": "~5.9.3",
75
+ "vite": "^7.3.1",
76
+ "vue": "^3.5.26",
77
+ "vue-eslint-parser": "^10.2.0",
78
+ "vue-tsc": "^3.2.2"
79
+ },
80
+ "keywords": [
81
+ "guichat",
82
+ "plugin",
83
+ "avatar",
84
+ "vrm",
85
+ "3d",
86
+ "lip-sync",
87
+ "react",
88
+ "vue",
89
+ "llm",
90
+ "chat"
91
+ ],
92
+ "license": "MIT",
93
+ "dependencies": {
94
+ "@pixiv/three-vrm": "^3.3.3",
95
+ "gui-chat-protocol": "file:../gui-chat-protocol",
96
+ "three": "^0.174.0"
97
+ }
98
+ }