@shogo-ai/voice 1.4.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/chunk-D2EVSUZX.js +27 -0
  4. package/dist/chunk-Q3TJAJ54.js +165 -0
  5. package/dist/chunk-QHDIEE7O.js +453 -0
  6. package/dist/chunk-S3S2RNO6.js +671 -0
  7. package/dist/chunk-ZGYHDC5G.js +423 -0
  8. package/dist/index.cjs +1316 -0
  9. package/dist/index.d.cts +432 -0
  10. package/dist/index.d.ts +432 -0
  11. package/dist/index.js +735 -0
  12. package/dist/native/index.cjs +1667 -0
  13. package/dist/native/index.d.cts +285 -0
  14. package/dist/native/index.d.ts +285 -0
  15. package/dist/native/index.js +949 -0
  16. package/dist/organicParticlesShaders-DIva0vBO.d.cts +693 -0
  17. package/dist/organicParticlesShaders-DIva0vBO.d.ts +693 -0
  18. package/dist/react/index.cjs +1670 -0
  19. package/dist/react/index.d.cts +423 -0
  20. package/dist/react/index.d.ts +423 -0
  21. package/dist/react/index.js +952 -0
  22. package/dist/route/agent.cjs +981 -0
  23. package/dist/route/agent.d.cts +20 -0
  24. package/dist/route/agent.d.ts +20 -0
  25. package/dist/route/agent.js +17 -0
  26. package/dist/route/audio-tags.cjs +975 -0
  27. package/dist/route/audio-tags.d.cts +16 -0
  28. package/dist/route/audio-tags.d.ts +16 -0
  29. package/dist/route/audio-tags.js +13 -0
  30. package/dist/route/index.cjs +987 -0
  31. package/dist/route/index.d.cts +115 -0
  32. package/dist/route/index.d.ts +115 -0
  33. package/dist/route/index.js +12 -0
  34. package/dist/route/signed-url.cjs +975 -0
  35. package/dist/route/signed-url.d.cts +12 -0
  36. package/dist/route/signed-url.d.ts +12 -0
  37. package/dist/route/signed-url.js +13 -0
  38. package/dist/route/tts-preview.cjs +975 -0
  39. package/dist/route/tts-preview.d.cts +17 -0
  40. package/dist/route/tts-preview.d.ts +17 -0
  41. package/dist/route/tts-preview.js +13 -0
  42. package/dist/server.cjs +999 -0
  43. package/dist/server.d.cts +88 -0
  44. package/dist/server.d.ts +88 -0
  45. package/dist/server.js +11 -0
  46. package/dist/types-BKJtOUbE.d.cts +352 -0
  47. package/dist/types-BKJtOUbE.d.ts +352 -0
  48. package/package.json +148 -0
