@aether-stack-dev/client-sdk 1.1.5 → 1.2.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/dist/AStackCSRClient.d.ts +3 -0
- package/dist/SupabaseSignalingClient.d.ts +2 -0
- package/dist/avatar/index.d.ts +0 -2
- package/dist/index.esm.js +57 -8
- package/dist/index.js +57 -8
- package/dist/react/index.d.ts +0 -2
- package/dist/react.esm.js +72 -159
- package/dist/react.js +71 -159
- package/dist/types.d.ts +1 -1
- package/package.json +5 -4
- package/dist/avatar/TalkingHeadAvatar.d.ts +0 -8
|
@@ -35,6 +35,8 @@ export interface AStackCSREvents {
|
|
|
35
35
|
model_loaded?: boolean;
|
|
36
36
|
blendshape_count?: number;
|
|
37
37
|
}) => void;
|
|
38
|
+
creditsExhausted: () => void;
|
|
39
|
+
sessionExpired: (reason: string) => void;
|
|
38
40
|
}
|
|
39
41
|
export type CallStatus = 'idle' | 'starting' | 'active' | 'stopping' | 'error';
|
|
40
42
|
export declare class AStackCSRClient extends EventEmitter<AStackCSREvents> {
|
|
@@ -65,6 +67,7 @@ export declare class AStackCSRClient extends EventEmitter<AStackCSREvents> {
|
|
|
65
67
|
startCall(options?: CallOptions): Promise<void>;
|
|
66
68
|
private startImageCapture;
|
|
67
69
|
private captureAndSendImage;
|
|
70
|
+
private cleanupLocalAudio;
|
|
68
71
|
stopCall(): void;
|
|
69
72
|
sendText(message: string): void;
|
|
70
73
|
disconnect(): Promise<void>;
|
|
@@ -13,6 +13,8 @@ export declare class SupabaseSignalingClient extends EventEmitter {
|
|
|
13
13
|
private maxReconnectAttempts;
|
|
14
14
|
private reconnectDelay;
|
|
15
15
|
private heartbeatInterval;
|
|
16
|
+
private supabaseUrl;
|
|
17
|
+
private supabasePublishableKey;
|
|
16
18
|
constructor(config: AStackConfig);
|
|
17
19
|
connect(sessionId: string, channelName: string, wsToken: string): Promise<void>;
|
|
18
20
|
private handleSignalingMessage;
|
package/dist/avatar/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export { VRMAvatar } from './VRMAvatar';
|
|
2
2
|
export type { VRMAvatarProps } from './VRMAvatar';
|
|
3
|
-
export { TalkingHeadAvatar } from './TalkingHeadAvatar';
|
|
4
|
-
export type { TalkingHeadAvatarProps } from './TalkingHeadAvatar';
|
|
5
3
|
export { ARKIT_BLENDSHAPES, BLENDSHAPE_COUNT } from './constants';
|
|
6
4
|
export type { ARKitBlendshapeName } from './constants';
|
package/dist/index.esm.js
CHANGED
|
@@ -105,22 +105,28 @@ class AudioPlayer extends EventEmitter {
|
|
|
105
105
|
const blendshapes = hasBlendshapes
|
|
106
106
|
? chunk.blendshapes
|
|
107
107
|
: this.generateAmplitudeBlendshapes(floatArray);
|
|
108
|
-
const duration = audioBuffer.duration
|
|
109
|
-
const
|
|
108
|
+
const duration = audioBuffer.duration;
|
|
109
|
+
const audioStartTime = ctx.currentTime;
|
|
110
110
|
let frameIndex = 0;
|
|
111
111
|
this.isPlaying = true;
|
|
112
112
|
this.emit('playbackStarted');
|
|
113
|
+
source.onended = () => {
|
|
114
|
+
if (this.animationFrameId !== null) {
|
|
115
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
116
|
+
this.animationFrameId = null;
|
|
117
|
+
}
|
|
118
|
+
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
119
|
+
this.emit('playbackEnded');
|
|
120
|
+
this.playNext();
|
|
121
|
+
};
|
|
113
122
|
const animate = () => {
|
|
114
123
|
if (!this.isPlaying) {
|
|
115
124
|
this.animationFrameId = null;
|
|
116
125
|
return;
|
|
117
126
|
}
|
|
118
|
-
const elapsed =
|
|
127
|
+
const elapsed = ctx.currentTime - audioStartTime;
|
|
119
128
|
if (elapsed >= duration || frameIndex >= blendshapes.length) {
|
|
120
129
|
this.animationFrameId = null;
|
|
121
|
-
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
122
|
-
this.emit('playbackEnded');
|
|
123
|
-
this.playNext();
|
|
124
130
|
return;
|
|
125
131
|
}
|
|
126
132
|
const progress = elapsed / duration;
|
|
@@ -233,7 +239,7 @@ class AStackCSRClient extends EventEmitter {
|
|
|
233
239
|
this.hasConnected = true;
|
|
234
240
|
if (isReconnect) {
|
|
235
241
|
if (this.callStatus === 'active' || this.callStatus === 'starting') {
|
|
236
|
-
this.
|
|
242
|
+
this.cleanupLocalAudio();
|
|
237
243
|
this.emit('callStopped');
|
|
238
244
|
}
|
|
239
245
|
this.emit('reconnected');
|
|
@@ -340,6 +346,13 @@ class AStackCSRClient extends EventEmitter {
|
|
|
340
346
|
case 'connected':
|
|
341
347
|
if (typeof data.clientId === 'string')
|
|
342
348
|
this.clientId = data.clientId;
|
|
349
|
+
if (data.protocol_version) {
|
|
350
|
+
const major = parseInt(data.protocol_version.split('.')[0], 10);
|
|
351
|
+
if (major > 1) {
|
|
352
|
+
console.error(`[CSR] Protocol version mismatch: server=${data.protocol_version}, expected=1.x`);
|
|
353
|
+
this.emit('error', new Error(`Protocol version mismatch: server=${data.protocol_version}`));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
343
356
|
break;
|
|
344
357
|
case 'authenticated':
|
|
345
358
|
if (this.pendingAuth) {
|
|
@@ -432,6 +445,14 @@ class AStackCSRClient extends EventEmitter {
|
|
|
432
445
|
blendshape_count: typeof data.blendshape_count === 'number' ? data.blendshape_count : undefined
|
|
433
446
|
});
|
|
434
447
|
break;
|
|
448
|
+
case 'session_expired': {
|
|
449
|
+
const reason = typeof data.reason === 'string' ? data.reason : 'unknown';
|
|
450
|
+
this.emit('sessionExpired', reason);
|
|
451
|
+
if (reason === 'credits_exhausted') {
|
|
452
|
+
this.emit('creditsExhausted');
|
|
453
|
+
}
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
435
456
|
}
|
|
436
457
|
}
|
|
437
458
|
async startCall(options) {
|
|
@@ -472,10 +493,11 @@ class AStackCSRClient extends EventEmitter {
|
|
|
472
493
|
this.startImageCapture();
|
|
473
494
|
}
|
|
474
495
|
}
|
|
496
|
+
const { systemPrompt, priorContext, configOverrides, disableA2F, providers } = options || {};
|
|
475
497
|
this.ws.send(JSON.stringify({
|
|
476
498
|
type: 'call_start',
|
|
477
499
|
fps: this.config.fps,
|
|
478
|
-
|
|
500
|
+
systemPrompt, priorContext, configOverrides, disableA2F, providers
|
|
479
501
|
}));
|
|
480
502
|
const source = this.audioContext.createMediaStreamSource(this.mediaStream);
|
|
481
503
|
this.audioProcessor = new AudioWorkletNode(this.audioContext, 'audio-processor');
|
|
@@ -546,6 +568,33 @@ class AStackCSRClient extends EventEmitter {
|
|
|
546
568
|
timestamp: Date.now()
|
|
547
569
|
}));
|
|
548
570
|
}
|
|
571
|
+
cleanupLocalAudio() {
|
|
572
|
+
if (this.imageCaptureInterval) {
|
|
573
|
+
clearInterval(this.imageCaptureInterval);
|
|
574
|
+
this.imageCaptureInterval = null;
|
|
575
|
+
}
|
|
576
|
+
if (this.videoRef) {
|
|
577
|
+
this.videoRef.srcObject = null;
|
|
578
|
+
this.videoRef = null;
|
|
579
|
+
}
|
|
580
|
+
if (this.mediaStream) {
|
|
581
|
+
this.mediaStream.getTracks().forEach(track => track.stop());
|
|
582
|
+
this.mediaStream = null;
|
|
583
|
+
}
|
|
584
|
+
if (this.audioProcessor) {
|
|
585
|
+
this.audioProcessor.disconnect();
|
|
586
|
+
this.audioProcessor.port.onmessage = null;
|
|
587
|
+
this.audioProcessor = null;
|
|
588
|
+
}
|
|
589
|
+
if (this.audioContext) {
|
|
590
|
+
this.audioContext.close();
|
|
591
|
+
this.audioContext = null;
|
|
592
|
+
}
|
|
593
|
+
this.audioPlayer.clearQueue();
|
|
594
|
+
this.callStatus = 'idle';
|
|
595
|
+
this.currentBlendshapes = new Array(BLENDSHAPE_COUNT).fill(0);
|
|
596
|
+
this.emit('blendshapeUpdate', this.currentBlendshapes);
|
|
597
|
+
}
|
|
549
598
|
stopCall() {
|
|
550
599
|
if (this.imageCaptureInterval) {
|
|
551
600
|
clearInterval(this.imageCaptureInterval);
|
package/dist/index.js
CHANGED
|
@@ -109,22 +109,28 @@ class AudioPlayer extends eventemitter3.EventEmitter {
|
|
|
109
109
|
const blendshapes = hasBlendshapes
|
|
110
110
|
? chunk.blendshapes
|
|
111
111
|
: this.generateAmplitudeBlendshapes(floatArray);
|
|
112
|
-
const duration = audioBuffer.duration
|
|
113
|
-
const
|
|
112
|
+
const duration = audioBuffer.duration;
|
|
113
|
+
const audioStartTime = ctx.currentTime;
|
|
114
114
|
let frameIndex = 0;
|
|
115
115
|
this.isPlaying = true;
|
|
116
116
|
this.emit('playbackStarted');
|
|
117
|
+
source.onended = () => {
|
|
118
|
+
if (this.animationFrameId !== null) {
|
|
119
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
120
|
+
this.animationFrameId = null;
|
|
121
|
+
}
|
|
122
|
+
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
123
|
+
this.emit('playbackEnded');
|
|
124
|
+
this.playNext();
|
|
125
|
+
};
|
|
117
126
|
const animate = () => {
|
|
118
127
|
if (!this.isPlaying) {
|
|
119
128
|
this.animationFrameId = null;
|
|
120
129
|
return;
|
|
121
130
|
}
|
|
122
|
-
const elapsed =
|
|
131
|
+
const elapsed = ctx.currentTime - audioStartTime;
|
|
123
132
|
if (elapsed >= duration || frameIndex >= blendshapes.length) {
|
|
124
133
|
this.animationFrameId = null;
|
|
125
|
-
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
126
|
-
this.emit('playbackEnded');
|
|
127
|
-
this.playNext();
|
|
128
134
|
return;
|
|
129
135
|
}
|
|
130
136
|
const progress = elapsed / duration;
|
|
@@ -237,7 +243,7 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
237
243
|
this.hasConnected = true;
|
|
238
244
|
if (isReconnect) {
|
|
239
245
|
if (this.callStatus === 'active' || this.callStatus === 'starting') {
|
|
240
|
-
this.
|
|
246
|
+
this.cleanupLocalAudio();
|
|
241
247
|
this.emit('callStopped');
|
|
242
248
|
}
|
|
243
249
|
this.emit('reconnected');
|
|
@@ -344,6 +350,13 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
344
350
|
case 'connected':
|
|
345
351
|
if (typeof data.clientId === 'string')
|
|
346
352
|
this.clientId = data.clientId;
|
|
353
|
+
if (data.protocol_version) {
|
|
354
|
+
const major = parseInt(data.protocol_version.split('.')[0], 10);
|
|
355
|
+
if (major > 1) {
|
|
356
|
+
console.error(`[CSR] Protocol version mismatch: server=${data.protocol_version}, expected=1.x`);
|
|
357
|
+
this.emit('error', new Error(`Protocol version mismatch: server=${data.protocol_version}`));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
347
360
|
break;
|
|
348
361
|
case 'authenticated':
|
|
349
362
|
if (this.pendingAuth) {
|
|
@@ -436,6 +449,14 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
436
449
|
blendshape_count: typeof data.blendshape_count === 'number' ? data.blendshape_count : undefined
|
|
437
450
|
});
|
|
438
451
|
break;
|
|
452
|
+
case 'session_expired': {
|
|
453
|
+
const reason = typeof data.reason === 'string' ? data.reason : 'unknown';
|
|
454
|
+
this.emit('sessionExpired', reason);
|
|
455
|
+
if (reason === 'credits_exhausted') {
|
|
456
|
+
this.emit('creditsExhausted');
|
|
457
|
+
}
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
439
460
|
}
|
|
440
461
|
}
|
|
441
462
|
async startCall(options) {
|
|
@@ -476,10 +497,11 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
476
497
|
this.startImageCapture();
|
|
477
498
|
}
|
|
478
499
|
}
|
|
500
|
+
const { systemPrompt, priorContext, configOverrides, disableA2F, providers } = options || {};
|
|
479
501
|
this.ws.send(JSON.stringify({
|
|
480
502
|
type: 'call_start',
|
|
481
503
|
fps: this.config.fps,
|
|
482
|
-
|
|
504
|
+
systemPrompt, priorContext, configOverrides, disableA2F, providers
|
|
483
505
|
}));
|
|
484
506
|
const source = this.audioContext.createMediaStreamSource(this.mediaStream);
|
|
485
507
|
this.audioProcessor = new AudioWorkletNode(this.audioContext, 'audio-processor');
|
|
@@ -550,6 +572,33 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
550
572
|
timestamp: Date.now()
|
|
551
573
|
}));
|
|
552
574
|
}
|
|
575
|
+
cleanupLocalAudio() {
|
|
576
|
+
if (this.imageCaptureInterval) {
|
|
577
|
+
clearInterval(this.imageCaptureInterval);
|
|
578
|
+
this.imageCaptureInterval = null;
|
|
579
|
+
}
|
|
580
|
+
if (this.videoRef) {
|
|
581
|
+
this.videoRef.srcObject = null;
|
|
582
|
+
this.videoRef = null;
|
|
583
|
+
}
|
|
584
|
+
if (this.mediaStream) {
|
|
585
|
+
this.mediaStream.getTracks().forEach(track => track.stop());
|
|
586
|
+
this.mediaStream = null;
|
|
587
|
+
}
|
|
588
|
+
if (this.audioProcessor) {
|
|
589
|
+
this.audioProcessor.disconnect();
|
|
590
|
+
this.audioProcessor.port.onmessage = null;
|
|
591
|
+
this.audioProcessor = null;
|
|
592
|
+
}
|
|
593
|
+
if (this.audioContext) {
|
|
594
|
+
this.audioContext.close();
|
|
595
|
+
this.audioContext = null;
|
|
596
|
+
}
|
|
597
|
+
this.audioPlayer.clearQueue();
|
|
598
|
+
this.callStatus = 'idle';
|
|
599
|
+
this.currentBlendshapes = new Array(BLENDSHAPE_COUNT).fill(0);
|
|
600
|
+
this.emit('blendshapeUpdate', this.currentBlendshapes);
|
|
601
|
+
}
|
|
553
602
|
stopCall() {
|
|
554
603
|
if (this.imageCaptureInterval) {
|
|
555
604
|
clearInterval(this.imageCaptureInterval);
|
package/dist/react/index.d.ts
CHANGED
|
@@ -2,5 +2,3 @@ export { useAStackCSR } from './useAStackCSR';
|
|
|
2
2
|
export type { UseAStackCSROptions, UseAStackCSRReturn } from './useAStackCSR';
|
|
3
3
|
export { VRMAvatar } from '../avatar/VRMAvatar';
|
|
4
4
|
export type { VRMAvatarProps } from '../avatar/VRMAvatar';
|
|
5
|
-
export { TalkingHeadAvatar } from '../avatar/TalkingHeadAvatar';
|
|
6
|
-
export type { TalkingHeadAvatarProps } from '../avatar/TalkingHeadAvatar';
|
package/dist/react.esm.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
2
3
|
import { EventEmitter } from 'eventemitter3';
|
|
3
4
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
@@ -110,22 +111,28 @@ class AudioPlayer extends EventEmitter {
|
|
|
110
111
|
const blendshapes = hasBlendshapes
|
|
111
112
|
? chunk.blendshapes
|
|
112
113
|
: this.generateAmplitudeBlendshapes(floatArray);
|
|
113
|
-
const duration = audioBuffer.duration
|
|
114
|
-
const
|
|
114
|
+
const duration = audioBuffer.duration;
|
|
115
|
+
const audioStartTime = ctx.currentTime;
|
|
115
116
|
let frameIndex = 0;
|
|
116
117
|
this.isPlaying = true;
|
|
117
118
|
this.emit('playbackStarted');
|
|
119
|
+
source.onended = () => {
|
|
120
|
+
if (this.animationFrameId !== null) {
|
|
121
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
122
|
+
this.animationFrameId = null;
|
|
123
|
+
}
|
|
124
|
+
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
125
|
+
this.emit('playbackEnded');
|
|
126
|
+
this.playNext();
|
|
127
|
+
};
|
|
118
128
|
const animate = () => {
|
|
119
129
|
if (!this.isPlaying) {
|
|
120
130
|
this.animationFrameId = null;
|
|
121
131
|
return;
|
|
122
132
|
}
|
|
123
|
-
const elapsed =
|
|
133
|
+
const elapsed = ctx.currentTime - audioStartTime;
|
|
124
134
|
if (elapsed >= duration || frameIndex >= blendshapes.length) {
|
|
125
135
|
this.animationFrameId = null;
|
|
126
|
-
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
127
|
-
this.emit('playbackEnded');
|
|
128
|
-
this.playNext();
|
|
129
136
|
return;
|
|
130
137
|
}
|
|
131
138
|
const progress = elapsed / duration;
|
|
@@ -238,7 +245,7 @@ class AStackCSRClient extends EventEmitter {
|
|
|
238
245
|
this.hasConnected = true;
|
|
239
246
|
if (isReconnect) {
|
|
240
247
|
if (this.callStatus === 'active' || this.callStatus === 'starting') {
|
|
241
|
-
this.
|
|
248
|
+
this.cleanupLocalAudio();
|
|
242
249
|
this.emit('callStopped');
|
|
243
250
|
}
|
|
244
251
|
this.emit('reconnected');
|
|
@@ -345,6 +352,13 @@ class AStackCSRClient extends EventEmitter {
|
|
|
345
352
|
case 'connected':
|
|
346
353
|
if (typeof data.clientId === 'string')
|
|
347
354
|
this.clientId = data.clientId;
|
|
355
|
+
if (data.protocol_version) {
|
|
356
|
+
const major = parseInt(data.protocol_version.split('.')[0], 10);
|
|
357
|
+
if (major > 1) {
|
|
358
|
+
console.error(`[CSR] Protocol version mismatch: server=${data.protocol_version}, expected=1.x`);
|
|
359
|
+
this.emit('error', new Error(`Protocol version mismatch: server=${data.protocol_version}`));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
348
362
|
break;
|
|
349
363
|
case 'authenticated':
|
|
350
364
|
if (this.pendingAuth) {
|
|
@@ -437,6 +451,14 @@ class AStackCSRClient extends EventEmitter {
|
|
|
437
451
|
blendshape_count: typeof data.blendshape_count === 'number' ? data.blendshape_count : undefined
|
|
438
452
|
});
|
|
439
453
|
break;
|
|
454
|
+
case 'session_expired': {
|
|
455
|
+
const reason = typeof data.reason === 'string' ? data.reason : 'unknown';
|
|
456
|
+
this.emit('sessionExpired', reason);
|
|
457
|
+
if (reason === 'credits_exhausted') {
|
|
458
|
+
this.emit('creditsExhausted');
|
|
459
|
+
}
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
440
462
|
}
|
|
441
463
|
}
|
|
442
464
|
async startCall(options) {
|
|
@@ -477,10 +499,11 @@ class AStackCSRClient extends EventEmitter {
|
|
|
477
499
|
this.startImageCapture();
|
|
478
500
|
}
|
|
479
501
|
}
|
|
502
|
+
const { systemPrompt, priorContext, configOverrides, disableA2F, providers } = options || {};
|
|
480
503
|
this.ws.send(JSON.stringify({
|
|
481
504
|
type: 'call_start',
|
|
482
505
|
fps: this.config.fps,
|
|
483
|
-
|
|
506
|
+
systemPrompt, priorContext, configOverrides, disableA2F, providers
|
|
484
507
|
}));
|
|
485
508
|
const source = this.audioContext.createMediaStreamSource(this.mediaStream);
|
|
486
509
|
this.audioProcessor = new AudioWorkletNode(this.audioContext, 'audio-processor');
|
|
@@ -551,6 +574,33 @@ class AStackCSRClient extends EventEmitter {
|
|
|
551
574
|
timestamp: Date.now()
|
|
552
575
|
}));
|
|
553
576
|
}
|
|
577
|
+
cleanupLocalAudio() {
|
|
578
|
+
if (this.imageCaptureInterval) {
|
|
579
|
+
clearInterval(this.imageCaptureInterval);
|
|
580
|
+
this.imageCaptureInterval = null;
|
|
581
|
+
}
|
|
582
|
+
if (this.videoRef) {
|
|
583
|
+
this.videoRef.srcObject = null;
|
|
584
|
+
this.videoRef = null;
|
|
585
|
+
}
|
|
586
|
+
if (this.mediaStream) {
|
|
587
|
+
this.mediaStream.getTracks().forEach(track => track.stop());
|
|
588
|
+
this.mediaStream = null;
|
|
589
|
+
}
|
|
590
|
+
if (this.audioProcessor) {
|
|
591
|
+
this.audioProcessor.disconnect();
|
|
592
|
+
this.audioProcessor.port.onmessage = null;
|
|
593
|
+
this.audioProcessor = null;
|
|
594
|
+
}
|
|
595
|
+
if (this.audioContext) {
|
|
596
|
+
this.audioContext.close();
|
|
597
|
+
this.audioContext = null;
|
|
598
|
+
}
|
|
599
|
+
this.audioPlayer.clearQueue();
|
|
600
|
+
this.callStatus = 'idle';
|
|
601
|
+
this.currentBlendshapes = new Array(BLENDSHAPE_COUNT).fill(0);
|
|
602
|
+
this.emit('blendshapeUpdate', this.currentBlendshapes);
|
|
603
|
+
}
|
|
554
604
|
stopCall() {
|
|
555
605
|
if (this.imageCaptureInterval) {
|
|
556
606
|
clearInterval(this.imageCaptureInterval);
|
|
@@ -786,11 +836,19 @@ function VRMAvatar({ blendshapes, width = 400, height = 400, modelUrl = '/models
|
|
|
786
836
|
const container = containerRef.current;
|
|
787
837
|
if (!container)
|
|
788
838
|
return;
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
839
|
+
let renderer;
|
|
840
|
+
try {
|
|
841
|
+
renderer = new THREE.WebGLRenderer({
|
|
842
|
+
antialias: true,
|
|
843
|
+
alpha: true,
|
|
844
|
+
powerPreference: 'high-performance'
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
catch {
|
|
848
|
+
setError('WebGL is not supported in this browser');
|
|
849
|
+
setLoading(false);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
794
852
|
renderer.setSize(width, height);
|
|
795
853
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
796
854
|
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
|
@@ -898,149 +956,4 @@ function VRMAvatar({ blendshapes, width = 400, height = 400, modelUrl = '/models
|
|
|
898
956
|
return (jsxs("div", { className: "relative", style: { width, height }, children: [jsx("div", { ref: containerRef, className: "rounded-lg overflow-hidden" }), loading && (jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-800 rounded-lg", children: jsx("div", { className: "text-white text-sm", children: "Loading VRM avatar..." }) })), error && (jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-red-900/50 rounded-lg", children: jsx("div", { className: "text-white text-sm", children: error }) }))] }));
|
|
899
957
|
}
|
|
900
958
|
|
|
901
|
-
|
|
902
|
-
const iframeRef = useRef(null);
|
|
903
|
-
const [loading, setLoading] = useState(true);
|
|
904
|
-
const [error, setError] = useState(null);
|
|
905
|
-
useEffect(() => {
|
|
906
|
-
const iframe = iframeRef.current;
|
|
907
|
-
if (!iframe)
|
|
908
|
-
return;
|
|
909
|
-
const handleMessage = (event) => {
|
|
910
|
-
if (event.data?.type === 'talkinghead-ready') {
|
|
911
|
-
setLoading(false);
|
|
912
|
-
}
|
|
913
|
-
else if (event.data?.type === 'talkinghead-error') {
|
|
914
|
-
setError(event.data.message || 'Failed to load TalkingHead avatar');
|
|
915
|
-
setLoading(false);
|
|
916
|
-
}
|
|
917
|
-
};
|
|
918
|
-
window.addEventListener('message', handleMessage);
|
|
919
|
-
return () => window.removeEventListener('message', handleMessage);
|
|
920
|
-
}, []);
|
|
921
|
-
useEffect(() => {
|
|
922
|
-
const iframe = iframeRef.current;
|
|
923
|
-
if (!iframe?.contentWindow || blendshapes.length === 0)
|
|
924
|
-
return;
|
|
925
|
-
const blendshapeData = {};
|
|
926
|
-
ARKIT_BLENDSHAPES.forEach((name, i) => {
|
|
927
|
-
blendshapeData[name] = blendshapes[i] || 0;
|
|
928
|
-
});
|
|
929
|
-
iframe.contentWindow.postMessage({
|
|
930
|
-
type: 'update-blendshapes',
|
|
931
|
-
blendshapes: blendshapeData
|
|
932
|
-
}, '*');
|
|
933
|
-
}, [blendshapes]);
|
|
934
|
-
const iframeSrc = `data:text/html,${encodeURIComponent(`
|
|
935
|
-
<!DOCTYPE html>
|
|
936
|
-
<html>
|
|
937
|
-
<head>
|
|
938
|
-
<style>
|
|
939
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
940
|
-
html, body { width: 100%; height: 100%; overflow: hidden; background: #1a1a2e; }
|
|
941
|
-
#avatar { width: 100%; height: 100%; }
|
|
942
|
-
</style>
|
|
943
|
-
<script type="importmap">
|
|
944
|
-
{
|
|
945
|
-
"imports": {
|
|
946
|
-
"three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js",
|
|
947
|
-
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/"
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
${"</"}script>
|
|
951
|
-
</head>
|
|
952
|
-
<body>
|
|
953
|
-
<div id="avatar"></div>
|
|
954
|
-
<script type="module">
|
|
955
|
-
import { TalkingHead } from 'https://cdn.jsdelivr.net/npm/@met4citizen/talkinghead@1.6.0/modules/talkinghead.mjs';
|
|
956
|
-
|
|
957
|
-
let head = null;
|
|
958
|
-
|
|
959
|
-
async function init() {
|
|
960
|
-
try {
|
|
961
|
-
const container = document.getElementById('avatar');
|
|
962
|
-
head = new TalkingHead(container, {
|
|
963
|
-
ttsEndpoint: null,
|
|
964
|
-
cameraView: 'head',
|
|
965
|
-
cameraRotateEnable: false,
|
|
966
|
-
cameraPanEnable: false,
|
|
967
|
-
cameraZoomEnable: false,
|
|
968
|
-
lightAmbientColor: 0xffffff,
|
|
969
|
-
lightAmbientIntensity: 0.6,
|
|
970
|
-
lightDirectColor: 0xffffff,
|
|
971
|
-
lightDirectIntensity: 0.8,
|
|
972
|
-
lightSpotIntensity: 0,
|
|
973
|
-
avatarMood: 'neutral',
|
|
974
|
-
modelPixelRatio: Math.min(window.devicePixelRatio, 2),
|
|
975
|
-
modelFPS: 30
|
|
976
|
-
});
|
|
977
|
-
|
|
978
|
-
await head.showAvatar({
|
|
979
|
-
url: '${avatarUrl}',
|
|
980
|
-
body: 'F',
|
|
981
|
-
avatarMood: 'neutral',
|
|
982
|
-
lipsyncLang: 'en'
|
|
983
|
-
});
|
|
984
|
-
|
|
985
|
-
window.parent.postMessage({ type: 'talkinghead-ready' }, '*');
|
|
986
|
-
} catch (err) {
|
|
987
|
-
console.error('[TalkingHead] Error:', err);
|
|
988
|
-
window.parent.postMessage({ type: 'talkinghead-error', message: err.message }, '*');
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
let started = false;
|
|
993
|
-
|
|
994
|
-
window.addEventListener('message', (event) => {
|
|
995
|
-
if (event.data?.type === 'update-blendshapes' && head) {
|
|
996
|
-
if (!started) {
|
|
997
|
-
head.start();
|
|
998
|
-
started = true;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
const shapes = event.data.blendshapes;
|
|
1002
|
-
const mtAvatar = head.mtAvatar;
|
|
1003
|
-
|
|
1004
|
-
if (mtAvatar) {
|
|
1005
|
-
for (const [name, value] of Object.entries(shapes)) {
|
|
1006
|
-
const v = Math.min(Number(value) || 0, 1);
|
|
1007
|
-
if (mtAvatar[name]) {
|
|
1008
|
-
mtAvatar[name].value = v;
|
|
1009
|
-
mtAvatar[name].applied = v;
|
|
1010
|
-
if (mtAvatar[name].ms && mtAvatar[name].is) {
|
|
1011
|
-
for (let i = 0; i < mtAvatar[name].ms.length; i++) {
|
|
1012
|
-
const influences = mtAvatar[name].ms[i];
|
|
1013
|
-
const idx = mtAvatar[name].is[i];
|
|
1014
|
-
if (influences && idx !== undefined) {
|
|
1015
|
-
influences[idx] = v;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
if (head.morphs) {
|
|
1024
|
-
for (const mesh of head.morphs) {
|
|
1025
|
-
if (mesh.morphTargetDictionary && mesh.morphTargetInfluences) {
|
|
1026
|
-
for (const [name, value] of Object.entries(shapes)) {
|
|
1027
|
-
const idx = mesh.morphTargetDictionary[name];
|
|
1028
|
-
if (idx !== undefined) {
|
|
1029
|
-
mesh.morphTargetInfluences[idx] = Math.min(Number(value) || 0, 1);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
|
|
1038
|
-
init();
|
|
1039
|
-
${"</"}script>
|
|
1040
|
-
</body>
|
|
1041
|
-
</html>
|
|
1042
|
-
`)}`;
|
|
1043
|
-
return (jsxs("div", { className: "relative bg-gray-900 rounded-lg overflow-hidden", style: { width, height }, children: [jsx("iframe", { ref: iframeRef, src: iframeSrc, style: { width: '100%', height: '100%', border: 'none' }, sandbox: "allow-scripts allow-same-origin" }), loading && (jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-800 rounded-lg", children: jsx("div", { className: "text-white text-sm", children: "Loading TalkingHead avatar..." }) })), error && (jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-red-900/50 rounded-lg", children: jsx("div", { className: "text-white text-sm text-center p-4", children: error }) }))] }));
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
export { TalkingHeadAvatar, VRMAvatar, useAStackCSR };
|
|
959
|
+
export { VRMAvatar, useAStackCSR };
|
package/dist/react.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
4
|
var react = require('react');
|
|
@@ -131,22 +132,28 @@ class AudioPlayer extends eventemitter3.EventEmitter {
|
|
|
131
132
|
const blendshapes = hasBlendshapes
|
|
132
133
|
? chunk.blendshapes
|
|
133
134
|
: this.generateAmplitudeBlendshapes(floatArray);
|
|
134
|
-
const duration = audioBuffer.duration
|
|
135
|
-
const
|
|
135
|
+
const duration = audioBuffer.duration;
|
|
136
|
+
const audioStartTime = ctx.currentTime;
|
|
136
137
|
let frameIndex = 0;
|
|
137
138
|
this.isPlaying = true;
|
|
138
139
|
this.emit('playbackStarted');
|
|
140
|
+
source.onended = () => {
|
|
141
|
+
if (this.animationFrameId !== null) {
|
|
142
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
143
|
+
this.animationFrameId = null;
|
|
144
|
+
}
|
|
145
|
+
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
146
|
+
this.emit('playbackEnded');
|
|
147
|
+
this.playNext();
|
|
148
|
+
};
|
|
139
149
|
const animate = () => {
|
|
140
150
|
if (!this.isPlaying) {
|
|
141
151
|
this.animationFrameId = null;
|
|
142
152
|
return;
|
|
143
153
|
}
|
|
144
|
-
const elapsed =
|
|
154
|
+
const elapsed = ctx.currentTime - audioStartTime;
|
|
145
155
|
if (elapsed >= duration || frameIndex >= blendshapes.length) {
|
|
146
156
|
this.animationFrameId = null;
|
|
147
|
-
this.emit('blendshapeUpdate', new Array(BLENDSHAPE_COUNT).fill(0));
|
|
148
|
-
this.emit('playbackEnded');
|
|
149
|
-
this.playNext();
|
|
150
157
|
return;
|
|
151
158
|
}
|
|
152
159
|
const progress = elapsed / duration;
|
|
@@ -259,7 +266,7 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
259
266
|
this.hasConnected = true;
|
|
260
267
|
if (isReconnect) {
|
|
261
268
|
if (this.callStatus === 'active' || this.callStatus === 'starting') {
|
|
262
|
-
this.
|
|
269
|
+
this.cleanupLocalAudio();
|
|
263
270
|
this.emit('callStopped');
|
|
264
271
|
}
|
|
265
272
|
this.emit('reconnected');
|
|
@@ -366,6 +373,13 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
366
373
|
case 'connected':
|
|
367
374
|
if (typeof data.clientId === 'string')
|
|
368
375
|
this.clientId = data.clientId;
|
|
376
|
+
if (data.protocol_version) {
|
|
377
|
+
const major = parseInt(data.protocol_version.split('.')[0], 10);
|
|
378
|
+
if (major > 1) {
|
|
379
|
+
console.error(`[CSR] Protocol version mismatch: server=${data.protocol_version}, expected=1.x`);
|
|
380
|
+
this.emit('error', new Error(`Protocol version mismatch: server=${data.protocol_version}`));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
369
383
|
break;
|
|
370
384
|
case 'authenticated':
|
|
371
385
|
if (this.pendingAuth) {
|
|
@@ -458,6 +472,14 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
458
472
|
blendshape_count: typeof data.blendshape_count === 'number' ? data.blendshape_count : undefined
|
|
459
473
|
});
|
|
460
474
|
break;
|
|
475
|
+
case 'session_expired': {
|
|
476
|
+
const reason = typeof data.reason === 'string' ? data.reason : 'unknown';
|
|
477
|
+
this.emit('sessionExpired', reason);
|
|
478
|
+
if (reason === 'credits_exhausted') {
|
|
479
|
+
this.emit('creditsExhausted');
|
|
480
|
+
}
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
461
483
|
}
|
|
462
484
|
}
|
|
463
485
|
async startCall(options) {
|
|
@@ -498,10 +520,11 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
498
520
|
this.startImageCapture();
|
|
499
521
|
}
|
|
500
522
|
}
|
|
523
|
+
const { systemPrompt, priorContext, configOverrides, disableA2F, providers } = options || {};
|
|
501
524
|
this.ws.send(JSON.stringify({
|
|
502
525
|
type: 'call_start',
|
|
503
526
|
fps: this.config.fps,
|
|
504
|
-
|
|
527
|
+
systemPrompt, priorContext, configOverrides, disableA2F, providers
|
|
505
528
|
}));
|
|
506
529
|
const source = this.audioContext.createMediaStreamSource(this.mediaStream);
|
|
507
530
|
this.audioProcessor = new AudioWorkletNode(this.audioContext, 'audio-processor');
|
|
@@ -572,6 +595,33 @@ class AStackCSRClient extends eventemitter3.EventEmitter {
|
|
|
572
595
|
timestamp: Date.now()
|
|
573
596
|
}));
|
|
574
597
|
}
|
|
598
|
+
cleanupLocalAudio() {
|
|
599
|
+
if (this.imageCaptureInterval) {
|
|
600
|
+
clearInterval(this.imageCaptureInterval);
|
|
601
|
+
this.imageCaptureInterval = null;
|
|
602
|
+
}
|
|
603
|
+
if (this.videoRef) {
|
|
604
|
+
this.videoRef.srcObject = null;
|
|
605
|
+
this.videoRef = null;
|
|
606
|
+
}
|
|
607
|
+
if (this.mediaStream) {
|
|
608
|
+
this.mediaStream.getTracks().forEach(track => track.stop());
|
|
609
|
+
this.mediaStream = null;
|
|
610
|
+
}
|
|
611
|
+
if (this.audioProcessor) {
|
|
612
|
+
this.audioProcessor.disconnect();
|
|
613
|
+
this.audioProcessor.port.onmessage = null;
|
|
614
|
+
this.audioProcessor = null;
|
|
615
|
+
}
|
|
616
|
+
if (this.audioContext) {
|
|
617
|
+
this.audioContext.close();
|
|
618
|
+
this.audioContext = null;
|
|
619
|
+
}
|
|
620
|
+
this.audioPlayer.clearQueue();
|
|
621
|
+
this.callStatus = 'idle';
|
|
622
|
+
this.currentBlendshapes = new Array(BLENDSHAPE_COUNT).fill(0);
|
|
623
|
+
this.emit('blendshapeUpdate', this.currentBlendshapes);
|
|
624
|
+
}
|
|
575
625
|
stopCall() {
|
|
576
626
|
if (this.imageCaptureInterval) {
|
|
577
627
|
clearInterval(this.imageCaptureInterval);
|
|
@@ -807,11 +857,19 @@ function VRMAvatar({ blendshapes, width = 400, height = 400, modelUrl = '/models
|
|
|
807
857
|
const container = containerRef.current;
|
|
808
858
|
if (!container)
|
|
809
859
|
return;
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
860
|
+
let renderer;
|
|
861
|
+
try {
|
|
862
|
+
renderer = new THREE__namespace.WebGLRenderer({
|
|
863
|
+
antialias: true,
|
|
864
|
+
alpha: true,
|
|
865
|
+
powerPreference: 'high-performance'
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
catch {
|
|
869
|
+
setError('WebGL is not supported in this browser');
|
|
870
|
+
setLoading(false);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
815
873
|
renderer.setSize(width, height);
|
|
816
874
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
817
875
|
renderer.outputColorSpace = THREE__namespace.SRGBColorSpace;
|
|
@@ -919,151 +977,5 @@ function VRMAvatar({ blendshapes, width = 400, height = 400, modelUrl = '/models
|
|
|
919
977
|
return (jsxRuntime.jsxs("div", { className: "relative", style: { width, height }, children: [jsxRuntime.jsx("div", { ref: containerRef, className: "rounded-lg overflow-hidden" }), loading && (jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-800 rounded-lg", children: jsxRuntime.jsx("div", { className: "text-white text-sm", children: "Loading VRM avatar..." }) })), error && (jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-red-900/50 rounded-lg", children: jsxRuntime.jsx("div", { className: "text-white text-sm", children: error }) }))] }));
|
|
920
978
|
}
|
|
921
979
|
|
|
922
|
-
function TalkingHeadAvatar({ blendshapes, width = 400, height = 400, avatarUrl = 'https://models.readyplayer.me/6460d95f9ae10f45bffb2864.glb?morphTargets=ARKit,Oculus+Visemes,mouthOpen,mouthSmile,eyesClosed,eyesLookUp,eyesLookDown&textureSizeLimit=1024&textureFormat=png' }) {
|
|
923
|
-
const iframeRef = react.useRef(null);
|
|
924
|
-
const [loading, setLoading] = react.useState(true);
|
|
925
|
-
const [error, setError] = react.useState(null);
|
|
926
|
-
react.useEffect(() => {
|
|
927
|
-
const iframe = iframeRef.current;
|
|
928
|
-
if (!iframe)
|
|
929
|
-
return;
|
|
930
|
-
const handleMessage = (event) => {
|
|
931
|
-
if (event.data?.type === 'talkinghead-ready') {
|
|
932
|
-
setLoading(false);
|
|
933
|
-
}
|
|
934
|
-
else if (event.data?.type === 'talkinghead-error') {
|
|
935
|
-
setError(event.data.message || 'Failed to load TalkingHead avatar');
|
|
936
|
-
setLoading(false);
|
|
937
|
-
}
|
|
938
|
-
};
|
|
939
|
-
window.addEventListener('message', handleMessage);
|
|
940
|
-
return () => window.removeEventListener('message', handleMessage);
|
|
941
|
-
}, []);
|
|
942
|
-
react.useEffect(() => {
|
|
943
|
-
const iframe = iframeRef.current;
|
|
944
|
-
if (!iframe?.contentWindow || blendshapes.length === 0)
|
|
945
|
-
return;
|
|
946
|
-
const blendshapeData = {};
|
|
947
|
-
ARKIT_BLENDSHAPES.forEach((name, i) => {
|
|
948
|
-
blendshapeData[name] = blendshapes[i] || 0;
|
|
949
|
-
});
|
|
950
|
-
iframe.contentWindow.postMessage({
|
|
951
|
-
type: 'update-blendshapes',
|
|
952
|
-
blendshapes: blendshapeData
|
|
953
|
-
}, '*');
|
|
954
|
-
}, [blendshapes]);
|
|
955
|
-
const iframeSrc = `data:text/html,${encodeURIComponent(`
|
|
956
|
-
<!DOCTYPE html>
|
|
957
|
-
<html>
|
|
958
|
-
<head>
|
|
959
|
-
<style>
|
|
960
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
961
|
-
html, body { width: 100%; height: 100%; overflow: hidden; background: #1a1a2e; }
|
|
962
|
-
#avatar { width: 100%; height: 100%; }
|
|
963
|
-
</style>
|
|
964
|
-
<script type="importmap">
|
|
965
|
-
{
|
|
966
|
-
"imports": {
|
|
967
|
-
"three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js",
|
|
968
|
-
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/"
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
${"</"}script>
|
|
972
|
-
</head>
|
|
973
|
-
<body>
|
|
974
|
-
<div id="avatar"></div>
|
|
975
|
-
<script type="module">
|
|
976
|
-
import { TalkingHead } from 'https://cdn.jsdelivr.net/npm/@met4citizen/talkinghead@1.6.0/modules/talkinghead.mjs';
|
|
977
|
-
|
|
978
|
-
let head = null;
|
|
979
|
-
|
|
980
|
-
async function init() {
|
|
981
|
-
try {
|
|
982
|
-
const container = document.getElementById('avatar');
|
|
983
|
-
head = new TalkingHead(container, {
|
|
984
|
-
ttsEndpoint: null,
|
|
985
|
-
cameraView: 'head',
|
|
986
|
-
cameraRotateEnable: false,
|
|
987
|
-
cameraPanEnable: false,
|
|
988
|
-
cameraZoomEnable: false,
|
|
989
|
-
lightAmbientColor: 0xffffff,
|
|
990
|
-
lightAmbientIntensity: 0.6,
|
|
991
|
-
lightDirectColor: 0xffffff,
|
|
992
|
-
lightDirectIntensity: 0.8,
|
|
993
|
-
lightSpotIntensity: 0,
|
|
994
|
-
avatarMood: 'neutral',
|
|
995
|
-
modelPixelRatio: Math.min(window.devicePixelRatio, 2),
|
|
996
|
-
modelFPS: 30
|
|
997
|
-
});
|
|
998
|
-
|
|
999
|
-
await head.showAvatar({
|
|
1000
|
-
url: '${avatarUrl}',
|
|
1001
|
-
body: 'F',
|
|
1002
|
-
avatarMood: 'neutral',
|
|
1003
|
-
lipsyncLang: 'en'
|
|
1004
|
-
});
|
|
1005
|
-
|
|
1006
|
-
window.parent.postMessage({ type: 'talkinghead-ready' }, '*');
|
|
1007
|
-
} catch (err) {
|
|
1008
|
-
console.error('[TalkingHead] Error:', err);
|
|
1009
|
-
window.parent.postMessage({ type: 'talkinghead-error', message: err.message }, '*');
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
let started = false;
|
|
1014
|
-
|
|
1015
|
-
window.addEventListener('message', (event) => {
|
|
1016
|
-
if (event.data?.type === 'update-blendshapes' && head) {
|
|
1017
|
-
if (!started) {
|
|
1018
|
-
head.start();
|
|
1019
|
-
started = true;
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
const shapes = event.data.blendshapes;
|
|
1023
|
-
const mtAvatar = head.mtAvatar;
|
|
1024
|
-
|
|
1025
|
-
if (mtAvatar) {
|
|
1026
|
-
for (const [name, value] of Object.entries(shapes)) {
|
|
1027
|
-
const v = Math.min(Number(value) || 0, 1);
|
|
1028
|
-
if (mtAvatar[name]) {
|
|
1029
|
-
mtAvatar[name].value = v;
|
|
1030
|
-
mtAvatar[name].applied = v;
|
|
1031
|
-
if (mtAvatar[name].ms && mtAvatar[name].is) {
|
|
1032
|
-
for (let i = 0; i < mtAvatar[name].ms.length; i++) {
|
|
1033
|
-
const influences = mtAvatar[name].ms[i];
|
|
1034
|
-
const idx = mtAvatar[name].is[i];
|
|
1035
|
-
if (influences && idx !== undefined) {
|
|
1036
|
-
influences[idx] = v;
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
if (head.morphs) {
|
|
1045
|
-
for (const mesh of head.morphs) {
|
|
1046
|
-
if (mesh.morphTargetDictionary && mesh.morphTargetInfluences) {
|
|
1047
|
-
for (const [name, value] of Object.entries(shapes)) {
|
|
1048
|
-
const idx = mesh.morphTargetDictionary[name];
|
|
1049
|
-
if (idx !== undefined) {
|
|
1050
|
-
mesh.morphTargetInfluences[idx] = Math.min(Number(value) || 0, 1);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
init();
|
|
1060
|
-
${"</"}script>
|
|
1061
|
-
</body>
|
|
1062
|
-
</html>
|
|
1063
|
-
`)}`;
|
|
1064
|
-
return (jsxRuntime.jsxs("div", { className: "relative bg-gray-900 rounded-lg overflow-hidden", style: { width, height }, children: [jsxRuntime.jsx("iframe", { ref: iframeRef, src: iframeSrc, style: { width: '100%', height: '100%', border: 'none' }, sandbox: "allow-scripts allow-same-origin" }), loading && (jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-800 rounded-lg", children: jsxRuntime.jsx("div", { className: "text-white text-sm", children: "Loading TalkingHead avatar..." }) })), error && (jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-red-900/50 rounded-lg", children: jsxRuntime.jsx("div", { className: "text-white text-sm text-center p-4", children: error }) }))] }));
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
exports.TalkingHeadAvatar = TalkingHeadAvatar;
|
|
1068
980
|
exports.VRMAvatar = VRMAvatar;
|
|
1069
981
|
exports.useAStackCSR = useAStackCSR;
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aether-stack-dev/client-sdk",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "JavaScript/TypeScript SDK for AStack video-to-video AI conversations",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.esm.js",
|
|
8
|
-
"types": "dist/
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"import": "./dist/index.esm.js",
|
|
12
12
|
"require": "./dist/index.js",
|
|
13
|
-
"types": "./dist/
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
14
|
},
|
|
15
15
|
"./react": {
|
|
16
16
|
"import": "./dist/react.esm.js",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"build:types": "tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
29
29
|
"test": "jest",
|
|
30
30
|
"test:watch": "jest --watch",
|
|
31
|
+
"test:coverage": "jest --coverage",
|
|
31
32
|
"lint": "eslint src/**/*.{ts,tsx}",
|
|
32
33
|
"typecheck": "tsc --noEmit",
|
|
33
34
|
"prepublishOnly": "npm run build"
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"license": "MIT",
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@pixiv/three-vrm": "^3.4.4",
|
|
46
|
-
"@supabase/supabase-js": "^2.
|
|
47
|
+
"@supabase/supabase-js": "^2.100.0",
|
|
47
48
|
"eventemitter3": "^5.0.1",
|
|
48
49
|
"three": "^0.181.2",
|
|
49
50
|
"uuid": "^9.0.1"
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface TalkingHeadAvatarProps {
|
|
2
|
-
blendshapes: number[];
|
|
3
|
-
width?: number;
|
|
4
|
-
height?: number;
|
|
5
|
-
avatarUrl?: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function TalkingHeadAvatar({ blendshapes, width, height, avatarUrl }: TalkingHeadAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export default TalkingHeadAvatar;
|