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