@rannah-labs/react-voice-orb 1.1.3
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 +240 -0
- package/assets/orb-ss.png +0 -0
- package/assets/thing.png +0 -0
- package/dist/bun-server/src/types/index.d.ts +37 -0
- package/dist/bun-server/src/types/index.js +2 -0
- package/dist/src/app/Types/TranscriberStuff.d.ts +0 -0
- package/dist/src/app/Types/TranscriberStuff.js +1 -0
- package/dist/src/app/Types/apiModels.d.ts +31 -0
- package/dist/src/app/Types/apiModels.js +2 -0
- package/dist/src/app/Types/api_types.d.ts +34 -0
- package/dist/src/app/Types/api_types.js +2 -0
- package/dist/src/app/Types/better-trpc.d.ts +5 -0
- package/dist/src/app/Types/better-trpc.js +23 -0
- package/dist/src/app/Types/browser-only.d.ts +1 -0
- package/dist/src/app/Types/browser-only.js +12 -0
- package/dist/src/app/Types/builltInNodesTemplates.d.ts +1070 -0
- package/dist/src/app/Types/builltInNodesTemplates.js +1341 -0
- package/dist/src/app/Types/defaults.d.ts +7 -0
- package/dist/src/app/Types/defaults.js +210 -0
- package/dist/src/app/Types/demo_extension.d.ts +11 -0
- package/dist/src/app/Types/demo_extension.js +190 -0
- package/dist/src/app/Types/envObject.d.ts +97 -0
- package/dist/src/app/Types/envObject.js +5 -0
- package/dist/src/app/Types/firebase.d.ts +30413 -0
- package/dist/src/app/Types/firebase.js +6696 -0
- package/dist/src/app/Types/gen_openapi.d.ts +130 -0
- package/dist/src/app/Types/gen_openapi.js +2 -0
- package/dist/src/app/Types/global_types.d.ts +254 -0
- package/dist/src/app/Types/global_types.js +6 -0
- package/dist/src/app/Types/messages_types.d.ts +76 -0
- package/dist/src/app/Types/messages_types.js +1 -0
- package/dist/src/app/Types/vapi_types.d.ts +358 -0
- package/dist/src/app/Types/vapi_types.js +2 -0
- package/dist/src/app/Types/vf_types.d.ts +33 -0
- package/dist/src/app/Types/vf_types.js +2 -0
- package/dist/src/public_packages/react-voice-orb/src/animation-orb.d.ts +11 -0
- package/dist/src/public_packages/react-voice-orb/src/animation-orb.js +397 -0
- package/dist/src/public_packages/react-voice-orb/src/index.d.ts +5 -0
- package/dist/src/public_packages/react-voice-orb/src/index.js +16 -0
- package/dist/src/public_packages/react-voice-orb/src/providers/AIAudioProvider.d.ts +21 -0
- package/dist/src/public_packages/react-voice-orb/src/providers/AIAudioProvider.js +81 -0
- package/dist/src/public_packages/react-voice-orb/src/providers/ThemeProvider.d.ts +23 -0
- package/dist/src/public_packages/react-voice-orb/src/providers/ThemeProvider.js +202 -0
- package/dist/src/public_packages/react-voice-orb/src/scripts/same-script.d.ts +1 -0
- package/dist/src/public_packages/react-voice-orb/src/scripts/same-script.js +79 -0
- package/dist/src/public_packages/react-voice-orb/src/spinner.d.ts +3 -0
- package/dist/src/public_packages/react-voice-orb/src/spinner.js +17 -0
- package/dist/src/public_packages/react-voice-orb/tsconfig.tsbuildinfo +1 -0
- package/dist/vg-docker/src/+global_consts/client.d.ts +84 -0
- package/dist/vg-docker/src/+global_consts/client.js +105 -0
- package/package.json +46 -0
- package/scripts/post-tsc.ts +74 -0
- package/src/animation-orb.tsx +441 -0
- package/src/index.ts +5 -0
- package/src/providers/AIAudioProvider.tsx +100 -0
- package/src/providers/ThemeProvider.tsx +211 -0
- package/src/scripts/same-script.ts +73 -0
- package/src/spinner.tsx +19 -0
- package/tsconfig.json +34 -0
- package/webpack.config.js +141 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useEffect, useRef } from "react";
|
|
3
|
+
import * as THREE from "three";
|
|
4
|
+
import { useOrbTheme } from "./providers/ThemeProvider"
|
|
5
|
+
import { useAIVoice } from "./providers/AIAudioProvider";
|
|
6
|
+
|
|
7
|
+
const AudioAnimationOrb = (props: {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
colorPallet?: string[];
|
|
11
|
+
animationSensitivity?: number;
|
|
12
|
+
orbPlaceholder?: React.ReactNode;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
showOrb: boolean;
|
|
15
|
+
}) => {
|
|
16
|
+
|
|
17
|
+
const { webCall, callState, isMuted } = useAIVoice();
|
|
18
|
+
const { getColor, customStyles } = useOrbTheme();
|
|
19
|
+
const mountRef = useRef<HTMLDivElement>(null);
|
|
20
|
+
|
|
21
|
+
// if (!webCall) {
|
|
22
|
+
// throw new Error("webCall is not found");
|
|
23
|
+
// }
|
|
24
|
+
// ======== Sensitivity factor ========
|
|
25
|
+
const SENSITIVITY = props.animationSensitivity ?? 1.5; // Adjust this to increase or decrease overall responsiveness
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const scene = new THREE.Scene();
|
|
29
|
+
// Calculate aspect ratio
|
|
30
|
+
const aspect = props.width / props.height;
|
|
31
|
+
|
|
32
|
+
// Define the size of the visible area
|
|
33
|
+
const innerRadius = 2; // Circle radius
|
|
34
|
+
const frustumHeight = innerRadius * 2; // Diameter of the circle
|
|
35
|
+
const frustumWidth = frustumHeight * aspect;
|
|
36
|
+
|
|
37
|
+
// Create an OrthographicCamera
|
|
38
|
+
const camera = new THREE.OrthographicCamera(
|
|
39
|
+
-frustumWidth / 2,
|
|
40
|
+
frustumWidth / 2,
|
|
41
|
+
frustumHeight / 2,
|
|
42
|
+
-frustumHeight / 2,
|
|
43
|
+
-1000,
|
|
44
|
+
1000
|
|
45
|
+
);
|
|
46
|
+
camera.position.z = 0;
|
|
47
|
+
|
|
48
|
+
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
|
49
|
+
renderer.setSize(props.width, props.height);
|
|
50
|
+
renderer.setPixelRatio(window.devicePixelRatio);
|
|
51
|
+
mountRef.current?.appendChild(renderer.domElement);
|
|
52
|
+
|
|
53
|
+
// Audio context and analyser
|
|
54
|
+
const audioContext = new (window.AudioContext ||
|
|
55
|
+
(window as any).webkitAudioContext)();
|
|
56
|
+
const analyser = audioContext.createAnalyser();
|
|
57
|
+
analyser.fftSize = 256;
|
|
58
|
+
const dataArray = new Uint8Array(analyser.frequencyBinCount);
|
|
59
|
+
|
|
60
|
+
const outerSegments = 1024;
|
|
61
|
+
|
|
62
|
+
// Circle geometry
|
|
63
|
+
const innerGeometry = new THREE.CircleGeometry(innerRadius, outerSegments);
|
|
64
|
+
|
|
65
|
+
const innerMaterial = new THREE.ShaderMaterial({
|
|
66
|
+
vertexShader: `
|
|
67
|
+
uniform float time;
|
|
68
|
+
varying vec2 vUv;
|
|
69
|
+
varying vec3 vPosition;
|
|
70
|
+
// Now we'll receive an array of 4 colors
|
|
71
|
+
uniform vec3 uColor[4];
|
|
72
|
+
varying vec3 vColor;
|
|
73
|
+
|
|
74
|
+
uniform float uFreqData;
|
|
75
|
+
uniform float freqInfluencedTime;
|
|
76
|
+
|
|
77
|
+
// Simplex noise utility functions
|
|
78
|
+
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
|
|
79
|
+
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
|
|
80
|
+
|
|
81
|
+
float snoise(vec3 v){
|
|
82
|
+
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
|
|
83
|
+
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
|
84
|
+
|
|
85
|
+
vec3 i = floor(v + dot(v, C.yyy) );
|
|
86
|
+
vec3 x0 = v - i + dot(i, C.xxx);
|
|
87
|
+
|
|
88
|
+
vec3 g = step(x0.yzx, x0.xyz);
|
|
89
|
+
vec3 l = 1.0 - g;
|
|
90
|
+
vec3 i1 = min( g.xyz, l.zxy );
|
|
91
|
+
vec3 i2 = max( g.xyz, l.zxy );
|
|
92
|
+
|
|
93
|
+
vec3 x1 = x0 - i1 + 1.0 * C.xxx;
|
|
94
|
+
vec3 x2 = x0 - i2 + 2.0 * C.xxx;
|
|
95
|
+
vec3 x3 = x0 - 1.0 + 3.0 * C.xxx;
|
|
96
|
+
|
|
97
|
+
i = mod(i, 289.0 );
|
|
98
|
+
vec4 p = permute( permute( permute(
|
|
99
|
+
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
|
|
100
|
+
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
|
|
101
|
+
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
|
|
102
|
+
|
|
103
|
+
float n_ = 1.0/7.0;
|
|
104
|
+
vec3 ns = n_ * D.wyz - D.xzx;
|
|
105
|
+
|
|
106
|
+
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
|
|
107
|
+
|
|
108
|
+
vec4 x_ = floor(j * ns.z);
|
|
109
|
+
vec4 y_ = floor(j - 7.0 * x_ );
|
|
110
|
+
vec4 x = x_ *ns.x + ns.yyyy;
|
|
111
|
+
vec4 y = y_ *ns.x + ns.yyyy;
|
|
112
|
+
vec4 h = 1.0 - abs(x) - abs(y);
|
|
113
|
+
|
|
114
|
+
vec4 b0 = vec4( x.xy, y.xy );
|
|
115
|
+
vec4 b1 = vec4( x.zw, y.zw );
|
|
116
|
+
|
|
117
|
+
vec4 s0 = floor(b0)*2.0 + 1.0;
|
|
118
|
+
vec4 s1 = floor(b1)*2.0 + 1.0;
|
|
119
|
+
vec4 sh = -step(h, vec4(0.0));
|
|
120
|
+
|
|
121
|
+
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
|
|
122
|
+
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
|
|
123
|
+
|
|
124
|
+
vec3 p0 = vec3(a0.xy,h.x);
|
|
125
|
+
vec3 p1 = vec3(a0.zw,h.y);
|
|
126
|
+
vec3 p2 = vec3(a1.xy,h.z);
|
|
127
|
+
vec3 p3 = vec3(a1.zw,h.w);
|
|
128
|
+
|
|
129
|
+
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
|
|
130
|
+
p0 *= norm.x;
|
|
131
|
+
p1 *= norm.y;
|
|
132
|
+
p2 *= norm.z;
|
|
133
|
+
p3 *= norm.w;
|
|
134
|
+
|
|
135
|
+
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
|
|
136
|
+
m = m * m;
|
|
137
|
+
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
|
|
138
|
+
dot(p2,x2), dot(p3,x3) ) );
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void main() {
|
|
142
|
+
float frequencyInfluence = uFreqData * 0.005;
|
|
143
|
+
float modified_time = freqInfluencedTime;
|
|
144
|
+
|
|
145
|
+
vUv = uv;
|
|
146
|
+
|
|
147
|
+
vec2 noiseCoord = uv * vec2(3.0, 4.0);
|
|
148
|
+
float noise = snoise(vec3(noiseCoord, modified_time * 0.3));
|
|
149
|
+
|
|
150
|
+
float distToCenter = length(uv - 0.5) * 2.0;
|
|
151
|
+
float influence = smoothstep(0.9, 0.8, distToCenter);
|
|
152
|
+
|
|
153
|
+
float displacement = influence * sin(vUv.x * 5.0 + freqInfluencedTime * 1.0) * 0.05 * uFreqData / 30.0;
|
|
154
|
+
displacement += influence * cos(vUv.y * 5.0 + freqInfluencedTime * 1.5) * 0.05 * uFreqData / 30.0;
|
|
155
|
+
|
|
156
|
+
vec3 pos = vec3(position.x, position.y + displacement, position.z - abs(displacement));
|
|
157
|
+
|
|
158
|
+
// Start from a neutral color instead of one of the array colors
|
|
159
|
+
vColor = vec3(0.0);
|
|
160
|
+
|
|
161
|
+
// Incorporate all 4 colors
|
|
162
|
+
for (int i = 0; i < 4; i++) {
|
|
163
|
+
float noiseFlow = 5.0 + float(i) * 0.3;
|
|
164
|
+
float noiseSpeed = 10.0 + float(i) * 0.3;
|
|
165
|
+
float noiseSeed = 1.0 + float(i) * 10.0;
|
|
166
|
+
vec2 noiseFreq = vec2(0.3, 0.4);
|
|
167
|
+
float noiseFloor = 0.1;
|
|
168
|
+
float noiseCeil = 0.6 + float(i) * 0.07;
|
|
169
|
+
|
|
170
|
+
float localNoise = smoothstep(
|
|
171
|
+
noiseFloor,
|
|
172
|
+
noiseCeil,
|
|
173
|
+
snoise(vec3(
|
|
174
|
+
noiseCoord * noiseFreq + vec2(modified_time * noiseFlow, modified_time * noiseSpeed),
|
|
175
|
+
noiseSeed
|
|
176
|
+
))
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Mix each color in
|
|
180
|
+
vColor = mix(vColor, uColor[i], localNoise);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
|
|
184
|
+
}
|
|
185
|
+
`,
|
|
186
|
+
fragmentShader: `
|
|
187
|
+
uniform float time;
|
|
188
|
+
varying vec2 vUv;
|
|
189
|
+
varying vec3 vPosition;
|
|
190
|
+
varying vec3 vColor;
|
|
191
|
+
uniform float freqInfluencedTime;
|
|
192
|
+
|
|
193
|
+
void main() {
|
|
194
|
+
gl_FragColor = vec4(vColor, 1.0);
|
|
195
|
+
}
|
|
196
|
+
`,
|
|
197
|
+
side: THREE.DoubleSide,
|
|
198
|
+
uniforms: {
|
|
199
|
+
time: { value: 0.0 },
|
|
200
|
+
uFreqData: { value: 0.0 },
|
|
201
|
+
// Change to 4 colors in the shader
|
|
202
|
+
uColor: {
|
|
203
|
+
value: [
|
|
204
|
+
new THREE.Color().set(getColor("primary-500")),
|
|
205
|
+
new THREE.Color().set(getColor("primary-800")),
|
|
206
|
+
new THREE.Color().set(getColor("primary-200")),
|
|
207
|
+
new THREE.Color().set(getColor("primary-600")),
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
resolution: { value: new THREE.Vector4() },
|
|
211
|
+
freqInfluencedTime: { value: 0.0 },
|
|
212
|
+
},
|
|
213
|
+
transparent: true,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const innerCircle = new THREE.Mesh(innerGeometry, innerMaterial);
|
|
217
|
+
scene.add(innerCircle);
|
|
218
|
+
|
|
219
|
+
// Green spot
|
|
220
|
+
const smallerInnerGeometry = new THREE.CircleGeometry(
|
|
221
|
+
innerRadius,
|
|
222
|
+
outerSegments
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const greenMaterial = new THREE.ShaderMaterial({
|
|
226
|
+
vertexShader: `
|
|
227
|
+
varying vec2 vUv;
|
|
228
|
+
uniform float uTime;
|
|
229
|
+
uniform float uFreqData;
|
|
230
|
+
void main() {
|
|
231
|
+
vUv = uv;
|
|
232
|
+
vec3 pos = position;
|
|
233
|
+
|
|
234
|
+
float distToCenter = length(vUv - 0.5);
|
|
235
|
+
float influence = smoothstep(0.5, 0.45, distToCenter);
|
|
236
|
+
|
|
237
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
|
|
238
|
+
}
|
|
239
|
+
`,
|
|
240
|
+
fragmentShader: `
|
|
241
|
+
varying vec2 vUv;
|
|
242
|
+
uniform float uTime;
|
|
243
|
+
uniform float uFreqData;
|
|
244
|
+
uniform float uMargin;
|
|
245
|
+
uniform float uOpacity;
|
|
246
|
+
uniform vec3 uSpotColor;
|
|
247
|
+
|
|
248
|
+
vec4 permute(vec4 x) {
|
|
249
|
+
return mod(((x*34.0)+1.0)*x, 289.0);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
float perlinNoise(vec2 p) {
|
|
253
|
+
vec2 i = floor(p);
|
|
254
|
+
vec2 f = fract(p);
|
|
255
|
+
|
|
256
|
+
vec4 v = vec4(
|
|
257
|
+
dot(i, vec2(127.1, 311.7)),
|
|
258
|
+
dot(i + vec2(1.0, 0.0), vec2(127.1, 311.7)),
|
|
259
|
+
dot(i + vec2(0.0, 1.0), vec2(127.1, 311.7)),
|
|
260
|
+
dot(i + vec2(1.0, 1.0), vec2(127.1, 311.7))
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
v = fract(sin(v) * 43758.5453123);
|
|
264
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
265
|
+
|
|
266
|
+
return mix(
|
|
267
|
+
mix(v.x, v.y, u.x),
|
|
268
|
+
mix(v.z, v.w, u.x),
|
|
269
|
+
u.y
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
void main() {
|
|
274
|
+
vec2 centeredUv = vUv - 0.5;
|
|
275
|
+
float distToCenter = length(centeredUv);
|
|
276
|
+
|
|
277
|
+
float maxRadius = 0.5 - uMargin;
|
|
278
|
+
float dynamicRadius = maxRadius;
|
|
279
|
+
float normalizedFreq = (uFreqData / 256.0);
|
|
280
|
+
|
|
281
|
+
float noiseScale = 3.0;
|
|
282
|
+
float adjustedTime = uTime * 0.1;
|
|
283
|
+
float edgeNoise = perlinNoise(centeredUv * noiseScale + adjustedTime);
|
|
284
|
+
edgeNoise = smoothstep(normalizedFreq, 1.0, edgeNoise);
|
|
285
|
+
|
|
286
|
+
float noisyRadius = dynamicRadius - edgeNoise * 0.2;
|
|
287
|
+
float edge = smoothstep(noisyRadius - 0.03, noisyRadius + 0.03, distToCenter);
|
|
288
|
+
|
|
289
|
+
vec3 spotColor = uSpotColor;
|
|
290
|
+
float alpha = (1.0 - edge) * uOpacity;
|
|
291
|
+
|
|
292
|
+
if (uMargin == 0.0) {
|
|
293
|
+
alpha = uOpacity * (1.0 - smoothstep(0.5 - 0.001, 0.5, distToCenter));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
gl_FragColor = vec4(spotColor, alpha);
|
|
297
|
+
}
|
|
298
|
+
`,
|
|
299
|
+
uniforms: {
|
|
300
|
+
uTime: { value: 0.0 },
|
|
301
|
+
uFreqData: { value: 0.0 },
|
|
302
|
+
uMargin: { value: 0.0001 },
|
|
303
|
+
uOpacity: { value: 0.3 },
|
|
304
|
+
uSpotColor: { value: new THREE.Color().set(getColor("primary-100")) },
|
|
305
|
+
},
|
|
306
|
+
transparent: true,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const InnerCircle = new THREE.Mesh(smallerInnerGeometry, greenMaterial);
|
|
310
|
+
scene.add(InnerCircle);
|
|
311
|
+
InnerCircle.position.z = 0.01;
|
|
312
|
+
|
|
313
|
+
let animationFrameId: number;
|
|
314
|
+
const animate = () => {
|
|
315
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
316
|
+
|
|
317
|
+
analyser.getByteFrequencyData(dataArray);
|
|
318
|
+
// Calculate average frequency and scale by SENSITIVITY
|
|
319
|
+
let freqData =
|
|
320
|
+
dataArray.reduce((acc, value) => acc + value, 0) / dataArray.length;
|
|
321
|
+
freqData *= SENSITIVITY; // Apply sensitivity here
|
|
322
|
+
|
|
323
|
+
// Update uniforms
|
|
324
|
+
innerMaterial.uniforms.time.value += 0.0005;
|
|
325
|
+
innerMaterial.uniforms.freqInfluencedTime.value +=
|
|
326
|
+
0.0001 + freqData * 0.00001;
|
|
327
|
+
innerMaterial.uniforms.uFreqData.value = freqData;
|
|
328
|
+
|
|
329
|
+
greenMaterial.uniforms.uTime.value += 0.05;
|
|
330
|
+
greenMaterial.uniforms.uFreqData.value = freqData;
|
|
331
|
+
|
|
332
|
+
renderer.render(scene, camera);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
let source: MediaStreamAudioSourceNode;
|
|
336
|
+
let stream: MediaStream;
|
|
337
|
+
|
|
338
|
+
navigator.mediaDevices
|
|
339
|
+
.getUserMedia({ audio: true })
|
|
340
|
+
.then((s) => {
|
|
341
|
+
stream = s;
|
|
342
|
+
source = audioContext.createMediaStreamSource(stream);
|
|
343
|
+
source.connect(analyser);
|
|
344
|
+
animate();
|
|
345
|
+
})
|
|
346
|
+
.catch((err) => {
|
|
347
|
+
console.error("Failed microphone access: ", err);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Handle window resize
|
|
351
|
+
const handleResize = () => {
|
|
352
|
+
if (!mountRef.current) return;
|
|
353
|
+
|
|
354
|
+
const aspect = props.width / props.height;
|
|
355
|
+
const frustumHeight = innerRadius * 2;
|
|
356
|
+
const frustumWidth = frustumHeight * aspect;
|
|
357
|
+
|
|
358
|
+
camera.left = -frustumWidth / 2;
|
|
359
|
+
camera.right = frustumWidth / 2;
|
|
360
|
+
camera.top = frustumHeight / 2;
|
|
361
|
+
camera.bottom = -frustumHeight / 2;
|
|
362
|
+
camera.updateProjectionMatrix();
|
|
363
|
+
|
|
364
|
+
renderer.setSize(props.width, props.height);
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
window.addEventListener("resize", handleResize);
|
|
368
|
+
|
|
369
|
+
return () => {
|
|
370
|
+
// Cancel animation loop
|
|
371
|
+
if (animationFrameId) {
|
|
372
|
+
cancelAnimationFrame(animationFrameId);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Remove event listener
|
|
376
|
+
window.removeEventListener("resize", handleResize);
|
|
377
|
+
|
|
378
|
+
// Clean up WebGL
|
|
379
|
+
if (mountRef.current) {
|
|
380
|
+
try {
|
|
381
|
+
mountRef.current.removeChild(renderer.domElement);
|
|
382
|
+
} catch (error) {
|
|
383
|
+
console.error(`error removing dom element`, error);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
renderer.dispose();
|
|
387
|
+
|
|
388
|
+
// Stop audio streams/tracks immediately
|
|
389
|
+
if (stream) {
|
|
390
|
+
console.log(`stopping stream`);
|
|
391
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
392
|
+
} else {
|
|
393
|
+
console.log(`stream not found`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Disconnect audio source if exists
|
|
397
|
+
if (source) {
|
|
398
|
+
console.log(`disconnecting source`);
|
|
399
|
+
source.disconnect();
|
|
400
|
+
} else {
|
|
401
|
+
console.log(`source not found`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Close the audio context
|
|
405
|
+
audioContext
|
|
406
|
+
.close()
|
|
407
|
+
.catch((err) => console.error("Error closing audioContext: ", err));
|
|
408
|
+
};
|
|
409
|
+
}, [customStyles, props.width, props.height, props.showOrb]);
|
|
410
|
+
|
|
411
|
+
return (
|
|
412
|
+
<div
|
|
413
|
+
className="ta-orb-wrapper"
|
|
414
|
+
style={{ position: "relative", width: props.width, height: props.height }}
|
|
415
|
+
>
|
|
416
|
+
<div
|
|
417
|
+
className="ta-orb-container"
|
|
418
|
+
style={{
|
|
419
|
+
position: "absolute",
|
|
420
|
+
top: 0,
|
|
421
|
+
left: 0,
|
|
422
|
+
width: "100%",
|
|
423
|
+
height: "100%",
|
|
424
|
+
}}
|
|
425
|
+
>
|
|
426
|
+
{props.children}
|
|
427
|
+
</div>
|
|
428
|
+
{props.showOrb ? (
|
|
429
|
+
<div
|
|
430
|
+
className="ta-orb-placeholder"
|
|
431
|
+
ref={mountRef}
|
|
432
|
+
style={{ width: props.width, height: props.height }}
|
|
433
|
+
/>
|
|
434
|
+
) : (
|
|
435
|
+
props.orbPlaceholder
|
|
436
|
+
)}
|
|
437
|
+
</div>
|
|
438
|
+
);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
export default AudioAnimationOrb;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as VoiceProvider } from "./providers/AIAudioProvider";
|
|
2
|
+
export { default as OrbThemeProvider } from "./providers/ThemeProvider";
|
|
3
|
+
export { useAIVoice } from "./providers/AIAudioProvider";
|
|
4
|
+
export { default as AudioAnimationOrb } from "./animation-orb";
|
|
5
|
+
export { default as Spinner } from "./spinner";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { createContext, ReactNode, useContext, useState } from "react";
|
|
3
|
+
declare module "react" {
|
|
4
|
+
interface CSSProperties {
|
|
5
|
+
[key: `--${string}`]: string | number;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
import { WebCall } from "@rannah-labs/web-sdk";
|
|
9
|
+
import {
|
|
10
|
+
InitCallOptionsType,
|
|
11
|
+
InitWebRtcCall,
|
|
12
|
+
} from "../../../../app/Types/firebase";
|
|
13
|
+
|
|
14
|
+
const VoiceContext = createContext<VoiceProviderProps | null>(null);
|
|
15
|
+
|
|
16
|
+
export type VoiceProviderProps = {
|
|
17
|
+
webCall: WebCall;
|
|
18
|
+
callState: "idle" | "connecting" | "connected";
|
|
19
|
+
isMuted: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function useAIVoice() {
|
|
23
|
+
const context = useContext<VoiceProviderProps | null>(VoiceContext);
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
"use tixae voice must be used within a tixae voice provider"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return context;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const VoiceProvider = (props: {
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
agentId: string;
|
|
35
|
+
region: "eu" | "na";
|
|
36
|
+
initCallOptions?: InitCallOptionsType;
|
|
37
|
+
}) => {
|
|
38
|
+
|
|
39
|
+
const [tixaeVoice, setTixaeVoice] = React.useState<WebCall>(
|
|
40
|
+
new WebCall()
|
|
41
|
+
);
|
|
42
|
+
const [callState, setCallState] = React.useState<
|
|
43
|
+
"idle" | "connecting" | "connected"
|
|
44
|
+
>("idle");
|
|
45
|
+
const [isMuted, setIsMuted] = React.useState(false);
|
|
46
|
+
|
|
47
|
+
async function initVoice() {
|
|
48
|
+
await tixaeVoice.init({
|
|
49
|
+
agentId: props.agentId,
|
|
50
|
+
region: props.region,
|
|
51
|
+
options: props.initCallOptions,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
setTixaeVoice(tixaeVoice);
|
|
55
|
+
|
|
56
|
+
tixaeVoice.on("call-start", () => {
|
|
57
|
+
console.log(`call has started..`);
|
|
58
|
+
});
|
|
59
|
+
tixaeVoice.on("call-end", () => {
|
|
60
|
+
console.log(`call has ended..`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// must handle errors
|
|
64
|
+
tixaeVoice.on("error", (error) => {
|
|
65
|
+
console.error(`error`, error);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
tixaeVoice.on("conversation-update", (payload) => {
|
|
69
|
+
console.log(`conversation-update`, payload);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
tixaeVoice.on("state-change", (state) => {
|
|
73
|
+
console.log(`state-change`, state);
|
|
74
|
+
setCallState(state);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
tixaeVoice.on("mute-change", (isMuted) => {
|
|
78
|
+
console.log(`mute-change`, isMuted);
|
|
79
|
+
setIsMuted(isMuted);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
React.useEffect(() => {
|
|
84
|
+
initVoice();
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
const value: VoiceProviderProps = {
|
|
88
|
+
webCall: tixaeVoice,
|
|
89
|
+
callState: callState,
|
|
90
|
+
isMuted: isMuted,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<VoiceContext.Provider value={value}>
|
|
95
|
+
{props.children}
|
|
96
|
+
</VoiceContext.Provider>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default VoiceProvider;
|