@@ -0,0 +1,952 @@
1
+ import {
2
+ DEFAULT_ORGANIC_PARTICLES_CONFIG,
3
+ DEFAULT_ORGANIC_SPHERE_CONFIG,
4
+ ORGANIC_PARTICLES_FRAGMENT_SHADER,
5
+ ORGANIC_PARTICLES_VERTEX_SHADER,
6
+ ORGANIC_SPHERE_FRAGMENT_SHADER,
7
+ ORGANIC_SPHERE_VERTEX_SHADER,
8
+ PERLIN_4D,
9
+ appendChatQuery,
10
+ appendTranscriptLine,
11
+ buildSessionPayload,
12
+ createMemoryAddTool,
13
+ createMemoryContextInjector,
14
+ createPostJson,
15
+ createTranscriptDisconnectHandler,
16
+ fetchSignedUrl,
17
+ resolveOrganicParticlesConfig,
18
+ resolveOrganicSphereConfig,
19
+ withProjectQuery
20
+ } from "../chunk-S3S2RNO6.js";
21
+ import "../chunk-Q3TJAJ54.js";
22
+
23
+ // src/react/useVoiceConversation.ts
24
+ import { useCallback, useEffect, useRef, useState } from "react";
25
+ import { useConversation } from "@elevenlabs/react";
26
+ function useVoiceConversation(options) {
27
+ const {
28
+ characterName,
29
+ signedUrlPath = "/api/voice/signed-url",
30
+ memoryAddPath = "/api/memory/add",
31
+ memoryRetrievePath = "/api/memory/retrieve",
32
+ transcriptIngestPath = "/api/memory/ingest",
33
+ autoInjectMemory = true,
34
+ includeMemoryTool = true,
35
+ clientTools = {},
36
+ onTranscript,
37
+ shogoApiKey,
38
+ projectId,
39
+ agentName,
40
+ fetchCredentials = shogoApiKey ? "omit" : "same-origin",
41
+ onError,
42
+ onMessage,
43
+ conversationId: optionConversationId,
44
+ dynamicVariables
45
+ } = options;
46
+ const authHeaders = useCallback(() => {
47
+ return shogoApiKey ? { authorization: `Bearer ${shogoApiKey}` } : {};
48
+ }, [shogoApiKey]);
49
+ const resolvedSignedUrlPath = withProjectQuery(signedUrlPath, { projectId, agentName });
50
+ const transcriptRef = useRef([]);
51
+ const lastInjectedRef = useRef("");
52
+ const weStartedSessionRef = useRef(false);
53
+ const [convaiConversationId, setConvaiConversationId] = useState(null);
54
+ const isRestartingRef = useRef(false);
55
+ const conversationRef = useRef(null);
56
+ const postJson = useCallback(
57
+ createPostJson({ authHeaders, fetchCredentials }),
58
+ [fetchCredentials, authHeaders]
59
+ );
60
+ const injectMemoryContext = useCallback(
61
+ createMemoryContextInjector({
62
+ postJson,
63
+ memoryRetrievePath,
64
+ conversationRef,
65
+ enabled: autoInjectMemory,
66
+ lastInjectedRef
67
+ }),
68
+ [autoInjectMemory, memoryRetrievePath, postJson]
69
+ );
70
+ const handleTranscriptOnDisconnect = useCallback(
71
+ createTranscriptDisconnectHandler({
72
+ isRestartingRef,
73
+ weStartedSessionRef,
74
+ transcriptRef,
75
+ onTranscript,
76
+ transcriptIngestPath,
77
+ postJson
78
+ }),
79
+ [onTranscript, postJson, transcriptIngestPath]
80
+ );
81
+ const builtInTools = includeMemoryTool ? { add_memory: createMemoryAddTool({ postJson, memoryAddPath }) } : {};
82
+ const mergedClientTools = { ...builtInTools, ...clientTools };
83
+ const conversation = useConversation({
84
+ clientTools: mergedClientTools,
85
+ onConnect: (info) => {
86
+ const id = info?.conversationId;
87
+ if (typeof id === "string" && id.length > 0) {
88
+ setConvaiConversationId(id);
89
+ }
90
+ if (!isRestartingRef.current) {
91
+ transcriptRef.current = [];
92
+ lastInjectedRef.current = "";
93
+ }
94
+ },
95
+ onDisconnect: () => {
96
+ handleTranscriptOnDisconnect();
97
+ if (!isRestartingRef.current) {
98
+ setConvaiConversationId(null);
99
+ }
100
+ },
101
+ onError: (e) => {
102
+ onError?.(e);
103
+ },
104
+ onMessage: (m) => {
105
+ const msg = m;
106
+ if (!msg?.message || !msg?.source) return;
107
+ appendTranscriptLine(msg, { transcriptRef, weStartedSessionRef });
108
+ onMessage?.({ source: msg.source, message: msg.message });
109
+ if (autoInjectMemory && msg.source === "user" && msg.message.trim().length >= 3 && weStartedSessionRef.current) {
110
+ void injectMemoryContext(msg.message);
111
+ }
112
+ }
113
+ });
114
+ const { status, isSpeaking, isListening, isMuted, setMuted, startSession, endSession } = conversation;
115
+ conversationRef.current = conversation;
116
+ useEffect(() => {
117
+ const onHide = () => {
118
+ if (!weStartedSessionRef.current || transcriptRef.current.length === 0) return;
119
+ const transcript = transcriptRef.current.join("\n").trim();
120
+ transcriptRef.current = [];
121
+ if (transcript.length < 20) return;
122
+ try {
123
+ const blob = new Blob([JSON.stringify({ transcript })], { type: "application/json" });
124
+ navigator.sendBeacon(transcriptIngestPath, blob);
125
+ } catch {
126
+ }
127
+ };
128
+ window.addEventListener("pagehide", onHide);
129
+ return () => window.removeEventListener("pagehide", onHide);
130
+ }, [transcriptIngestPath]);
131
+ const startInternal = useCallback(
132
+ async (opts) => {
133
+ if (status === "connected" || status === "connecting") {
134
+ try {
135
+ endSession();
136
+ await new Promise((r) => setTimeout(r, 120));
137
+ } catch {
138
+ }
139
+ }
140
+ await navigator.mediaDevices.getUserMedia({ audio: true });
141
+ const data = await fetchSignedUrl({
142
+ path: resolvedSignedUrlPath,
143
+ fetchCredentials,
144
+ authHeaders
145
+ });
146
+ const ctx = data.userContext || "No prior memories yet.";
147
+ weStartedSessionRef.current = true;
148
+ const sessionPayload = buildSessionPayload({
149
+ signedUrl: data.signedUrl,
150
+ characterName,
151
+ userContext: ctx,
152
+ agentPromptOverride: data.agentPromptOverride,
153
+ suppressFirstMessage: opts?.suppressFirstMessage,
154
+ conversationId: optionConversationId,
155
+ dynamicVariables
156
+ });
157
+ await startSession(sessionPayload);
158
+ },
159
+ [
160
+ status,
161
+ endSession,
162
+ resolvedSignedUrlPath,
163
+ fetchCredentials,
164
+ dynamicVariables,
165
+ startSession,
166
+ characterName,
167
+ authHeaders,
168
+ optionConversationId
169
+ ]
170
+ );
171
+ const start = useCallback(
172
+ async (options2) => {
173
+ await startInternal(options2);
174
+ },
175
+ [startInternal]
176
+ );
177
+ const end = useCallback(() => {
178
+ endSession();
179
+ }, [endSession]);
180
+ const restart = useCallback(
181
+ async (options2) => {
182
+ const suppress = options2?.suppressFirstMessage ?? true;
183
+ isRestartingRef.current = true;
184
+ try {
185
+ try {
186
+ endSession();
187
+ } catch {
188
+ }
189
+ await new Promise((r) => setTimeout(r, 120));
190
+ await startInternal({ suppressFirstMessage: suppress });
191
+ } finally {
192
+ isRestartingRef.current = false;
193
+ }
194
+ },
195
+ [endSession, startInternal]
196
+ );
197
+ const sendContextualUpdate = useCallback((text) => {
198
+ conversationRef.current?.sendContextualUpdate(text);
199
+ }, []);
200
+ const sendUserMessage = useCallback((text) => {
201
+ const ref = conversationRef.current;
202
+ if (!ref) return;
203
+ if (typeof ref.sendUserMessage === "function") {
204
+ ref.sendUserMessage(text);
205
+ return;
206
+ }
207
+ ref.sendContextualUpdate(text);
208
+ try {
209
+ ref.sendUserActivity?.();
210
+ } catch {
211
+ }
212
+ }, []);
213
+ const sendUserActivity = useCallback(() => {
214
+ conversationRef.current?.sendUserActivity?.();
215
+ }, []);
216
+ const getOutputByteFrequencyData = useCallback(() => {
217
+ try {
218
+ const fn = conversation.getOutputByteFrequencyData;
219
+ return fn ? fn() : null;
220
+ } catch {
221
+ return null;
222
+ }
223
+ }, [conversation]);
224
+ return {
225
+ start,
226
+ end,
227
+ restart,
228
+ status,
229
+ isSpeaking,
230
+ isListening,
231
+ isMuted,
232
+ setMuted,
233
+ getOutputByteFrequencyData,
234
+ sendContextualUpdate,
235
+ sendUserMessage,
236
+ sendUserActivity,
237
+ conversationId: optionConversationId ?? convaiConversationId,
238
+ convaiConversationId
239
+ };
240
+ }
241
+
242
+ // src/react/useShogoVoice.ts
243
+ function useShogoVoice(opts = {}) {
244
+ return useVoiceConversation({
245
+ characterName: opts.characterName ?? "Shogo",
246
+ signedUrlPath: opts.signedUrlPath ?? "/api/voice/signed-url",
247
+ fetchCredentials: opts.fetchCredentials ?? "same-origin",
248
+ ...opts
249
+ });
250
+ }
251
+
252
+ // src/react/useChatConversation.ts
253
+ import { useCallback as useCallback2, useMemo, useRef as useRef2 } from "react";
254
+ import { useChat } from "@ai-sdk/react";
255
+ import { DefaultChatTransport } from "ai";
256
+ var DEFAULT_API_PATH = "/api/chat/turn";
257
+ var DEFAULT_CHAT_ID = "shogo-chat";
258
+ function syntheticId(prefix) {
259
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
260
+ }
261
+ function useChatConversation(options = {}) {
262
+ const {
263
+ api = DEFAULT_API_PATH,
264
+ shogoApiKey,
265
+ projectId,
266
+ agentName,
267
+ conversationId,
268
+ initialMessages,
269
+ clientTools = {},
270
+ tools,
271
+ fetchCredentials = shogoApiKey ? "omit" : "same-origin",
272
+ id,
273
+ onError,
274
+ dynamicVariables
275
+ } = options;
276
+ const resolvedApi = useMemo(
277
+ () => appendChatQuery(api, { projectId, conversationId, agentName }),
278
+ [api, projectId, conversationId, agentName]
279
+ );
280
+ const headers = useMemo(() => {
281
+ const h = {};
282
+ if (shogoApiKey) h.authorization = `Bearer ${shogoApiKey}`;
283
+ return h;
284
+ }, [shogoApiKey]);
285
+ const body = useMemo(() => {
286
+ const b = {};
287
+ if (projectId) b.projectId = projectId;
288
+ if (agentName) b.agentName = agentName;
289
+ if (conversationId) b.conversationId = conversationId;
290
+ if (tools && tools.length > 0) b.tools = tools;
291
+ if (dynamicVariables && Object.keys(dynamicVariables).length > 0) {
292
+ b.dynamicVariables = dynamicVariables;
293
+ }
294
+ return b;
295
+ }, [projectId, agentName, conversationId, tools, dynamicVariables]);
296
+ const transport = useMemo(
297
+ () => new DefaultChatTransport({
298
+ api: resolvedApi,
299
+ credentials: fetchCredentials,
300
+ headers,
301
+ body
302
+ }),
303
+ [resolvedApi, fetchCredentials, headers, body]
304
+ );
305
+ const toolsRef = useRef2(clientTools);
306
+ toolsRef.current = clientTools;
307
+ const chatRef = useRef2(null);
308
+ const resolvedId = id ?? (conversationId ? `shogo-chat:${conversationId}` : DEFAULT_CHAT_ID);
309
+ const chat = useChat({
310
+ id: resolvedId,
311
+ transport,
312
+ messages: initialMessages,
313
+ async onToolCall({ toolCall }) {
314
+ const name = toolCall.toolName;
315
+ const fn = toolsRef.current[name];
316
+ const current = chatRef.current;
317
+ if (!current) return;
318
+ if (!fn) {
319
+ await current.addToolOutput({
320
+ state: "output-error",
321
+ tool: name,
322
+ toolCallId: toolCall.toolCallId,
323
+ errorText: `Unknown chat tool: ${String(name)}`
324
+ });
325
+ return;
326
+ }
327
+ try {
328
+ const params = typeof toolCall.input === "object" && toolCall.input !== null ? toolCall.input : {};
329
+ const output = await fn(params);
330
+ await current.addToolOutput({
331
+ tool: name,
332
+ toolCallId: toolCall.toolCallId,
333
+ output
334
+ });
335
+ } catch (err) {
336
+ await current.addToolOutput({
337
+ state: "output-error",
338
+ tool: name,
339
+ toolCallId: toolCall.toolCallId,
340
+ errorText: err?.message ?? String(err)
341
+ });
342
+ }
343
+ },
344
+ onError(err) {
345
+ onError?.(err);
346
+ }
347
+ });
348
+ chatRef.current = chat;
349
+ const sendMessage = useCallback2(
350
+ async (text) => {
351
+ if (!text.trim()) return;
352
+ await chatRef.current?.sendMessage({ text });
353
+ },
354
+ []
355
+ );
356
+ const setMessages = useCallback2((messages) => {
357
+ chatRef.current?.setMessages?.(messages);
358
+ }, []);
359
+ const appendAssistantMessage = useCallback2((text) => {
360
+ const next = {
361
+ id: syntheticId("synthetic-assistant"),
362
+ role: "assistant",
363
+ parts: [{ type: "text", text }]
364
+ };
365
+ const current = chatRef.current;
366
+ if (!current) return;
367
+ const nextList = [...current.messages, next];
368
+ current.setMessages?.(nextList);
369
+ }, []);
370
+ const appendUserMessage = useCallback2((text) => {
371
+ const next = {
372
+ id: syntheticId("synthetic-user"),
373
+ role: "user",
374
+ parts: [{ type: "text", text }]
375
+ };
376
+ const current = chatRef.current;
377
+ if (!current) return;
378
+ const nextList = [...current.messages, next];
379
+ current.setMessages?.(nextList);
380
+ }, []);
381
+ const status = chat.status;
382
+ return {
383
+ messages: chat.messages,
384
+ sendMessage,
385
+ setMessages,
386
+ appendAssistantMessage,
387
+ appendUserMessage,
388
+ status,
389
+ conversationId: conversationId ?? null
390
+ };
391
+ }
392
+
393
+ // src/react/useShogoChat.ts
394
+ function useShogoChat(opts = {}) {
395
+ return useChatConversation({
396
+ api: opts.api ?? "/api/chat/turn",
397
+ fetchCredentials: opts.fetchCredentials ?? "same-origin",
398
+ ...opts
399
+ });
400
+ }
401
+
402
+ // src/react/ShogoVoiceProvider.tsx
403
+ import { ConversationProvider as ElevenLabsConversationProvider } from "@elevenlabs/react";
404
+ import { jsx } from "react/jsx-runtime";
405
+ function ShogoVoiceProvider(props) {
406
+ return /* @__PURE__ */ jsx(ElevenLabsConversationProvider, { children: props.children });
407
+ }
408
+
409
+ // src/react/OrganicSphere.tsx
410
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef3 } from "react";
411
+ import * as THREE from "three";
412
+ import { jsx as jsx2 } from "react/jsx-runtime";
413
+ function seededUnit(seed, salt) {
414
+ const s = Math.sin((seed + salt * 7919) * 12.9898 + salt * 78.233) * 43758.5453;
415
+ return s - Math.floor(s);
416
+ }
417
+ function OrganicSphere({
418
+ getFrequencyData,
419
+ active = true,
420
+ config: configProp,
421
+ lightAColor,
422
+ lightBColor,
423
+ rimColor,
424
+ rimThreshold,
425
+ rimPower,
426
+ backgroundColor,
427
+ subdivisions,
428
+ maxPixelRatio,
429
+ className,
430
+ style
431
+ }) {
432
+ const containerRef = useRef3(null);
433
+ const resolvedConfig = useMemo2(() => {
434
+ const base = resolveOrganicSphereConfig(configProp);
435
+ const overrides = {};
436
+ if (lightAColor !== void 0) overrides.lightAColor = lightAColor;
437
+ if (lightBColor !== void 0) overrides.lightBColor = lightBColor;
438
+ if (rimColor !== void 0) overrides.rimColor = rimColor;
439
+ if (rimThreshold !== void 0) overrides.rimThreshold = rimThreshold;
440
+ if (rimPower !== void 0) overrides.rimPower = rimPower;
441
+ if (backgroundColor !== void 0) overrides.backgroundColor = backgroundColor;
442
+ if (subdivisions !== void 0) overrides.subdivisions = subdivisions;
443
+ if (maxPixelRatio !== void 0) overrides.maxPixelRatio = maxPixelRatio;
444
+ return { ...base, ...overrides };
445
+ }, [
446
+ configProp,
447
+ lightAColor,
448
+ lightBColor,
449
+ rimColor,
450
+ rimThreshold,
451
+ rimPower,
452
+ backgroundColor,
453
+ subdivisions,
454
+ maxPixelRatio
455
+ ]);
456
+ const configRef = useRef3(resolvedConfig);
457
+ useEffect2(() => {
458
+ configRef.current = resolvedConfig;
459
+ }, [resolvedConfig]);
460
+ const getFrequencyDataRef = useRef3(getFrequencyData);
461
+ const activeRef = useRef3(active);
462
+ useEffect2(() => {
463
+ getFrequencyDataRef.current = getFrequencyData;
464
+ }, [getFrequencyData]);
465
+ useEffect2(() => {
466
+ activeRef.current = active;
467
+ }, [active]);
468
+ const initSubdivisions = resolvedConfig.subdivisions;
469
+ const initMaxPixelRatio = resolvedConfig.maxPixelRatio;
470
+ const initBackgroundColor = resolvedConfig.backgroundColor;
471
+ useEffect2(() => {
472
+ const container = containerRef.current;
473
+ if (!container) return;
474
+ const cfg0 = configRef.current;
475
+ const width = container.clientWidth || 1;
476
+ const height = container.clientHeight || 1;
477
+ const pixelRatio = Math.min(
478
+ initMaxPixelRatio ?? (typeof window !== "undefined" ? window.devicePixelRatio : 1),
479
+ 2
480
+ );
481
+ const scene = new THREE.Scene();
482
+ if (initBackgroundColor) scene.background = new THREE.Color(initBackgroundColor);
483
+ const camera = new THREE.PerspectiveCamera(cfg0.fov, width / height, 0.1, 100);
484
+ camera.position.set(0, 0, cfg0.cameraZ);
485
+ scene.add(camera);
486
+ const renderer = new THREE.WebGLRenderer({
487
+ antialias: true,
488
+ alpha: initBackgroundColor === null,
489
+ powerPreference: "high-performance"
490
+ });
491
+ renderer.setSize(width, height, false);
492
+ renderer.setPixelRatio(pixelRatio);
493
+ renderer.setClearColor(0, initBackgroundColor === null ? 0 : 1);
494
+ container.appendChild(renderer.domElement);
495
+ renderer.domElement.style.display = "block";
496
+ renderer.domElement.style.width = "100%";
497
+ renderer.domElement.style.height = "100%";
498
+ const geometry = new THREE.SphereGeometry(1, initSubdivisions, initSubdivisions);
499
+ geometry.computeTangents();
500
+ const lightAPos = new THREE.Vector3().setFromSpherical(
501
+ new THREE.Spherical(1, 0.615, 2.049)
502
+ );
503
+ const lightBPos = new THREE.Vector3().setFromSpherical(
504
+ new THREE.Spherical(1, 2.561, -1.844)
505
+ );
506
+ const lightAColorObj = new THREE.Color(cfg0.lightAColor);
507
+ const lightBColorObj = new THREE.Color(cfg0.lightBColor);
508
+ const rimColorObj = new THREE.Color(cfg0.rimColor ?? cfg0.lightBColor);
509
+ const material = new THREE.ShaderMaterial({
510
+ uniforms: {
511
+ uLightAColor: { value: lightAColorObj },
512
+ uLightAPosition: { value: lightAPos },
513
+ uLightAIntensity: { value: cfg0.lightAIntensity },
514
+ uLightBColor: { value: lightBColorObj },
515
+ uLightBPosition: { value: lightBPos },
516
+ uLightBIntensity: { value: cfg0.lightBIntensity },
517
+ uSubdivision: {
518
+ value: new THREE.Vector2(initSubdivisions, initSubdivisions)
519
+ },
520
+ uOffset: { value: new THREE.Vector3() },
521
+ uDistortionFrequency: { value: cfg0.distortionFrequency },
522
+ uDistortionStrength: { value: cfg0.high.idle },
523
+ uDisplacementFrequency: { value: cfg0.displacementFrequency },
524
+ uDisplacementStrength: { value: cfg0.volume.idle },
525
+ uFresnelOffset: { value: cfg0.fresnelOffset },
526
+ uFresnelMultiplier: { value: cfg0.medium.idle },
527
+ uFresnelPower: { value: cfg0.fresnelPower },
528
+ uRimColor: { value: rimColorObj },
529
+ uRimThreshold: { value: cfg0.rimThreshold },
530
+ uRimPower: { value: cfg0.rimPower },
531
+ uTime: { value: 0 }
532
+ },
533
+ defines: { USE_TANGENT: "" },
534
+ vertexShader: ORGANIC_SPHERE_VERTEX_SHADER,
535
+ fragmentShader: ORGANIC_SPHERE_FRAGMENT_SHADER
536
+ });
537
+ const mesh = new THREE.Mesh(geometry, material);
538
+ scene.add(mesh);
539
+ const seedInitialPhase = (seed) => ({
540
+ phi: seededUnit(seed, 1) * Math.PI,
541
+ theta: seededUnit(seed, 2) * Math.PI * 2
542
+ });
543
+ const initialPhase = seedInitialPhase(cfg0.seed);
544
+ const offsetSpherical = new THREE.Spherical(1, initialPhase.phi, initialPhase.theta);
545
+ const offsetDirection = new THREE.Vector3().setFromSpherical(offsetSpherical);
546
+ const variations = {
547
+ volume: { target: 0, current: cfg0.volume.idle },
548
+ medium: { target: 0, current: cfg0.medium.idle },
549
+ high: { target: 0, current: cfg0.high.idle },
550
+ low: { target: 0, current: cfg0.low.idle }
551
+ };
552
+ const resetAnimationState = (cfg) => {
553
+ material.uniforms.uTime.value = 0;
554
+ material.uniforms.uOffset.value.set(0, 0, 0);
555
+ const phase = seedInitialPhase(cfg.seed);
556
+ offsetSpherical.radius = 1;
557
+ offsetSpherical.phi = phase.phi;
558
+ offsetSpherical.theta = phase.theta;
559
+ variations.volume.current = cfg.volume.idle;
560
+ variations.medium.current = cfg.medium.idle;
561
+ variations.high.current = cfg.high.idle;
562
+ variations.low.current = cfg.low.idle;
563
+ variations.volume.target = cfg.volume.idle;
564
+ variations.medium.target = cfg.medium.idle;
565
+ variations.high.target = cfg.high.idle;
566
+ variations.low.target = cfg.low.idle;
567
+ };
568
+ const LEVEL_COUNT = 8;
569
+ function computeLevels(buf) {
570
+ const bins = Math.floor(buf.length / LEVEL_COUNT);
571
+ const levels = new Array(LEVEL_COUNT).fill(0);
572
+ if (bins <= 0) return levels;
573
+ for (let i = 0; i < LEVEL_COUNT; i++) {
574
+ let sum = 0;
575
+ for (let j = 0; j < bins; j++) sum += buf[i * bins + j];
576
+ levels[i] = sum / bins / 256;
577
+ }
578
+ return levels;
579
+ }
580
+ function bandTarget(reactivity, levels, sourceLevel) {
581
+ if (!levels) return reactivity.idle;
582
+ return Math.max(reactivity.floor, reactivity.floor + sourceLevel * reactivity.gain);
583
+ }
584
+ let rafId = 0;
585
+ let lastTs = performance.now();
586
+ let disposed = false;
587
+ let wasActive = activeRef.current;
588
+ let lastSeed = cfg0.seed;
589
+ const render = () => {
590
+ if (disposed) return;
591
+ const now = performance.now();
592
+ const delta = Math.min(now - lastTs, 100);
593
+ lastTs = now;
594
+ const cfg = configRef.current;
595
+ lightAColorObj.set(cfg.lightAColor);
596
+ lightBColorObj.set(cfg.lightBColor);
597
+ rimColorObj.set(cfg.rimColor ?? cfg.lightBColor);
598
+ material.uniforms.uLightAIntensity.value = cfg.lightAIntensity;
599
+ material.uniforms.uLightBIntensity.value = cfg.lightBIntensity;
600
+ material.uniforms.uDistortionFrequency.value = cfg.distortionFrequency;
601
+ material.uniforms.uDisplacementFrequency.value = cfg.displacementFrequency;
602
+ material.uniforms.uFresnelOffset.value = cfg.fresnelOffset;
603
+ material.uniforms.uFresnelPower.value = cfg.fresnelPower;
604
+ material.uniforms.uRimThreshold.value = cfg.rimThreshold;
605
+ material.uniforms.uRimPower.value = cfg.rimPower;
606
+ if (camera.position.z !== cfg.cameraZ) camera.position.z = cfg.cameraZ;
607
+ if (camera.fov !== cfg.fov) {
608
+ camera.fov = cfg.fov;
609
+ camera.updateProjectionMatrix();
610
+ }
611
+ const isActive = activeRef.current;
612
+ if (isActive && !wasActive || cfg.seed !== lastSeed) {
613
+ resetAnimationState(cfg);
614
+ lastSeed = cfg.seed;
615
+ }
616
+ wasActive = isActive;
617
+ if (isActive) {
618
+ let levels = null;
619
+ if (getFrequencyDataRef.current) {
620
+ try {
621
+ const buf = getFrequencyDataRef.current();
622
+ if (buf && buf.length > 0) levels = computeLevels(buf);
623
+ } catch {
624
+ levels = null;
625
+ }
626
+ }
627
+ const l0 = levels ? levels[0] || 0 : 0;
628
+ const l1 = levels ? levels[1] || 0 : 0;
629
+ const l2 = levels ? levels[2] || 0 : 0;
630
+ const loudest = Math.max(l0, l1, l2);
631
+ const update = (variation, reactivity, sourceLevel) => {
632
+ variation.target = bandTarget(reactivity, levels, sourceLevel);
633
+ const easing = variation.target > variation.current ? reactivity.attack : reactivity.decay;
634
+ variation.current += (variation.target - variation.current) * easing * delta;
635
+ };
636
+ update(variations.volume, cfg.volume, loudest);
637
+ update(variations.medium, cfg.medium, l1);
638
+ update(variations.high, cfg.high, l2);
639
+ update(variations.low, cfg.low, l0);
640
+ const timeFrequency = variations.low.current;
641
+ const elapsed = delta * timeFrequency;
642
+ material.uniforms.uDisplacementStrength.value = variations.volume.current;
643
+ material.uniforms.uDistortionStrength.value = variations.high.current;
644
+ material.uniforms.uFresnelMultiplier.value = variations.medium.current;
645
+ const offsetTime = (material.uniforms.uTime.value + elapsed) * 0.3;
646
+ offsetSpherical.phi = (Math.sin(offsetTime * 1e-3) * Math.sin(offsetTime * 321e-5) * 0.5 + 0.5) * Math.PI;
647
+ offsetSpherical.theta = (Math.sin(offsetTime * 1e-4) * Math.sin(offsetTime * 321e-6) * 0.5 + 0.5) * Math.PI * 2;
648
+ offsetDirection.setFromSpherical(offsetSpherical);
649
+ offsetDirection.multiplyScalar(timeFrequency * 2);
650
+ material.uniforms.uOffset.value.add(offsetDirection);
651
+ material.uniforms.uTime.value += elapsed;
652
+ }
653
+ renderer.render(scene, camera);
654
+ rafId = requestAnimationFrame(render);
655
+ };
656
+ rafId = requestAnimationFrame(render);
657
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
658
+ const w = container.clientWidth || 1;
659
+ const h = container.clientHeight || 1;
660
+ camera.aspect = w / h;
661
+ camera.updateProjectionMatrix();
662
+ renderer.setSize(w, h, false);
663
+ }) : null;
664
+ resizeObserver?.observe(container);
665
+ return () => {
666
+ disposed = true;
667
+ cancelAnimationFrame(rafId);
668
+ resizeObserver?.disconnect();
669
+ geometry.dispose();
670
+ material.dispose();
671
+ renderer.dispose();
672
+ if (renderer.domElement.parentNode === container) {
673
+ container.removeChild(renderer.domElement);
674
+ }
675
+ };
676
+ }, [initSubdivisions, initMaxPixelRatio, initBackgroundColor]);
677
+ return /* @__PURE__ */ jsx2("div", { ref: containerRef, className, style });
678
+ }
679
+
680
+ // src/react/OrganicParticles.tsx
681
+ import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef4 } from "react";
682
+ import * as THREE2 from "three";
683
+ import { jsx as jsx3 } from "react/jsx-runtime";
684
+ function seededUnit2(seed, salt) {
685
+ const s = Math.sin((seed + salt * 7919) * 12.9898 + salt * 78.233) * 43758.5453;
686
+ return s - Math.floor(s);
687
+ }
688
+ function buildParticleAttributes(count, innerRadius, outerRadius, seed) {
689
+ const positions = new Float32Array(count * 3);
690
+ const seeds = new Float32Array(count);
691
+ for (let i = 0; i < count; i++) {
692
+ const u = seededUnit2(seed, i * 4 + 1);
693
+ const v = seededUnit2(seed, i * 4 + 2);
694
+ const w = seededUnit2(seed, i * 4 + 3);
695
+ const s = seededUnit2(seed, i * 4 + 4);
696
+ const theta = v * Math.PI * 2;
697
+ const phi = Math.acos(2 * w - 1);
698
+ const r = innerRadius + u * Math.max(0, outerRadius - innerRadius);
699
+ const sinPhi = Math.sin(phi);
700
+ positions[i * 3 + 0] = r * sinPhi * Math.cos(theta);
701
+ positions[i * 3 + 1] = r * sinPhi * Math.sin(theta);
702
+ positions[i * 3 + 2] = r * Math.cos(phi);
703
+ seeds[i] = s;
704
+ }
705
+ return { positions, seeds };
706
+ }
707
+ function OrganicParticles({
708
+ getFrequencyData,
709
+ active = true,
710
+ config: configProp,
711
+ className,
712
+ style
713
+ }) {
714
+ const containerRef = useRef4(null);
715
+ const resolvedConfig = useMemo3(
716
+ () => resolveOrganicParticlesConfig(configProp),
717
+ [configProp]
718
+ );
719
+ const configRef = useRef4(resolvedConfig);
720
+ useEffect3(() => {
721
+ configRef.current = resolvedConfig;
722
+ }, [resolvedConfig]);
723
+ const getFrequencyDataRef = useRef4(getFrequencyData);
724
+ const activeRef = useRef4(active);
725
+ useEffect3(() => {
726
+ getFrequencyDataRef.current = getFrequencyData;
727
+ }, [getFrequencyData]);
728
+ useEffect3(() => {
729
+ activeRef.current = active;
730
+ }, [active]);
731
+ const initCount = resolvedConfig.count;
732
+ const initInnerRadius = resolvedConfig.innerRadius;
733
+ const initOuterRadius = resolvedConfig.outerRadius;
734
+ const initSeed = resolvedConfig.seed;
735
+ const initMaxPixelRatio = resolvedConfig.maxPixelRatio;
736
+ const initBackgroundColor = resolvedConfig.backgroundColor;
737
+ useEffect3(() => {
738
+ const container = containerRef.current;
739
+ if (!container) return;
740
+ const cfg0 = configRef.current;
741
+ const width = container.clientWidth || 1;
742
+ const height = container.clientHeight || 1;
743
+ const pixelRatio = Math.min(
744
+ initMaxPixelRatio ?? (typeof window !== "undefined" ? window.devicePixelRatio : 1),
745
+ 2
746
+ );
747
+ const scene = new THREE2.Scene();
748
+ if (initBackgroundColor) scene.background = new THREE2.Color(initBackgroundColor);
749
+ const camera = new THREE2.PerspectiveCamera(cfg0.fov, width / height, 0.1, 100);
750
+ camera.position.set(0, 0, cfg0.cameraZ);
751
+ scene.add(camera);
752
+ const renderer = new THREE2.WebGLRenderer({
753
+ antialias: true,
754
+ alpha: initBackgroundColor === null,
755
+ powerPreference: "high-performance"
756
+ });
757
+ renderer.setSize(width, height, false);
758
+ renderer.setPixelRatio(pixelRatio);
759
+ renderer.setClearColor(0, initBackgroundColor === null ? 0 : 1);
760
+ container.appendChild(renderer.domElement);
761
+ renderer.domElement.style.display = "block";
762
+ renderer.domElement.style.width = "100%";
763
+ renderer.domElement.style.height = "100%";
764
+ const { positions, seeds } = buildParticleAttributes(
765
+ initCount,
766
+ initInnerRadius,
767
+ initOuterRadius,
768
+ initSeed
769
+ );
770
+ const geometry = new THREE2.BufferGeometry();
771
+ geometry.setAttribute("aBasePosition", new THREE2.BufferAttribute(positions, 3));
772
+ geometry.setAttribute("aSeed", new THREE2.BufferAttribute(seeds, 1));
773
+ geometry.setAttribute("position", new THREE2.BufferAttribute(positions, 3));
774
+ const colorCoolObj = new THREE2.Color(cfg0.colorCool);
775
+ const colorHotObj = new THREE2.Color(cfg0.colorHot);
776
+ const material = new THREE2.ShaderMaterial({
777
+ uniforms: {
778
+ uTime: { value: 0 },
779
+ uNoiseFrequency: { value: cfg0.noiseFrequency },
780
+ uNoiseStrength: { value: cfg0.noiseStrength },
781
+ uDisplacement: { value: cfg0.volume.idle },
782
+ uSwirl: { value: cfg0.high.idle },
783
+ uSwirlTimeRate: { value: cfg0.swirlTimeRate },
784
+ uSizeBase: { value: cfg0.sizeBase },
785
+ uSizeAudio: { value: cfg0.medium.idle },
786
+ uPixelRatio: { value: pixelRatio },
787
+ uColorCool: { value: colorCoolObj },
788
+ uColorHot: { value: colorHotObj },
789
+ uOpacity: { value: cfg0.opacity },
790
+ uSoftness: { value: cfg0.softness }
791
+ },
792
+ vertexShader: ORGANIC_PARTICLES_VERTEX_SHADER,
793
+ fragmentShader: ORGANIC_PARTICLES_FRAGMENT_SHADER,
794
+ transparent: true,
795
+ depthWrite: false,
796
+ blending: THREE2.AdditiveBlending
797
+ });
798
+ const points = new THREE2.Points(geometry, material);
799
+ scene.add(points);
800
+ const variations = {
801
+ volume: { target: 0, current: cfg0.volume.idle },
802
+ medium: { target: 0, current: cfg0.medium.idle },
803
+ high: { target: 0, current: cfg0.high.idle },
804
+ low: { target: 0, current: cfg0.low.idle }
805
+ };
806
+ const LEVEL_COUNT = 8;
807
+ function computeLevels(buf) {
808
+ const bins = Math.floor(buf.length / LEVEL_COUNT);
809
+ const levels = new Array(LEVEL_COUNT).fill(0);
810
+ if (bins <= 0) return levels;
811
+ for (let i = 0; i < LEVEL_COUNT; i++) {
812
+ let sum = 0;
813
+ for (let j = 0; j < bins; j++) sum += buf[i * bins + j];
814
+ levels[i] = sum / bins / 256;
815
+ }
816
+ return levels;
817
+ }
818
+ function bandTarget(reactivity, levels, sourceLevel) {
819
+ if (!levels) return reactivity.idle;
820
+ return Math.max(
821
+ reactivity.floor,
822
+ reactivity.floor + sourceLevel * reactivity.gain
823
+ );
824
+ }
825
+ const rotationState = { y: 0 };
826
+ const resetAnimationState = (cfg) => {
827
+ material.uniforms.uTime.value = 0;
828
+ rotationState.y = 0;
829
+ points.rotation.set(0, 0, 0);
830
+ variations.volume.current = cfg.volume.idle;
831
+ variations.medium.current = cfg.medium.idle;
832
+ variations.high.current = cfg.high.idle;
833
+ variations.low.current = cfg.low.idle;
834
+ variations.volume.target = cfg.volume.idle;
835
+ variations.medium.target = cfg.medium.idle;
836
+ variations.high.target = cfg.high.idle;
837
+ variations.low.target = cfg.low.idle;
838
+ };
839
+ let rafId = 0;
840
+ let lastTs = performance.now();
841
+ let disposed = false;
842
+ let wasActive = activeRef.current;
843
+ const render = () => {
844
+ if (disposed) return;
845
+ const now = performance.now();
846
+ const delta = Math.min(now - lastTs, 100);
847
+ lastTs = now;
848
+ const cfg = configRef.current;
849
+ colorCoolObj.set(cfg.colorCool);
850
+ colorHotObj.set(cfg.colorHot);
851
+ material.uniforms.uNoiseFrequency.value = cfg.noiseFrequency;
852
+ material.uniforms.uNoiseStrength.value = cfg.noiseStrength;
853
+ material.uniforms.uSwirlTimeRate.value = cfg.swirlTimeRate;
854
+ material.uniforms.uSizeBase.value = cfg.sizeBase;
855
+ material.uniforms.uOpacity.value = cfg.opacity;
856
+ material.uniforms.uSoftness.value = cfg.softness;
857
+ if (camera.position.z !== cfg.cameraZ) camera.position.z = cfg.cameraZ;
858
+ if (camera.fov !== cfg.fov) {
859
+ camera.fov = cfg.fov;
860
+ camera.updateProjectionMatrix();
861
+ }
862
+ const isActive = activeRef.current;
863
+ if (isActive && !wasActive) {
864
+ resetAnimationState(cfg);
865
+ }
866
+ wasActive = isActive;
867
+ if (isActive) {
868
+ let levels = null;
869
+ if (getFrequencyDataRef.current) {
870
+ try {
871
+ const buf = getFrequencyDataRef.current();
872
+ if (buf && buf.length > 0) levels = computeLevels(buf);
873
+ } catch {
874
+ levels = null;
875
+ }
876
+ }
877
+ const l0 = levels ? levels[0] || 0 : 0;
878
+ const l1 = levels ? levels[1] || 0 : 0;
879
+ const l2 = levels ? levels[2] || 0 : 0;
880
+ const loudest = Math.max(l0, l1, l2);
881
+ const update = (variation, reactivity, sourceLevel) => {
882
+ variation.target = bandTarget(reactivity, levels, sourceLevel);
883
+ const easing = variation.target > variation.current ? reactivity.attack : reactivity.decay;
884
+ variation.current += (variation.target - variation.current) * easing * delta;
885
+ };
886
+ update(variations.volume, cfg.volume, loudest);
887
+ update(variations.medium, cfg.medium, l1);
888
+ update(variations.high, cfg.high, l2);
889
+ update(variations.low, cfg.low, l0);
890
+ const timeFrequency = variations.low.current;
891
+ const elapsed = delta * timeFrequency;
892
+ material.uniforms.uDisplacement.value = variations.volume.current;
893
+ material.uniforms.uSizeAudio.value = variations.medium.current;
894
+ material.uniforms.uSwirl.value = variations.high.current;
895
+ material.uniforms.uTime.value += elapsed;
896
+ rotationState.y += delta * cfg.rotationBias + variations.high.current * delta * 8e-4;
897
+ points.rotation.y = rotationState.y;
898
+ } else {
899
+ material.uniforms.uTime.value += delta * cfg.idleNoiseRate;
900
+ }
901
+ renderer.render(scene, camera);
902
+ rafId = requestAnimationFrame(render);
903
+ };
904
+ rafId = requestAnimationFrame(render);
905
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
906
+ const w = container.clientWidth || 1;
907
+ const h = container.clientHeight || 1;
908
+ camera.aspect = w / h;
909
+ camera.updateProjectionMatrix();
910
+ renderer.setSize(w, h, false);
911
+ }) : null;
912
+ resizeObserver?.observe(container);
913
+ return () => {
914
+ disposed = true;
915
+ cancelAnimationFrame(rafId);
916
+ resizeObserver?.disconnect();
917
+ geometry.dispose();
918
+ material.dispose();
919
+ renderer.dispose();
920
+ if (renderer.domElement.parentNode === container) {
921
+ container.removeChild(renderer.domElement);
922
+ }
923
+ };
924
+ }, [
925
+ initCount,
926
+ initInnerRadius,
927
+ initOuterRadius,
928
+ initSeed,
929
+ initMaxPixelRatio,
930
+ initBackgroundColor
931
+ ]);
932
+ return /* @__PURE__ */ jsx3("div", { ref: containerRef, className, style });
933
+ }
934
+ export {
935
+ DEFAULT_ORGANIC_PARTICLES_CONFIG,
936
+ DEFAULT_ORGANIC_SPHERE_CONFIG,
937
+ ORGANIC_PARTICLES_FRAGMENT_SHADER,
938
+ ORGANIC_PARTICLES_VERTEX_SHADER,
939
+ ORGANIC_SPHERE_FRAGMENT_SHADER,
940
+ ORGANIC_SPHERE_VERTEX_SHADER,
941
+ OrganicParticles,
942
+ OrganicSphere,
943
+ PERLIN_4D,
944
+ ShogoVoiceProvider,
945
+ resolveOrganicParticlesConfig,
946
+ resolveOrganicSphereConfig,
947
+ useChatConversation,
948
+ useShogoChat,
949
+ useShogoVoice,
950
+ useVoiceConversation
951
+ };
952
+ //# sourceMappingURL=index.js.map