@bytexbyte/nxtlinq-ai-agent-ui-react-native-development 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/NxtlinqAgentAssistant.d.ts +29 -0
- package/dist/NxtlinqAgentAssistant.d.ts.map +1 -0
- package/dist/NxtlinqAgentAssistant.js +32 -0
- package/dist/components/AgentAssistantShell.d.ts +7 -0
- package/dist/components/AgentAssistantShell.d.ts.map +1 -0
- package/dist/components/AgentAssistantShell.js +77 -0
- package/dist/components/AgentComposer.d.ts +3 -0
- package/dist/components/AgentComposer.d.ts.map +1 -0
- package/dist/components/AgentComposer.js +56 -0
- package/dist/components/AgentMessageList.d.ts +3 -0
- package/dist/components/AgentMessageList.d.ts.map +1 -0
- package/dist/components/AgentMessageList.js +91 -0
- package/dist/components/AgentRemoteAudio.d.ts +14 -0
- package/dist/components/AgentRemoteAudio.d.ts.map +1 -0
- package/dist/components/AgentRemoteAudio.js +62 -0
- package/dist/components/AgentVoiceBar.d.ts +3 -0
- package/dist/components/AgentVoiceBar.d.ts.map +1 -0
- package/dist/components/AgentVoiceBar.js +133 -0
- package/dist/components/PresetMessageChips.d.ts +3 -0
- package/dist/components/PresetMessageChips.d.ts.map +1 -0
- package/dist/components/PresetMessageChips.js +39 -0
- package/dist/components/VoiceGreetTrigger.d.ts +10 -0
- package/dist/components/VoiceGreetTrigger.d.ts.map +1 -0
- package/dist/components/VoiceGreetTrigger.js +99 -0
- package/dist/components/VoiceIcons.d.ts +12 -0
- package/dist/components/VoiceIcons.d.ts.map +1 -0
- package/dist/components/VoiceIcons.js +17 -0
- package/dist/components/VoiceImageInput.d.ts +10 -0
- package/dist/components/VoiceImageInput.d.ts.map +1 -0
- package/dist/components/VoiceImageInput.js +100 -0
- package/dist/components/VoiceWaveform.d.ts +7 -0
- package/dist/components/VoiceWaveform.d.ts.map +1 -0
- package/dist/components/VoiceWaveform.js +64 -0
- package/dist/context/AgentAssistantContext.d.ts +45 -0
- package/dist/context/AgentAssistantContext.d.ts.map +1 -0
- package/dist/context/AgentAssistantContext.js +244 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/theme/defaultTheme.d.ts +3 -0
- package/dist/theme/defaultTheme.d.ts.map +1 -0
- package/dist/theme/defaultTheme.js +33 -0
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/voice/AudioSessionWaker.d.ts +18 -0
- package/dist/voice/AudioSessionWaker.d.ts.map +1 -0
- package/dist/voice/AudioSessionWaker.js +49 -0
- package/dist/voice/TextTtsPlayer.d.ts +21 -0
- package/dist/voice/TextTtsPlayer.d.ts.map +1 -0
- package/dist/voice/TextTtsPlayer.js +91 -0
- package/dist/voice/VoiceAutoGreetBinder.d.ts +6 -0
- package/dist/voice/VoiceAutoGreetBinder.d.ts.map +1 -0
- package/dist/voice/VoiceAutoGreetBinder.js +25 -0
- package/dist/voice/useVoiceAutoGreet.d.ts +24 -0
- package/dist/voice/useVoiceAutoGreet.d.ts.map +1 -0
- package/dist/voice/useVoiceAutoGreet.js +64 -0
- package/dist/voice/useVoiceMicState.d.ts +24 -0
- package/dist/voice/useVoiceMicState.d.ts.map +1 -0
- package/dist/voice/useVoiceMicState.js +84 -0
- package/dist/voice/voiceMicConstants.d.ts +5 -0
- package/dist/voice/voiceMicConstants.d.ts.map +1 -0
- package/dist/voice/voiceMicConstants.js +11 -0
- package/dist/voice/voiceWaveformConstants.d.ts +6 -0
- package/dist/voice/voiceWaveformConstants.d.ts.map +1 -0
- package/dist/voice/voiceWaveformConstants.js +7 -0
- package/dist/voice/webrtcAudioGain.d.ts +6 -0
- package/dist/voice/webrtcAudioGain.d.ts.map +1 -0
- package/dist/voice/webrtcAudioGain.js +11 -0
- package/dist/voice/writeTtsCacheFile.d.ts +9 -0
- package/dist/voice/writeTtsCacheFile.d.ts.map +1 -0
- package/dist/voice/writeTtsCacheFile.js +37 -0
- package/package.json +64 -0
- package/src/NxtlinqAgentAssistant.tsx +103 -0
- package/src/components/AgentAssistantShell.tsx +167 -0
- package/src/components/AgentComposer.tsx +117 -0
- package/src/components/AgentMessageList.tsx +187 -0
- package/src/components/AgentRemoteAudio.tsx +105 -0
- package/src/components/AgentVoiceBar.tsx +232 -0
- package/src/components/PresetMessageChips.tsx +64 -0
- package/src/components/VoiceGreetTrigger.tsx +158 -0
- package/src/components/VoiceIcons.tsx +32 -0
- package/src/components/VoiceImageInput.tsx +178 -0
- package/src/components/VoiceWaveform.tsx +84 -0
- package/src/context/AgentAssistantContext.tsx +369 -0
- package/src/index.ts +59 -0
- package/src/react-native.d.ts +42 -0
- package/src/theme/defaultTheme.ts +35 -0
- package/src/types.ts +107 -0
- package/src/voice/AudioSessionWaker.tsx +94 -0
- package/src/voice/TextTtsPlayer.tsx +151 -0
- package/src/voice/VoiceAutoGreetBinder.tsx +38 -0
- package/src/voice/useVoiceAutoGreet.ts +95 -0
- package/src/voice/useVoiceMicState.ts +116 -0
- package/src/voice/voiceMicConstants.ts +14 -0
- package/src/voice/voiceWaveformConstants.ts +10 -0
- package/src/voice/webrtcAudioGain.ts +21 -0
- package/src/voice/writeTtsCacheFile.ts +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaultTheme.d.ts","sourceRoot":"","sources":["../../src/theme/defaultTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEpD,eAAO,MAAM,0BAA0B,EAAE,mBAgCxC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const defaultAgentAssistantTheme = {
|
|
2
|
+
colors: {
|
|
3
|
+
background: '#ffffff',
|
|
4
|
+
surface: '#f8fafc',
|
|
5
|
+
border: '#e2e8f0',
|
|
6
|
+
primary: '#2563eb',
|
|
7
|
+
primaryText: '#ffffff',
|
|
8
|
+
userBubble: '#2563eb',
|
|
9
|
+
userText: '#ffffff',
|
|
10
|
+
assistantBubble: '#f1f5f9',
|
|
11
|
+
assistantText: '#0f172a',
|
|
12
|
+
mutedText: '#64748b',
|
|
13
|
+
error: '#dc2626',
|
|
14
|
+
voiceActive: '#22c55e',
|
|
15
|
+
voiceSpeaking: '#ec4899',
|
|
16
|
+
},
|
|
17
|
+
spacing: {
|
|
18
|
+
xs: 4,
|
|
19
|
+
sm: 8,
|
|
20
|
+
md: 12,
|
|
21
|
+
lg: 16,
|
|
22
|
+
},
|
|
23
|
+
radius: {
|
|
24
|
+
bubble: 16,
|
|
25
|
+
panel: 12,
|
|
26
|
+
button: 8,
|
|
27
|
+
},
|
|
28
|
+
typography: {
|
|
29
|
+
titleSize: 17,
|
|
30
|
+
bodySize: 15,
|
|
31
|
+
captionSize: 12,
|
|
32
|
+
},
|
|
33
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Message, ToolUse as CoreToolUse } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
|
+
import type { NxtlinqAgentProviderProps } from '@bytexbyte/nxtlinq-ai-agent-react-native-development';
|
|
3
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
4
|
+
import type { VoiceAutoGreetConfig } from './voice/useVoiceAutoGreet';
|
|
5
|
+
export type PresetMessage = {
|
|
6
|
+
text: string;
|
|
7
|
+
autoSend?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type AgentAssistantTheme = {
|
|
10
|
+
colors: {
|
|
11
|
+
background: string;
|
|
12
|
+
surface: string;
|
|
13
|
+
border: string;
|
|
14
|
+
primary: string;
|
|
15
|
+
primaryText: string;
|
|
16
|
+
userBubble: string;
|
|
17
|
+
userText: string;
|
|
18
|
+
assistantBubble: string;
|
|
19
|
+
assistantText: string;
|
|
20
|
+
mutedText: string;
|
|
21
|
+
error: string;
|
|
22
|
+
voiceActive: string;
|
|
23
|
+
voiceSpeaking: string;
|
|
24
|
+
};
|
|
25
|
+
spacing: {
|
|
26
|
+
xs: number;
|
|
27
|
+
sm: number;
|
|
28
|
+
md: number;
|
|
29
|
+
lg: number;
|
|
30
|
+
};
|
|
31
|
+
radius: {
|
|
32
|
+
bubble: number;
|
|
33
|
+
panel: number;
|
|
34
|
+
button: number;
|
|
35
|
+
};
|
|
36
|
+
typography: {
|
|
37
|
+
titleSize: number;
|
|
38
|
+
bodySize: number;
|
|
39
|
+
captionSize: number;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export type NxtlinqAgentAssistantProps = Omit<NxtlinqAgentProviderProps, 'children'> & {
|
|
43
|
+
children?: NxtlinqAgentProviderProps['children'];
|
|
44
|
+
/** Panel title in the header. */
|
|
45
|
+
title?: string;
|
|
46
|
+
placeholder?: string;
|
|
47
|
+
presetMessages?: PresetMessage[];
|
|
48
|
+
/** Fetch history on mount when `pseudoId` is set. */
|
|
49
|
+
loadHistoryOnMount?: boolean;
|
|
50
|
+
/** Max messages to prefetch (default 50). */
|
|
51
|
+
historyLast?: number;
|
|
52
|
+
/** Show voice mode controls (requires `webrtcModule`). */
|
|
53
|
+
enableVoice?: boolean;
|
|
54
|
+
/** Start in voice interaction mode. */
|
|
55
|
+
startInVoiceMode?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Mute mic when voice connects (Berify hold-to-talk). Set false for open-mic demos.
|
|
58
|
+
* @default true
|
|
59
|
+
*/
|
|
60
|
+
startWithMicMuted?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Mute mic while assistant is transcribing/speaking (echo guard).
|
|
63
|
+
* Independent of `startWithMicMuted`; defaults true.
|
|
64
|
+
*/
|
|
65
|
+
holdMicDuringAssistant?: boolean;
|
|
66
|
+
/** Show output RMS waveform while in voice mode (P0 / SDK-1). */
|
|
67
|
+
showVoiceWaveform?: boolean;
|
|
68
|
+
/** Show voice image URL input for P0 testing (SDK-2). */
|
|
69
|
+
showVoiceImageInput?: boolean;
|
|
70
|
+
/** Demo product image URL for greet + quick send. */
|
|
71
|
+
voiceDemoProductImageUrl?: string;
|
|
72
|
+
/** Auto-greet on first voice connect when no user history (SDK-3). */
|
|
73
|
+
voiceAutoGreet?: VoiceAutoGreetConfig | boolean;
|
|
74
|
+
/**
|
|
75
|
+
* iOS: bundled silent MP3 (`require('./assets/silent.mp3')`) to wake AVAudioSession.
|
|
76
|
+
* Strongly recommended — without it WebRTC/TTS may be silent on cold start.
|
|
77
|
+
*/
|
|
78
|
+
iosSilentAudioSource?: number | {
|
|
79
|
+
uri: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Voice-mode assistant playback gain (WebRTC `_setVolume`, 0–10). Default 5 to
|
|
83
|
+
* match text TTS loudness (react-native-video volume 1). Default 10.
|
|
84
|
+
*/
|
|
85
|
+
voiceRemoteAudioGain?: number;
|
|
86
|
+
/** Text-mode TTS volume (react-native-video, 0–1). @default 1 */
|
|
87
|
+
textTtsVolume?: number;
|
|
88
|
+
theme?: Partial<AgentAssistantTheme>;
|
|
89
|
+
style?: StyleProp<ViewStyle>;
|
|
90
|
+
headerStyle?: StyleProp<ViewStyle>;
|
|
91
|
+
onMessage?: (message: Message) => void;
|
|
92
|
+
onError?: (error: Error) => void;
|
|
93
|
+
onToolUse?: (toolUse: CoreToolUse, onProgress?: (update: {
|
|
94
|
+
status?: string;
|
|
95
|
+
progress?: number;
|
|
96
|
+
partialResult?: string;
|
|
97
|
+
steps?: string[];
|
|
98
|
+
partialContent?: string;
|
|
99
|
+
}) => void) => Promise<Message | void>;
|
|
100
|
+
};
|
|
101
|
+
export type ToolUse = CoreToolUse;
|
|
102
|
+
export type { Message };
|
|
103
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type VideoSource = number | {
|
|
3
|
+
uri: string;
|
|
4
|
+
};
|
|
5
|
+
export type AudioSessionWakerProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Bundled silent MP3 (e.g. `require('./assets/silent.mp3')`).
|
|
8
|
+
* Required on iOS for WebRTC + TTS until AVAudioSession is activated.
|
|
9
|
+
*/
|
|
10
|
+
silentSource: VideoSource;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* iOS-only: loop silent audio at volume 0 so react-native-video keeps AVAudioSession active.
|
|
14
|
+
* View stays mounted for app lifetime (Berify pattern — never unmount).
|
|
15
|
+
*/
|
|
16
|
+
export declare function AudioSessionWaker({ silentSource }: AudioSessionWakerProps): React.ReactElement | null;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=AudioSessionWaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioSessionWaker.d.ts","sourceRoot":"","sources":["../../src/voice/AudioSessionWaker.tsx"],"names":[],"mappings":"AAIA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,KAAK,WAAW,GAAG,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAuC5C,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,YAAY,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,EAAE,sBAAsB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAoCrG"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { markIOSAudioSessionReady, setIOSAudioSessionPrewarmHandler, } from '@bytexbyte/nxtlinq-ai-agent-react-native-development';
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
function isIOS() {
|
|
5
|
+
try {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
7
|
+
const { Platform } = require('react-native');
|
|
8
|
+
return Platform.OS === 'ios';
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
let VideoComponent = null;
|
|
15
|
+
try {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
17
|
+
VideoComponent = require('react-native-video').default;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
VideoComponent = null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* iOS-only: loop silent audio at volume 0 so react-native-video keeps AVAudioSession active.
|
|
24
|
+
* View stays mounted for app lifetime (Berify pattern — never unmount).
|
|
25
|
+
*/
|
|
26
|
+
export function AudioSessionWaker({ silentSource }) {
|
|
27
|
+
const videoRef = useRef(null);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!isIOS() || !VideoComponent)
|
|
30
|
+
return undefined;
|
|
31
|
+
// Seek-only replay — pausing react-native-video can drop AVAudioSession on repeated reconnects.
|
|
32
|
+
setIOSAudioSessionPrewarmHandler(() => {
|
|
33
|
+
try {
|
|
34
|
+
videoRef.current?.seek(0);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* noop */
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return () => setIOSAudioSessionPrewarmHandler(null);
|
|
41
|
+
}, []);
|
|
42
|
+
if (!isIOS() || !VideoComponent) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return (_jsx(VideoComponent, { ref: videoRef, source: silentSource, paused: false, volume: 0, playInBackground: false, repeat: true, ignoreSilentSwitch: "ignore", style: { width: 0, height: 0, position: 'absolute', opacity: 0 }, onLoad: () => markIOSAudioSessionReady(), onProgress: ({ currentTime }) => {
|
|
46
|
+
if (currentTime > 0)
|
|
47
|
+
markIOSAudioSessionReady();
|
|
48
|
+
} }));
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PostTextTtsResult } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
3
|
+
export declare function isTextTtsPlayerSupported(): boolean;
|
|
4
|
+
export type TextTtsPlayerHandle = {
|
|
5
|
+
play: (result: PostTextTtsResult) => Promise<void>;
|
|
6
|
+
stop: () => void;
|
|
7
|
+
isAvailable: boolean;
|
|
8
|
+
};
|
|
9
|
+
type Props = {
|
|
10
|
+
/** react-native-video volume 0–1. @default 1 */
|
|
11
|
+
volume?: number;
|
|
12
|
+
onPlayingChange?: (playing: boolean) => void;
|
|
13
|
+
onError?: (message: string) => void;
|
|
14
|
+
playerRef: React.MutableRefObject<TextTtsPlayerHandle | null>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Hidden audio player for text-mode TTS (Berify uses react-native-video the same way).
|
|
18
|
+
*/
|
|
19
|
+
export declare function TextTtsPlayer({ volume, onPlayingChange, onError, playerRef, }: Props): React.ReactElement | null;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=TextTtsPlayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextTtsPlayer.d.ts","sourceRoot":"","sources":["../../src/voice/TextTtsPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AA6BtF,wBAAgB,wBAAwB,IAAI,OAAO,CAElD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;CAC/D,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,MAAU,EACV,eAAe,EACf,OAAO,EACP,SAAS,GACV,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAkFnC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
3
|
+
import { StyleSheet } from 'react-native';
|
|
4
|
+
import { isTtsCacheFileSupported, removeTtsCacheFile, writeTtsCacheFile } from './writeTtsCacheFile';
|
|
5
|
+
let VideoComponent = null;
|
|
6
|
+
try {
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
|
+
VideoComponent = require('react-native-video').default;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
VideoComponent = null;
|
|
12
|
+
}
|
|
13
|
+
export function isTextTtsPlayerSupported() {
|
|
14
|
+
return VideoComponent != null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Hidden audio player for text-mode TTS (Berify uses react-native-video the same way).
|
|
18
|
+
*/
|
|
19
|
+
export function TextTtsPlayer({ volume = 1, onPlayingChange, onError, playerRef, }) {
|
|
20
|
+
const videoRef = useRef(null);
|
|
21
|
+
const [source, setSource] = useState();
|
|
22
|
+
const lastFileRef = useRef(null);
|
|
23
|
+
const stop = useCallback(() => {
|
|
24
|
+
const prev = lastFileRef.current;
|
|
25
|
+
lastFileRef.current = null;
|
|
26
|
+
setSource(undefined);
|
|
27
|
+
onPlayingChange?.(false);
|
|
28
|
+
if (prev && isTtsCacheFileSupported()) {
|
|
29
|
+
void removeTtsCacheFile(prev);
|
|
30
|
+
}
|
|
31
|
+
}, [onPlayingChange]);
|
|
32
|
+
const play = useCallback(async (result) => {
|
|
33
|
+
if (!VideoComponent)
|
|
34
|
+
return;
|
|
35
|
+
try {
|
|
36
|
+
const uri = await writeTtsCacheFile(result);
|
|
37
|
+
if (lastFileRef.current && lastFileRef.current !== uri) {
|
|
38
|
+
void removeTtsCacheFile(lastFileRef.current);
|
|
39
|
+
}
|
|
40
|
+
lastFileRef.current = uri;
|
|
41
|
+
setSource({ uri });
|
|
42
|
+
onPlayingChange?.(true);
|
|
43
|
+
requestAnimationFrame(() => {
|
|
44
|
+
try {
|
|
45
|
+
videoRef.current?.seek(0);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
/* noop */
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
54
|
+
onError?.(message);
|
|
55
|
+
stop();
|
|
56
|
+
}
|
|
57
|
+
}, [onPlayingChange, onError, stop]);
|
|
58
|
+
React.useEffect(() => {
|
|
59
|
+
playerRef.current = {
|
|
60
|
+
play,
|
|
61
|
+
stop,
|
|
62
|
+
isAvailable: VideoComponent != null,
|
|
63
|
+
};
|
|
64
|
+
return () => {
|
|
65
|
+
playerRef.current = null;
|
|
66
|
+
stop();
|
|
67
|
+
};
|
|
68
|
+
}, [play, stop, playerRef]);
|
|
69
|
+
if (!VideoComponent)
|
|
70
|
+
return null;
|
|
71
|
+
return (_jsx(VideoComponent, { ref: videoRef, source: source, style: styles.hidden, paused: !source, volume: volume, audioOnly: true, ignoreSilentSwitch: "ignore", playInBackground: false, playWhenInactive: true, onEnd: stop, onError: (e) => {
|
|
72
|
+
const msg = e?.error?.errorString ?? 'TTS playback failed';
|
|
73
|
+
onError?.(msg);
|
|
74
|
+
stop();
|
|
75
|
+
}, bufferConfig: {
|
|
76
|
+
minBufferMs: 800,
|
|
77
|
+
maxBufferMs: 4000,
|
|
78
|
+
bufferForPlaybackMs: 150,
|
|
79
|
+
bufferForPlaybackAfterRebufferMs: 500,
|
|
80
|
+
}, preferredForwardBufferDuration: 0.2 }));
|
|
81
|
+
}
|
|
82
|
+
const styles = StyleSheet.create({
|
|
83
|
+
hidden: {
|
|
84
|
+
width: 1,
|
|
85
|
+
height: 1,
|
|
86
|
+
opacity: 0.01,
|
|
87
|
+
position: 'absolute',
|
|
88
|
+
left: -1000,
|
|
89
|
+
top: -1000,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoiceAutoGreetBinder.d.ts","sourceRoot":"","sources":["../../src/voice/VoiceAutoGreetBinder.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,wBAAgB,oBAAoB,CAAC,EACnC,MAAM,EACN,YAAY,GACb,EAAE;IACD,MAAM,EAAE,oBAAoB,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;CACvB,GAAG,IAAI,CAyBP"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { uriToVoiceImageAttachment } from '@bytexbyte/nxtlinq-ai-agent-react-native-development';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { useAgentAssistant } from '../context/AgentAssistantContext';
|
|
4
|
+
import { useVoiceAutoGreet } from './useVoiceAutoGreet';
|
|
5
|
+
export function VoiceAutoGreetBinder({ config, historyReady, }) {
|
|
6
|
+
const voice = useAgentAssistant();
|
|
7
|
+
const hasUserMessage = useMemo(() => voice.messages.some((m) => m.role === 'user'), [voice.messages]);
|
|
8
|
+
const resolveAttachments = useCallback(async () => {
|
|
9
|
+
const url = config.productImageUrl?.trim();
|
|
10
|
+
if (!url)
|
|
11
|
+
return config.attachments;
|
|
12
|
+
const image = await uriToVoiceImageAttachment(url, 'product.jpg');
|
|
13
|
+
return config.attachments?.length
|
|
14
|
+
? [...config.attachments, image]
|
|
15
|
+
: [image];
|
|
16
|
+
}, [config.attachments, config.productImageUrl]);
|
|
17
|
+
useVoiceAutoGreet(voice, {
|
|
18
|
+
enabled: true,
|
|
19
|
+
config,
|
|
20
|
+
historyReady,
|
|
21
|
+
hasUserMessage,
|
|
22
|
+
resolveAttachments: config.productImageUrl ? resolveAttachments : undefined,
|
|
23
|
+
});
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Attachment } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
|
+
import type { UseNxtlinqVoiceResult } from '@bytexbyte/nxtlinq-ai-agent-react-native-development';
|
|
3
|
+
export type VoiceAutoGreetConfig = {
|
|
4
|
+
/** Greeting user text (e.g. product intro prompt). */
|
|
5
|
+
text?: string;
|
|
6
|
+
productName?: string;
|
|
7
|
+
/** HTTPS or data URI attached on first greet. */
|
|
8
|
+
productImageUrl?: string;
|
|
9
|
+
/** Pre-built attachments (overrides productImageUrl). */
|
|
10
|
+
attachments?: Attachment[];
|
|
11
|
+
skipUserMessage?: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Berify Jeannie auto-greet: after voice connects + history loaded, trigger one
|
|
15
|
+
* synthetic user turn when there is no prior real user message.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useVoiceAutoGreet(voice: Pick<UseNxtlinqVoiceResult, 'isVoiceActive' | 'triggerVoiceGreeting'>, options: {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
config?: VoiceAutoGreetConfig;
|
|
20
|
+
historyReady: boolean;
|
|
21
|
+
hasUserMessage: boolean;
|
|
22
|
+
resolveAttachments?: () => Promise<Attachment[] | undefined>;
|
|
23
|
+
}): void;
|
|
24
|
+
//# sourceMappingURL=useVoiceAutoGreet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVoiceAutoGreet.d.ts","sourceRoot":"","sources":["../../src/voice/useVoiceAutoGreet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE/E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sDAAsD,CAAC;AAElG,MAAM,MAAM,oBAAoB,GAAG;IACjC,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAUF;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,CACT,qBAAqB,EACrB,eAAe,GAAG,sBAAsB,CACzC,EACD,OAAO,EAAE;IACP,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,UAAU,EAAE,GAAG,SAAS,CAAC,CAAC;CAC9D,GACA,IAAI,CAuDN"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
function buildGreetingText(config) {
|
|
3
|
+
if (config.text?.trim())
|
|
4
|
+
return config.text.trim();
|
|
5
|
+
const product = config.productName?.trim();
|
|
6
|
+
return product
|
|
7
|
+
? `Hi! Please give me a brief introduction to ${product}.`
|
|
8
|
+
: 'Hi! Please say hello and introduce yourself.';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Berify Jeannie auto-greet: after voice connects + history loaded, trigger one
|
|
12
|
+
* synthetic user turn when there is no prior real user message.
|
|
13
|
+
*/
|
|
14
|
+
export function useVoiceAutoGreet(voice, options) {
|
|
15
|
+
const sentRef = useRef(false);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!options.enabled || !options.config) {
|
|
18
|
+
sentRef.current = false;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!voice.isVoiceActive || !options.historyReady)
|
|
22
|
+
return;
|
|
23
|
+
if (sentRef.current)
|
|
24
|
+
return;
|
|
25
|
+
if (options.hasUserMessage) {
|
|
26
|
+
sentRef.current = true;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let cancelled = false;
|
|
30
|
+
sentRef.current = true;
|
|
31
|
+
(async () => {
|
|
32
|
+
try {
|
|
33
|
+
const attachments = options.config?.attachments ??
|
|
34
|
+
(await options.resolveAttachments?.());
|
|
35
|
+
if (cancelled)
|
|
36
|
+
return;
|
|
37
|
+
await voice.triggerVoiceGreeting({
|
|
38
|
+
text: buildGreetingText(options.config),
|
|
39
|
+
attachments,
|
|
40
|
+
skipUserMessage: options.config?.skipUserMessage ?? true,
|
|
41
|
+
}, { waitForChannel: true, timeoutMs: 12000 });
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
sentRef.current = false;
|
|
45
|
+
}
|
|
46
|
+
})();
|
|
47
|
+
return () => {
|
|
48
|
+
cancelled = true;
|
|
49
|
+
};
|
|
50
|
+
}, [
|
|
51
|
+
voice.isVoiceActive,
|
|
52
|
+
voice.triggerVoiceGreeting,
|
|
53
|
+
options.enabled,
|
|
54
|
+
options.config,
|
|
55
|
+
options.historyReady,
|
|
56
|
+
options.hasUserMessage,
|
|
57
|
+
options.resolveAttachments,
|
|
58
|
+
]);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!voice.isVoiceActive) {
|
|
61
|
+
sentRef.current = false;
|
|
62
|
+
}
|
|
63
|
+
}, [voice.isVoiceActive]);
|
|
64
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { UseNxtlinqVoiceResult } from '@bytexbyte/nxtlinq-ai-agent-react-native-development';
|
|
2
|
+
/**
|
|
3
|
+
* Mic mute / hold behavior aligned with web SDK {@link useVoiceMode}.
|
|
4
|
+
* - Starts muted on connect (`startWithMicMuted`)
|
|
5
|
+
* - Holds mic during assistant transcribing → speaking
|
|
6
|
+
*/
|
|
7
|
+
export type UseVoiceMicStateOptions = {
|
|
8
|
+
/** When false, mic is open after connect (demo-friendly). Default true (Berify hold-to-talk). */
|
|
9
|
+
startWithMicMuted?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* When false, do not mute the mic (local + server) while the assistant is responding.
|
|
12
|
+
* Use with open-mic demos (`startWithMicMuted={false}`). Berify hold-to-talk keeps this true.
|
|
13
|
+
*/
|
|
14
|
+
holdMicDuringAssistant?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare function useVoiceMicState(voice: UseNxtlinqVoiceResult, isVoiceConnecting: boolean, options?: UseVoiceMicStateOptions): {
|
|
17
|
+
isMicMuted: boolean;
|
|
18
|
+
isMicHeldForAssistant: boolean;
|
|
19
|
+
toggleVoiceMicMute: () => void;
|
|
20
|
+
prepareForVoiceConnect: () => void;
|
|
21
|
+
resetMicState: () => void;
|
|
22
|
+
clearAssistantMicHold: () => void;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=useVoiceMicState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVoiceMicState.d.ts","sourceRoot":"","sources":["../../src/voice/useVoiceMicState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sDAAsD,CAAC;AAIlG;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,OAAO,CAAC,EAAE,uBAAuB;;;;;;;EA4FlC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { ASSISTANT_MIC_HOLD_STATUSES } from './voiceMicConstants';
|
|
3
|
+
export function useVoiceMicState(voice, isVoiceConnecting, options) {
|
|
4
|
+
const connectMuted = options?.startWithMicMuted !== false;
|
|
5
|
+
const holdDuringAssistant = options?.holdMicDuringAssistant !== false;
|
|
6
|
+
const userMicMutedRef = useRef(connectMuted);
|
|
7
|
+
const assistantMicHoldRef = useRef(false);
|
|
8
|
+
const userMicOptInRef = useRef(!connectMuted);
|
|
9
|
+
const [isMicMuted, setIsMicMuted] = useState(connectMuted);
|
|
10
|
+
const applyMicState = useCallback(() => {
|
|
11
|
+
const shouldMute = userMicMutedRef.current || assistantMicHoldRef.current;
|
|
12
|
+
voice.muteMic(shouldMute);
|
|
13
|
+
setIsMicMuted(shouldMute);
|
|
14
|
+
}, [voice]);
|
|
15
|
+
const resetMicState = useCallback(() => {
|
|
16
|
+
userMicMutedRef.current = false;
|
|
17
|
+
assistantMicHoldRef.current = false;
|
|
18
|
+
userMicOptInRef.current = false;
|
|
19
|
+
setIsMicMuted(false);
|
|
20
|
+
}, []);
|
|
21
|
+
const prepareForVoiceConnect = useCallback(() => {
|
|
22
|
+
userMicMutedRef.current = connectMuted;
|
|
23
|
+
userMicOptInRef.current = !connectMuted;
|
|
24
|
+
assistantMicHoldRef.current = false;
|
|
25
|
+
setIsMicMuted(connectMuted);
|
|
26
|
+
voice.muteMic(connectMuted);
|
|
27
|
+
}, [voice, connectMuted]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!isVoiceConnecting)
|
|
30
|
+
return;
|
|
31
|
+
userMicMutedRef.current = connectMuted;
|
|
32
|
+
voice.muteMic(connectMuted);
|
|
33
|
+
setIsMicMuted(connectMuted);
|
|
34
|
+
}, [isVoiceConnecting, voice, connectMuted]);
|
|
35
|
+
const prevVoiceStatusRef = useRef(voice.voiceStatus);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const status = voice.voiceStatus;
|
|
38
|
+
const prev = prevVoiceStatusRef.current;
|
|
39
|
+
prevVoiceStatusRef.current = status;
|
|
40
|
+
if (holdDuringAssistant && ASSISTANT_MIC_HOLD_STATUSES.has(status)) {
|
|
41
|
+
assistantMicHoldRef.current = true;
|
|
42
|
+
applyMicState();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (status === 'listening' || status === 'idle') {
|
|
46
|
+
assistantMicHoldRef.current = false;
|
|
47
|
+
if (connectMuted
|
|
48
|
+
&& status === 'listening'
|
|
49
|
+
&& prev === 'speaking'
|
|
50
|
+
&& !userMicOptInRef.current) {
|
|
51
|
+
userMicMutedRef.current = true;
|
|
52
|
+
}
|
|
53
|
+
applyMicState();
|
|
54
|
+
}
|
|
55
|
+
}, [voice.voiceStatus, applyMicState, holdDuringAssistant]);
|
|
56
|
+
const toggleVoiceMicMute = useCallback(() => {
|
|
57
|
+
if (!voice.isVoiceActive && !isVoiceConnecting)
|
|
58
|
+
return;
|
|
59
|
+
if (assistantMicHoldRef.current && userMicMutedRef.current)
|
|
60
|
+
return;
|
|
61
|
+
if (assistantMicHoldRef.current) {
|
|
62
|
+
userMicMutedRef.current = true;
|
|
63
|
+
applyMicState();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const nextMuted = !userMicMutedRef.current;
|
|
67
|
+
userMicMutedRef.current = nextMuted;
|
|
68
|
+
userMicOptInRef.current = !nextMuted;
|
|
69
|
+
applyMicState();
|
|
70
|
+
}, [voice.isVoiceActive, isVoiceConnecting, applyMicState]);
|
|
71
|
+
const clearAssistantMicHold = useCallback(() => {
|
|
72
|
+
assistantMicHoldRef.current = false;
|
|
73
|
+
applyMicState();
|
|
74
|
+
}, [applyMicState]);
|
|
75
|
+
const isMicHeldForAssistant = ASSISTANT_MIC_HOLD_STATUSES.has(voice.voiceStatus);
|
|
76
|
+
return {
|
|
77
|
+
isMicMuted,
|
|
78
|
+
isMicHeldForAssistant,
|
|
79
|
+
toggleVoiceMicMute,
|
|
80
|
+
prepareForVoiceConnect,
|
|
81
|
+
resetMicState,
|
|
82
|
+
clearAssistantMicHold,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { VoiceStatus } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
|
+
/** Pause mic while assistant pipeline runs (aligned with web {@link useVoiceMode}). */
|
|
3
|
+
export declare const ASSISTANT_MIC_HOLD_STATUSES: ReadonlySet<VoiceStatus>;
|
|
4
|
+
export declare const SPEAKER_ACTIVE_STATUSES: ReadonlySet<VoiceStatus>;
|
|
5
|
+
//# sourceMappingURL=voiceMicConstants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voiceMicConstants.d.ts","sourceRoot":"","sources":["../../src/voice/voiceMicConstants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8CAA8C,CAAC;AAEhF,uFAAuF;AACvF,eAAO,MAAM,2BAA2B,EAAE,WAAW,CAAC,WAAW,CAK/D,CAAC;AAEH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,WAAW,CAG3D,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Pause mic while assistant pipeline runs (aligned with web {@link useVoiceMode}). */
|
|
2
|
+
export const ASSISTANT_MIC_HOLD_STATUSES = new Set([
|
|
3
|
+
'transcribing',
|
|
4
|
+
'thinking',
|
|
5
|
+
'generating',
|
|
6
|
+
'speaking',
|
|
7
|
+
]);
|
|
8
|
+
export const SPEAKER_ACTIVE_STATUSES = new Set([
|
|
9
|
+
'generating',
|
|
10
|
+
'speaking',
|
|
11
|
+
]);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { VoiceStatus } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
|
+
/** Match Berify: waveform animates only while assistant output audio is active. */
|
|
3
|
+
export declare const WAVEFORM_ACTIVE_STATUSES: ReadonlySet<VoiceStatus>;
|
|
4
|
+
export declare const WAVEFORM_AUDIBLE_THRESHOLD = 0.01;
|
|
5
|
+
export declare const WAVEFORM_AUDIBLE_HOLD_MS = 800;
|
|
6
|
+
//# sourceMappingURL=voiceWaveformConstants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voiceWaveformConstants.d.ts","sourceRoot":"","sources":["../../src/voice/voiceWaveformConstants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8CAA8C,CAAC;AAEhF,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,WAAW,CAG5D,CAAC;AAEH,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,wBAAwB,MAAM,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Match Berify: waveform animates only while assistant output audio is active. */
|
|
2
|
+
export const WAVEFORM_ACTIVE_STATUSES = new Set([
|
|
3
|
+
'generating',
|
|
4
|
+
'speaking',
|
|
5
|
+
]);
|
|
6
|
+
export const WAVEFORM_AUDIBLE_THRESHOLD = 0.01;
|
|
7
|
+
export const WAVEFORM_AUDIBLE_HOLD_MS = 800;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type RemoteAudioTrackLike } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
|
+
export declare const DEFAULT_VOICE_REMOTE_AUDIO_GAIN = 10;
|
|
3
|
+
export type WebRTCAudioTrackLike = RemoteAudioTrackLike;
|
|
4
|
+
/** @deprecated Use {@link applyRemoteAudioPlaybackGain} */
|
|
5
|
+
export declare function applyWebRTCAudioTrackGain(tracks: WebRTCAudioTrackLike[], gain: number): void;
|
|
6
|
+
//# sourceMappingURL=webrtcAudioGain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtcAudioGain.d.ts","sourceRoot":"","sources":["../../src/voice/webrtcAudioGain.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,8CAA8C,CAAC;AAEtD,eAAO,MAAM,+BAA+B,KAA4B,CAAC;AACzE,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAExD,2DAA2D;AAC3D,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,oBAAoB,EAAE,EAC9B,IAAI,EAAE,MAAM,GACX,IAAI,CAON"}
|