@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.
Files changed (98) hide show
  1. package/dist/NxtlinqAgentAssistant.d.ts +29 -0
  2. package/dist/NxtlinqAgentAssistant.d.ts.map +1 -0
  3. package/dist/NxtlinqAgentAssistant.js +32 -0
  4. package/dist/components/AgentAssistantShell.d.ts +7 -0
  5. package/dist/components/AgentAssistantShell.d.ts.map +1 -0
  6. package/dist/components/AgentAssistantShell.js +77 -0
  7. package/dist/components/AgentComposer.d.ts +3 -0
  8. package/dist/components/AgentComposer.d.ts.map +1 -0
  9. package/dist/components/AgentComposer.js +56 -0
  10. package/dist/components/AgentMessageList.d.ts +3 -0
  11. package/dist/components/AgentMessageList.d.ts.map +1 -0
  12. package/dist/components/AgentMessageList.js +91 -0
  13. package/dist/components/AgentRemoteAudio.d.ts +14 -0
  14. package/dist/components/AgentRemoteAudio.d.ts.map +1 -0
  15. package/dist/components/AgentRemoteAudio.js +62 -0
  16. package/dist/components/AgentVoiceBar.d.ts +3 -0
  17. package/dist/components/AgentVoiceBar.d.ts.map +1 -0
  18. package/dist/components/AgentVoiceBar.js +133 -0
  19. package/dist/components/PresetMessageChips.d.ts +3 -0
  20. package/dist/components/PresetMessageChips.d.ts.map +1 -0
  21. package/dist/components/PresetMessageChips.js +39 -0
  22. package/dist/components/VoiceGreetTrigger.d.ts +10 -0
  23. package/dist/components/VoiceGreetTrigger.d.ts.map +1 -0
  24. package/dist/components/VoiceGreetTrigger.js +99 -0
  25. package/dist/components/VoiceIcons.d.ts +12 -0
  26. package/dist/components/VoiceIcons.d.ts.map +1 -0
  27. package/dist/components/VoiceIcons.js +17 -0
  28. package/dist/components/VoiceImageInput.d.ts +10 -0
  29. package/dist/components/VoiceImageInput.d.ts.map +1 -0
  30. package/dist/components/VoiceImageInput.js +100 -0
  31. package/dist/components/VoiceWaveform.d.ts +7 -0
  32. package/dist/components/VoiceWaveform.d.ts.map +1 -0
  33. package/dist/components/VoiceWaveform.js +64 -0
  34. package/dist/context/AgentAssistantContext.d.ts +45 -0
  35. package/dist/context/AgentAssistantContext.d.ts.map +1 -0
  36. package/dist/context/AgentAssistantContext.js +244 -0
  37. package/dist/index.d.ts +16 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +14 -0
  40. package/dist/theme/defaultTheme.d.ts +3 -0
  41. package/dist/theme/defaultTheme.d.ts.map +1 -0
  42. package/dist/theme/defaultTheme.js +33 -0
  43. package/dist/types.d.ts +103 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +1 -0
  46. package/dist/voice/AudioSessionWaker.d.ts +18 -0
  47. package/dist/voice/AudioSessionWaker.d.ts.map +1 -0
  48. package/dist/voice/AudioSessionWaker.js +49 -0
  49. package/dist/voice/TextTtsPlayer.d.ts +21 -0
  50. package/dist/voice/TextTtsPlayer.d.ts.map +1 -0
  51. package/dist/voice/TextTtsPlayer.js +91 -0
  52. package/dist/voice/VoiceAutoGreetBinder.d.ts +6 -0
  53. package/dist/voice/VoiceAutoGreetBinder.d.ts.map +1 -0
  54. package/dist/voice/VoiceAutoGreetBinder.js +25 -0
  55. package/dist/voice/useVoiceAutoGreet.d.ts +24 -0
  56. package/dist/voice/useVoiceAutoGreet.d.ts.map +1 -0
  57. package/dist/voice/useVoiceAutoGreet.js +64 -0
  58. package/dist/voice/useVoiceMicState.d.ts +24 -0
  59. package/dist/voice/useVoiceMicState.d.ts.map +1 -0
  60. package/dist/voice/useVoiceMicState.js +84 -0
  61. package/dist/voice/voiceMicConstants.d.ts +5 -0
  62. package/dist/voice/voiceMicConstants.d.ts.map +1 -0
  63. package/dist/voice/voiceMicConstants.js +11 -0
  64. package/dist/voice/voiceWaveformConstants.d.ts +6 -0
  65. package/dist/voice/voiceWaveformConstants.d.ts.map +1 -0
  66. package/dist/voice/voiceWaveformConstants.js +7 -0
  67. package/dist/voice/webrtcAudioGain.d.ts +6 -0
  68. package/dist/voice/webrtcAudioGain.d.ts.map +1 -0
  69. package/dist/voice/webrtcAudioGain.js +11 -0
  70. package/dist/voice/writeTtsCacheFile.d.ts +9 -0
  71. package/dist/voice/writeTtsCacheFile.d.ts.map +1 -0
  72. package/dist/voice/writeTtsCacheFile.js +37 -0
  73. package/package.json +64 -0
  74. package/src/NxtlinqAgentAssistant.tsx +103 -0
  75. package/src/components/AgentAssistantShell.tsx +167 -0
  76. package/src/components/AgentComposer.tsx +117 -0
  77. package/src/components/AgentMessageList.tsx +187 -0
  78. package/src/components/AgentRemoteAudio.tsx +105 -0
  79. package/src/components/AgentVoiceBar.tsx +232 -0
  80. package/src/components/PresetMessageChips.tsx +64 -0
  81. package/src/components/VoiceGreetTrigger.tsx +158 -0
  82. package/src/components/VoiceIcons.tsx +32 -0
  83. package/src/components/VoiceImageInput.tsx +178 -0
  84. package/src/components/VoiceWaveform.tsx +84 -0
  85. package/src/context/AgentAssistantContext.tsx +369 -0
  86. package/src/index.ts +59 -0
  87. package/src/react-native.d.ts +42 -0
  88. package/src/theme/defaultTheme.ts +35 -0
  89. package/src/types.ts +107 -0
  90. package/src/voice/AudioSessionWaker.tsx +94 -0
  91. package/src/voice/TextTtsPlayer.tsx +151 -0
  92. package/src/voice/VoiceAutoGreetBinder.tsx +38 -0
  93. package/src/voice/useVoiceAutoGreet.ts +95 -0
  94. package/src/voice/useVoiceMicState.ts +116 -0
  95. package/src/voice/voiceMicConstants.ts +14 -0
  96. package/src/voice/voiceWaveformConstants.ts +10 -0
  97. package/src/voice/webrtcAudioGain.ts +21 -0
  98. package/src/voice/writeTtsCacheFile.ts +47 -0
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import type { NxtlinqAgentAssistantProps } from './types';
3
+ /**
4
+ * Drop-in React Native assistant UI wired to `@bytexbyte/nxtlinq-ai-agent-react-native-development`.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
9
+ * import { RTCPeerConnection, mediaDevices } from 'react-native-webrtc';
10
+ * import { NxtlinqAgentAssistant } from '@bytexbyte/nxtlinq-ai-agent-ui-react-native-development';
11
+ *
12
+ * export default function Screen() {
13
+ * return (
14
+ * <NxtlinqAgentAssistant
15
+ * storage={AsyncStorage}
16
+ * webrtcModule={{ RTCPeerConnection, mediaDevices }}
17
+ * serviceId="..."
18
+ * apiKey="..."
19
+ * apiSecret="..."
20
+ * pseudoId={chatId}
21
+ * enableVoice
22
+ * loadHistoryOnMount
23
+ * />
24
+ * );
25
+ * }
26
+ * ```
27
+ */
28
+ export declare function NxtlinqAgentAssistant({ title, placeholder, presetMessages, loadHistoryOnMount, historyLast, enableVoice, startInVoiceMode, startWithMicMuted, holdMicDuringAssistant, showVoiceWaveform, showVoiceImageInput, voiceDemoProductImageUrl, voiceAutoGreet, iosSilentAudioSource, voiceRemoteAudioGain, textTtsVolume, theme, style, headerStyle, onMessage, onError, onToolUse, children, storage, fetchImpl, getTimezone, webrtcModule, webrtc, resetOnIdentityChange, ...agentConfig }: NxtlinqAgentAssistantProps): React.ReactElement;
29
+ //# sourceMappingURL=NxtlinqAgentAssistant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NxtlinqAgentAssistant.d.ts","sourceRoot":"","sources":["../src/NxtlinqAgentAssistant.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,WAAW,EACX,cAAc,EACd,kBAA0B,EAC1B,WAAW,EACX,WAAkB,EAClB,gBAAwB,EACxB,iBAAwB,EACxB,sBAA6B,EAC7B,iBAAwB,EACxB,mBAA2B,EAC3B,wBAAwB,EACxB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,KAAK,EACL,KAAK,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,OAAO,EACP,SAAS,EACT,WAAW,EACX,YAAY,EACZ,MAAM,EACN,qBAAqB,EACrB,GAAG,WAAW,EACf,EAAE,0BAA0B,GAAG,KAAK,CAAC,YAAY,CAyCjD"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { NxtlinqAgentProvider } from '@bytexbyte/nxtlinq-ai-agent-react-native-development';
3
+ import { AgentAssistantShell } from './components/AgentAssistantShell';
4
+ /**
5
+ * Drop-in React Native assistant UI wired to `@bytexbyte/nxtlinq-ai-agent-react-native-development`.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
10
+ * import { RTCPeerConnection, mediaDevices } from 'react-native-webrtc';
11
+ * import { NxtlinqAgentAssistant } from '@bytexbyte/nxtlinq-ai-agent-ui-react-native-development';
12
+ *
13
+ * export default function Screen() {
14
+ * return (
15
+ * <NxtlinqAgentAssistant
16
+ * storage={AsyncStorage}
17
+ * webrtcModule={{ RTCPeerConnection, mediaDevices }}
18
+ * serviceId="..."
19
+ * apiKey="..."
20
+ * apiSecret="..."
21
+ * pseudoId={chatId}
22
+ * enableVoice
23
+ * loadHistoryOnMount
24
+ * />
25
+ * );
26
+ * }
27
+ * ```
28
+ */
29
+ export function NxtlinqAgentAssistant({ title, placeholder, presetMessages, loadHistoryOnMount = false, historyLast, enableVoice = true, startInVoiceMode = false, startWithMicMuted = true, holdMicDuringAssistant = true, showVoiceWaveform = true, showVoiceImageInput = false, voiceDemoProductImageUrl, voiceAutoGreet, iosSilentAudioSource, voiceRemoteAudioGain, textTtsVolume, theme, style, headerStyle, onMessage, onError, onToolUse, children, storage, fetchImpl, getTimezone, webrtcModule, webrtc, resetOnIdentityChange, ...agentConfig }) {
30
+ const webrtcEnabled = Boolean(webrtcModule || webrtc);
31
+ return (_jsxs(NxtlinqAgentProvider, { storage: storage, fetchImpl: fetchImpl, getTimezone: getTimezone, webrtcModule: webrtcModule, webrtc: webrtc, resetOnIdentityChange: resetOnIdentityChange, onMessage: onMessage, onError: onError, onToolUse: onToolUse, ...agentConfig, children: [_jsx(AgentAssistantShell, { title: title, placeholder: placeholder, presetMessages: presetMessages, loadHistoryOnMount: loadHistoryOnMount, historyLast: historyLast, enableVoice: enableVoice, startInVoiceMode: startInVoiceMode, startWithMicMuted: startWithMicMuted, holdMicDuringAssistant: holdMicDuringAssistant, showVoiceWaveform: showVoiceWaveform, showVoiceImageInput: showVoiceImageInput, voiceDemoProductImageUrl: voiceDemoProductImageUrl, voiceAutoGreet: voiceAutoGreet, iosSilentAudioSource: iosSilentAudioSource, voiceRemoteAudioGain: voiceRemoteAudioGain, textTtsVolume: textTtsVolume, theme: theme, style: style, headerStyle: headerStyle, webrtcEnabled: webrtcEnabled }), children] }));
32
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { NxtlinqAgentAssistantProps } from '../types';
3
+ export type AgentAssistantShellProps = Pick<NxtlinqAgentAssistantProps, 'title' | 'placeholder' | 'presetMessages' | 'enableVoice' | 'theme' | 'style' | 'headerStyle' | 'loadHistoryOnMount' | 'historyLast' | 'startInVoiceMode' | 'startWithMicMuted' | 'holdMicDuringAssistant' | 'showVoiceWaveform' | 'showVoiceImageInput' | 'voiceDemoProductImageUrl' | 'voiceAutoGreet' | 'iosSilentAudioSource' | 'voiceRemoteAudioGain' | 'textTtsVolume'> & {
4
+ webrtcEnabled: boolean;
5
+ };
6
+ export declare function AgentAssistantShell(props: AgentAssistantShellProps): React.ReactElement;
7
+ //# sourceMappingURL=AgentAssistantShell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentAssistantShell.d.ts","sourceRoot":"","sources":["../../src/components/AgentAssistantShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAMzC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAY3D,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,0BAA0B,EACxB,OAAO,GACP,aAAa,GACb,gBAAgB,GAChB,aAAa,GACb,OAAO,GACP,OAAO,GACP,aAAa,GACb,oBAAoB,GACpB,aAAa,GACb,kBAAkB,GAClB,mBAAmB,GACnB,wBAAwB,GACxB,mBAAmB,GACnB,qBAAqB,GACrB,0BAA0B,GAC1B,gBAAgB,GAChB,sBAAsB,GACtB,sBAAsB,GACtB,eAAe,CAClB,GAAG;IACF,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAiGF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,KAAK,CAAC,YAAY,CAmBvF"}
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useEffect } from 'react';
3
+ import { StyleSheet, Text, View } from 'react-native';
4
+ import { AgentAssistantProvider, useAgentAssistant, } from '../context/AgentAssistantContext';
5
+ import { AgentComposer } from './AgentComposer';
6
+ import { AgentMessageList } from './AgentMessageList';
7
+ import { AgentRemoteAudio } from './AgentRemoteAudio';
8
+ import { AgentVoiceBar } from './AgentVoiceBar';
9
+ import { PresetMessageChips } from './PresetMessageChips';
10
+ import { VoiceGreetTrigger } from './VoiceGreetTrigger';
11
+ import { VoiceImageInput } from './VoiceImageInput';
12
+ import { VoiceWaveform } from './VoiceWaveform';
13
+ import { AudioSessionWaker } from '../voice/AudioSessionWaker';
14
+ import { VoiceAutoGreetBinder } from '../voice/VoiceAutoGreetBinder';
15
+ function AgentAssistantInner({ title, headerStyle, style, loadHistoryOnMount, historyLast, startInVoiceMode, startWithMicMuted, showVoiceWaveform, showVoiceImageInput, voiceDemoProductImageUrl, voiceAutoGreet, iosSilentAudioSource, voiceRemoteAudioGain, }) {
16
+ const { theme, loadHistory, startVoice, isVoiceAvailable, } = useAgentAssistant();
17
+ const [historyReady, setHistoryReady] = React.useState(!loadHistoryOnMount);
18
+ useEffect(() => {
19
+ if (loadHistoryOnMount) {
20
+ void loadHistory({ last: historyLast ?? 50 }).finally(() => setHistoryReady(true));
21
+ }
22
+ }, [loadHistory, loadHistoryOnMount, historyLast]);
23
+ const autoGreetConfig = React.useMemo(() => {
24
+ if (!voiceAutoGreet)
25
+ return null;
26
+ if (voiceAutoGreet === true) {
27
+ return {
28
+ productImageUrl: voiceDemoProductImageUrl,
29
+ skipUserMessage: true,
30
+ };
31
+ }
32
+ return {
33
+ ...voiceAutoGreet,
34
+ productImageUrl: voiceAutoGreet.productImageUrl ?? voiceDemoProductImageUrl,
35
+ skipUserMessage: voiceAutoGreet.skipUserMessage ?? true,
36
+ };
37
+ }, [voiceAutoGreet, voiceDemoProductImageUrl]);
38
+ const voiceAutoStartRef = React.useRef(false);
39
+ useEffect(() => {
40
+ if (!startInVoiceMode || !isVoiceAvailable || voiceAutoStartRef.current) {
41
+ return;
42
+ }
43
+ voiceAutoStartRef.current = true;
44
+ void startVoice();
45
+ }, [startInVoiceMode, isVoiceAvailable, startVoice]);
46
+ return (_jsxs(View, { style: [styles.root, { backgroundColor: theme.colors.background }, style], children: [iosSilentAudioSource ? (_jsx(AudioSessionWaker, { silentSource: iosSilentAudioSource })) : null, _jsx(View, { style: [
47
+ styles.header,
48
+ { borderBottomColor: theme.colors.border, backgroundColor: theme.colors.surface },
49
+ headerStyle,
50
+ ], children: _jsx(Text, { style: {
51
+ fontSize: theme.typography.titleSize,
52
+ fontWeight: '600',
53
+ color: theme.colors.assistantText,
54
+ }, children: title ?? 'AI Assistant' }) }), _jsx(PresetMessageChips, {}), _jsx(AgentMessageList, {}), showVoiceWaveform !== false ? _jsx(VoiceWaveform, {}) : null, _jsx(AgentVoiceBar, {}), showVoiceImageInput && autoGreetConfig ? (_jsx(VoiceGreetTrigger, { config: autoGreetConfig })) : null, showVoiceImageInput ? (_jsx(VoiceImageInput, { demoImageUrl: voiceDemoProductImageUrl })) : null, _jsx(AgentComposer, {}), _jsx(AgentRemoteAudio, { remoteAudioGain: voiceRemoteAudioGain }), autoGreetConfig ? (_jsx(VoiceAutoGreetBinder, { config: autoGreetConfig, historyReady: historyReady })) : null] }));
55
+ }
56
+ export function AgentAssistantShell(props) {
57
+ return (_jsx(AgentAssistantProvider, { ui: {
58
+ title: props.title,
59
+ placeholder: props.placeholder,
60
+ presetMessages: props.presetMessages,
61
+ enableVoice: props.enableVoice,
62
+ theme: props.theme,
63
+ startWithMicMuted: props.startWithMicMuted,
64
+ holdMicDuringAssistant: props.holdMicDuringAssistant,
65
+ textTtsVolume: props.textTtsVolume,
66
+ voiceRemoteAudioGain: props.voiceRemoteAudioGain,
67
+ webrtcEnabled: props.webrtcEnabled,
68
+ }, children: _jsx(AgentAssistantInner, { ...props }) }));
69
+ }
70
+ const styles = StyleSheet.create({
71
+ root: { flex: 1, minHeight: 0 },
72
+ header: {
73
+ paddingHorizontal: 16,
74
+ paddingVertical: 12,
75
+ borderBottomWidth: StyleSheet.hairlineWidth,
76
+ },
77
+ });
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function AgentComposer(): React.ReactElement;
3
+ //# sourceMappingURL=AgentComposer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentComposer.d.ts","sourceRoot":"","sources":["../../src/components/AgentComposer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAW1B,wBAAgB,aAAa,IAAI,KAAK,CAAC,YAAY,CAqFlD"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ActivityIndicator, Pressable, StyleSheet, Text, TextInput, View, } from 'react-native';
3
+ import { useAgentAssistant } from '../context/AgentAssistantContext';
4
+ export function AgentComposer() {
5
+ const { theme, placeholder, inputText, setInputText, sendText, isLoading, isVoiceAvailable, interactionMode, startVoice, isVoiceConnecting, } = useAgentAssistant();
6
+ if (interactionMode === 'voice') {
7
+ return _jsx(View, {});
8
+ }
9
+ return (_jsxs(View, { style: [
10
+ styles.container,
11
+ {
12
+ borderTopColor: theme.colors.border,
13
+ backgroundColor: theme.colors.surface,
14
+ padding: theme.spacing.md,
15
+ },
16
+ ], children: [_jsxs(View, { style: styles.row, children: [_jsx(TextInput, { style: [
17
+ styles.input,
18
+ {
19
+ borderColor: theme.colors.border,
20
+ borderRadius: theme.radius.panel,
21
+ fontSize: theme.typography.bodySize,
22
+ color: theme.colors.assistantText,
23
+ },
24
+ ], value: inputText, onChangeText: setInputText, placeholder: placeholder, placeholderTextColor: theme.colors.mutedText, multiline: true, maxLength: 4000, editable: !isLoading }), _jsx(Pressable, { onPress: () => void sendText(), disabled: isLoading || !inputText.trim(), style: ({ pressed }) => [
25
+ styles.sendButton,
26
+ {
27
+ backgroundColor: theme.colors.primary,
28
+ borderRadius: theme.radius.button,
29
+ opacity: pressed || isLoading || !inputText.trim() ? 0.5 : 1,
30
+ },
31
+ ], children: isLoading ? (_jsx(ActivityIndicator, { color: theme.colors.primaryText, size: "small" })) : (_jsx(Text, { style: { color: theme.colors.primaryText, fontWeight: '600' }, children: "Send" })) })] }), isVoiceAvailable ? (_jsx(Pressable, { onPress: () => {
32
+ startVoice().catch((err) => {
33
+ const message = err instanceof Error ? err.message : String(err);
34
+ console.warn('[nxtlinq] startVoice failed:', message);
35
+ });
36
+ }, disabled: isVoiceConnecting, style: styles.voiceLink, children: _jsx(Text, { style: { color: theme.colors.primary, fontSize: theme.typography.captionSize }, children: isVoiceConnecting ? 'Connecting voice…' : 'Switch to voice mode' }) })) : null] }));
37
+ }
38
+ const styles = StyleSheet.create({
39
+ container: { borderTopWidth: StyleSheet.hairlineWidth },
40
+ row: { flexDirection: 'row', alignItems: 'flex-end', gap: 8 },
41
+ input: {
42
+ flex: 1,
43
+ borderWidth: 1,
44
+ paddingHorizontal: 12,
45
+ paddingVertical: 10,
46
+ maxHeight: 120,
47
+ },
48
+ sendButton: {
49
+ paddingHorizontal: 16,
50
+ paddingVertical: 12,
51
+ justifyContent: 'center',
52
+ alignItems: 'center',
53
+ minWidth: 64,
54
+ },
55
+ voiceLink: { marginTop: 8, alignSelf: 'center' },
56
+ });
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function AgentMessageList(): React.ReactElement;
3
+ //# sourceMappingURL=AgentMessageList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentMessageList.d.ts","sourceRoot":"","sources":["../../src/components/AgentMessageList.tsx"],"names":[],"mappings":"AACA,OAAO,KAAyC,MAAM,OAAO,CAAC;AA4I9D,wBAAgB,gBAAgB,IAAI,KAAK,CAAC,YAAY,CAkCrD"}
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useCallback, useEffect, useRef } from 'react';
3
+ import { ActivityIndicator, FlatList, Pressable, StyleSheet, Text, View, } from 'react-native';
4
+ import { useAgentAssistant } from '../context/AgentAssistantContext';
5
+ import { SpeakerIcon } from './VoiceIcons';
6
+ function MessageBubble({ message }) {
7
+ const { theme, interactionMode, playMessageTts, playingMessageId, isTextTtsAvailable, } = useAgentAssistant();
8
+ const isUser = message.role === 'user';
9
+ const displayText = message.partialContent && message.isStreaming
10
+ ? message.partialContent
11
+ : message.content;
12
+ const [ttsBusy, setTtsBusy] = React.useState(false);
13
+ const [ttsError, setTtsError] = React.useState(null);
14
+ const requestRef = useRef(0);
15
+ const playTts = useCallback(async () => {
16
+ const text = (displayText ?? '').trim();
17
+ if (!text || ttsBusy)
18
+ return;
19
+ if (!isTextTtsAvailable) {
20
+ setTtsError('Install react-native-video for TTS playback');
21
+ return;
22
+ }
23
+ const requestId = ++requestRef.current;
24
+ setTtsBusy(true);
25
+ setTtsError(null);
26
+ try {
27
+ await playMessageTts(message.id, text);
28
+ }
29
+ catch (e) {
30
+ if (requestRef.current === requestId) {
31
+ setTtsError(e instanceof Error ? e.message : String(e));
32
+ }
33
+ }
34
+ finally {
35
+ if (requestRef.current === requestId) {
36
+ setTtsBusy(false);
37
+ }
38
+ }
39
+ }, [displayText, ttsBusy, isTextTtsAvailable, playMessageTts, message.id]);
40
+ const showTts = !isUser &&
41
+ Boolean((displayText ?? '').trim()) &&
42
+ !message.isStreaming;
43
+ const isPlaying = playingMessageId === message.id;
44
+ return (_jsx(View, { style: [
45
+ styles.bubbleRow,
46
+ isUser ? styles.bubbleRowUser : styles.bubbleRowAssistant,
47
+ ], children: _jsxs(View, { style: [
48
+ styles.bubble,
49
+ {
50
+ backgroundColor: isUser
51
+ ? theme.colors.userBubble
52
+ : theme.colors.assistantBubble,
53
+ borderRadius: theme.radius.bubble,
54
+ maxWidth: '85%',
55
+ },
56
+ ], children: [_jsx(Text, { style: {
57
+ color: isUser ? theme.colors.userText : theme.colors.assistantText,
58
+ fontSize: theme.typography.bodySize,
59
+ }, children: displayText || ' ' }), message.isStreaming && message.streamingStatus ? (_jsx(Text, { style: {
60
+ color: theme.colors.mutedText,
61
+ fontSize: theme.typography.captionSize,
62
+ marginTop: theme.spacing.xs,
63
+ }, children: message.streamingStatus })) : null, message.error ? (_jsx(Text, { style: { color: theme.colors.error, fontSize: theme.typography.captionSize }, children: message.error })) : null, showTts ? (_jsxs(View, { style: styles.ttsRow, children: [_jsx(Pressable, { onPress: () => void playTts(), disabled: ttsBusy, style: ({ pressed }) => [
64
+ styles.ttsBtn,
65
+ { opacity: pressed || ttsBusy ? 0.5 : 1 },
66
+ ], children: ttsBusy ? (_jsx(ActivityIndicator, { size: "small", color: theme.colors.primary })) : (_jsx(SpeakerIcon, { size: 22, color: isPlaying ? theme.colors.voiceSpeaking : theme.colors.primary })) }), ttsError ? (_jsx(Text, { style: {
67
+ color: theme.colors.error,
68
+ fontSize: theme.typography.captionSize - 1,
69
+ flex: 1,
70
+ }, numberOfLines: 2, children: ttsError })) : null] })) : null] }) }));
71
+ }
72
+ export function AgentMessageList() {
73
+ const { messages, isLoading, theme } = useAgentAssistant();
74
+ const listRef = useRef(null);
75
+ useEffect(() => {
76
+ if (messages.length > 0) {
77
+ listRef.current?.scrollToEnd({ animated: true });
78
+ }
79
+ }, [messages.length, messages[messages.length - 1]?.content]);
80
+ return (_jsx(View, { style: [styles.container, { backgroundColor: theme.colors.background }], children: _jsx(FlatList, { ref: listRef, data: messages, keyExtractor: (item) => item.id, renderItem: ({ item }) => _jsx(MessageBubble, { message: item }), contentContainerStyle: { padding: theme.spacing.md, paddingBottom: theme.spacing.lg }, ListEmptyComponent: _jsx(Text, { style: [styles.empty, { color: theme.colors.mutedText }], children: "Send a message to start the conversation." }), ListFooterComponent: isLoading ? (_jsx(ActivityIndicator, { style: { marginTop: theme.spacing.sm }, color: theme.colors.primary })) : null }) }));
81
+ }
82
+ const styles = StyleSheet.create({
83
+ container: { flex: 1, minHeight: 0 },
84
+ bubbleRow: { marginBottom: 10 },
85
+ bubbleRowUser: { alignItems: 'flex-end' },
86
+ bubbleRowAssistant: { alignItems: 'flex-start' },
87
+ bubble: { paddingHorizontal: 14, paddingVertical: 10 },
88
+ ttsRow: { flexDirection: 'row', alignItems: 'center', gap: 8, marginTop: 6 },
89
+ ttsBtn: { paddingVertical: 2 },
90
+ empty: { textAlign: 'center', marginTop: 40, fontSize: 15 },
91
+ });
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ export type AgentRemoteAudioProps = {
3
+ /**
4
+ * Playback gain for assistant WebRTC audio (`MediaStreamTrack._setVolume`, 0–10).
5
+ * Text TTS uses react-native-video at volume 1 and sounds louder by default.
6
+ */
7
+ remoteAudioGain?: number;
8
+ };
9
+ /**
10
+ * Plays remote WebRTC assistant audio. On iOS/Android, {@link RTCView} is required
11
+ * (enabling tracks alone is not enough).
12
+ */
13
+ export declare function AgentRemoteAudio({ remoteAudioGain, }: AgentRemoteAudioProps): React.ReactElement | null;
14
+ //# sourceMappingURL=AgentRemoteAudio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentRemoteAudio.d.ts","sourceRoot":"","sources":["../../src/components/AgentRemoteAudio.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AA4BnD,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,eAA2C,GAC5C,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAmDnD"}
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { StyleSheet } from 'react-native';
4
+ import { useAgentAssistant } from '../context/AgentAssistantContext';
5
+ import { applyRemoteAudioPlaybackGain, DEFAULT_REMOTE_AUDIO_GAIN, } from '@bytexbyte/nxtlinq-ai-agent-core-development';
6
+ let RTCView = null;
7
+ try {
8
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
9
+ RTCView = require('react-native-webrtc').RTCView;
10
+ }
11
+ catch {
12
+ RTCView = null;
13
+ }
14
+ /**
15
+ * Plays remote WebRTC assistant audio. On iOS/Android, {@link RTCView} is required
16
+ * (enabling tracks alone is not enough).
17
+ */
18
+ export function AgentRemoteAudio({ remoteAudioGain = DEFAULT_REMOTE_AUDIO_GAIN, }) {
19
+ const { getRemoteAudioStream, interactionMode, voiceSessionId, voiceStatus } = useAgentAssistant();
20
+ const [streamUrl, setStreamUrl] = useState(null);
21
+ useEffect(() => {
22
+ if (interactionMode !== 'voice' || !voiceSessionId) {
23
+ setStreamUrl(null);
24
+ return;
25
+ }
26
+ const sync = () => {
27
+ const stream = getRemoteAudioStream();
28
+ applyRemoteAudioPlaybackGain(stream, remoteAudioGain);
29
+ const tracks = stream?.getAudioTracks?.() ?? [];
30
+ for (const track of tracks) {
31
+ if (track.kind === 'audio' || track.kind == null) {
32
+ track.enabled = true;
33
+ }
34
+ }
35
+ const url = stream?.toURL?.() ?? null;
36
+ setStreamUrl((prev) => (prev === url ? prev : url));
37
+ };
38
+ sync();
39
+ const id = setInterval(sync, 150);
40
+ return () => clearInterval(id);
41
+ }, [
42
+ getRemoteAudioStream,
43
+ interactionMode,
44
+ voiceSessionId,
45
+ voiceStatus,
46
+ remoteAudioGain,
47
+ ]);
48
+ if (!RTCView || !streamUrl || interactionMode !== 'voice') {
49
+ return null;
50
+ }
51
+ return (_jsx(RTCView, { streamURL: streamUrl, objectFit: "cover", zOrder: -1, style: styles.hidden }, streamUrl));
52
+ }
53
+ const styles = StyleSheet.create({
54
+ hidden: {
55
+ position: 'absolute',
56
+ width: 1,
57
+ height: 1,
58
+ opacity: 0.01,
59
+ left: -1000,
60
+ top: -1000,
61
+ },
62
+ });
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function AgentVoiceBar(): React.ReactElement | null;
3
+ //# sourceMappingURL=AgentVoiceBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentVoiceBar.d.ts","sourceRoot":"","sources":["../../src/components/AgentVoiceBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AA+BhE,wBAAgB,aAAa,IAAI,KAAK,CAAC,YAAY,GAAG,IAAI,CAsKzD"}
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { ActivityIndicator, Pressable, StyleSheet, Text, View, } from 'react-native';
4
+ import { useAgentAssistant, voiceStatusLabel, } from '../context/AgentAssistantContext';
5
+ import { SPEAKER_ACTIVE_STATUSES } from '../voice/voiceMicConstants';
6
+ import { MicIcon, MicOffIcon, SpeakerIcon, StopIcon } from './VoiceIcons';
7
+ function statusDotColor(status, theme) {
8
+ switch (status) {
9
+ case 'listening':
10
+ return theme.colors.voiceActive;
11
+ case 'speaking':
12
+ return theme.colors.voiceSpeaking;
13
+ case 'idle':
14
+ return theme.colors.mutedText;
15
+ default:
16
+ return theme.colors.primary;
17
+ }
18
+ }
19
+ export function AgentVoiceBar() {
20
+ const { theme, interactionMode, voiceStatus, voiceSessionId, isVoiceConnecting, isVoiceChannelReady, stopVoice, interrupt, isVoiceAvailable, isMicMuted, isMicHeldForAssistant, toggleVoiceMicMute, } = useAgentAssistant();
21
+ const [speakerPulseOpacity, setSpeakerPulseOpacity] = useState(1);
22
+ const isSpeakerActive = SPEAKER_ACTIVE_STATUSES.has(voiceStatus);
23
+ useEffect(() => {
24
+ if (!isSpeakerActive) {
25
+ setSpeakerPulseOpacity(1);
26
+ return;
27
+ }
28
+ let high = true;
29
+ const id = setInterval(() => {
30
+ setSpeakerPulseOpacity(high ? 0.35 : 1);
31
+ high = !high;
32
+ }, 600);
33
+ return () => clearInterval(id);
34
+ }, [isSpeakerActive]);
35
+ const returnToTextMode = useCallback(() => {
36
+ void stopVoice();
37
+ }, [stopVoice]);
38
+ if (!isVoiceAvailable)
39
+ return null;
40
+ if (interactionMode !== 'voice' && !isVoiceConnecting)
41
+ return null;
42
+ const showConnecting = isVoiceConnecting;
43
+ const awaitingChannel = Boolean(voiceSessionId) &&
44
+ !isVoiceConnecting &&
45
+ !isVoiceChannelReady &&
46
+ voiceStatus === 'idle';
47
+ const statusHint = showConnecting
48
+ ? 'Tap Back to text mode below to cancel'
49
+ : awaitingChannel
50
+ ? 'Waiting for voice channel…'
51
+ : isMicHeldForAssistant
52
+ ? 'Mic paused while assistant responds (use Interrupt to speak)'
53
+ : isMicMuted
54
+ ? 'Mic is off — tap the mic when ready to speak'
55
+ : voiceStatus === 'listening'
56
+ ? 'Start speaking'
57
+ : voiceStatus === 'speaking'
58
+ ? 'Assistant is speaking…'
59
+ : voiceStatus === 'thinking'
60
+ ? 'Thinking…'
61
+ : '';
62
+ return (_jsxs(View, { style: [
63
+ styles.container,
64
+ {
65
+ borderTopColor: theme.colors.border,
66
+ backgroundColor: theme.colors.surface,
67
+ padding: theme.spacing.md,
68
+ },
69
+ ], children: [_jsxs(View, { style: styles.mainRow, children: [_jsxs(View, { style: [
70
+ styles.statusPill,
71
+ { borderColor: theme.colors.border, backgroundColor: theme.colors.background },
72
+ ], children: [_jsxs(View, { style: styles.statusPillHeader, children: [_jsx(View, { style: [
73
+ styles.dot,
74
+ { backgroundColor: statusDotColor(voiceStatus, theme) },
75
+ ] }), _jsx(Text, { style: {
76
+ color: theme.colors.assistantText,
77
+ fontSize: theme.typography.captionSize,
78
+ fontWeight: '600',
79
+ flexShrink: 1,
80
+ }, numberOfLines: 1, children: showConnecting
81
+ ? 'Connecting'
82
+ : awaitingChannel
83
+ ? 'Connecting'
84
+ : voiceStatusLabel(voiceStatus) }), showConnecting ? (_jsx(ActivityIndicator, { color: theme.colors.primary, size: "small" })) : null] }), statusHint ? (_jsx(Text, { style: {
85
+ color: theme.colors.mutedText,
86
+ fontSize: theme.typography.captionSize - 1,
87
+ flexShrink: 1,
88
+ alignSelf: 'stretch',
89
+ }, numberOfLines: 2, children: statusHint })) : null] }), _jsxs(View, { style: styles.iconRow, children: [_jsx(View, { style: { opacity: isSpeakerActive ? speakerPulseOpacity : 0.35 }, children: _jsx(SpeakerIcon, { size: 24, color: theme.colors.voiceSpeaking }) }), _jsx(Pressable, { onPress: toggleVoiceMicMute, disabled: showConnecting || awaitingChannel || isMicHeldForAssistant, style: ({ pressed }) => [
90
+ styles.iconButton,
91
+ {
92
+ opacity: pressed || showConnecting || awaitingChannel || isMicHeldForAssistant
93
+ ? 0.45
94
+ : 1,
95
+ },
96
+ ], children: isMicMuted ? (_jsx(MicOffIcon, { size: 24, color: "#ef4444" })) : (_jsx(MicIcon, { size: 24, color: theme.colors.assistantText })) }), _jsx(Pressable, { onPress: () => interrupt(), disabled: showConnecting || awaitingChannel, style: ({ pressed }) => [
97
+ styles.iconButton,
98
+ { opacity: pressed || showConnecting || awaitingChannel ? 0.45 : 1 },
99
+ ], children: _jsx(StopIcon, { size: 24, color: theme.colors.assistantText }) })] })] }), _jsx(Pressable, { onPress: returnToTextMode, style: styles.textLink, children: _jsx(Text, { style: { color: theme.colors.primary, fontSize: theme.typography.captionSize }, children: "Back to text mode" }) })] }));
100
+ }
101
+ const styles = StyleSheet.create({
102
+ container: { borderTopWidth: StyleSheet.hairlineWidth },
103
+ mainRow: {
104
+ flexDirection: 'row',
105
+ alignItems: 'center',
106
+ justifyContent: 'space-between',
107
+ gap: 10,
108
+ },
109
+ statusPill: {
110
+ flex: 1,
111
+ minWidth: 0,
112
+ flexDirection: 'column',
113
+ alignItems: 'flex-start',
114
+ gap: 4,
115
+ paddingHorizontal: 10,
116
+ paddingVertical: 8,
117
+ borderRadius: 12,
118
+ borderWidth: 1,
119
+ },
120
+ statusPillHeader: {
121
+ flexDirection: 'row',
122
+ alignItems: 'center',
123
+ gap: 6,
124
+ alignSelf: 'stretch',
125
+ },
126
+ dot: { width: 8, height: 8, borderRadius: 4 },
127
+ iconRow: { flexDirection: 'row', alignItems: 'center', gap: 2 },
128
+ iconButton: {
129
+ padding: 8,
130
+ borderRadius: 8,
131
+ },
132
+ textLink: { marginTop: 10, alignSelf: 'center' },
133
+ });
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function PresetMessageChips(): React.ReactElement | null;
3
+ //# sourceMappingURL=PresetMessageChips.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PresetMessageChips.d.ts","sourceRoot":"","sources":["../../src/components/PresetMessageChips.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,wBAAgB,kBAAkB,IAAI,KAAK,CAAC,YAAY,GAAG,IAAI,CA6C9D"}
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
3
+ import { useAgentAssistant } from '../context/AgentAssistantContext';
4
+ export function PresetMessageChips() {
5
+ const { presetMessages, selectPreset, theme, interactionMode } = useAgentAssistant();
6
+ if (!presetMessages.length || interactionMode === 'voice') {
7
+ return null;
8
+ }
9
+ return (_jsx(View, { style: [
10
+ styles.container,
11
+ {
12
+ borderBottomColor: theme.colors.border,
13
+ backgroundColor: theme.colors.surface,
14
+ },
15
+ ], children: _jsx(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, contentContainerStyle: styles.scroll, children: presetMessages.map((preset, index) => (_jsx(Pressable, { onPress: () => void selectPreset(preset), style: ({ pressed }) => [
16
+ styles.chip,
17
+ {
18
+ borderColor: theme.colors.border,
19
+ backgroundColor: pressed ? theme.colors.border : theme.colors.background,
20
+ borderRadius: theme.radius.button,
21
+ },
22
+ ], children: _jsx(Text, { style: {
23
+ color: theme.colors.assistantText,
24
+ fontSize: theme.typography.captionSize,
25
+ }, numberOfLines: 1, children: preset.text }) }, `${preset.text}-${index}`))) }) }));
26
+ }
27
+ const styles = StyleSheet.create({
28
+ container: {
29
+ borderBottomWidth: StyleSheet.hairlineWidth,
30
+ paddingVertical: 8,
31
+ },
32
+ scroll: { paddingHorizontal: 12, gap: 8 },
33
+ chip: {
34
+ borderWidth: 1,
35
+ paddingHorizontal: 12,
36
+ paddingVertical: 8,
37
+ marginRight: 8,
38
+ },
39
+ });
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type { VoiceAutoGreetConfig } from '../voice/useVoiceAutoGreet';
3
+ export type VoiceGreetTriggerProps = {
4
+ config: VoiceAutoGreetConfig;
5
+ };
6
+ /**
7
+ * P0 / QA: manually fire the same payload as {@link useVoiceAutoGreet} (product image + intro text).
8
+ */
9
+ export declare function VoiceGreetTrigger({ config }: VoiceGreetTriggerProps): React.ReactElement | null;
10
+ //# sourceMappingURL=VoiceGreetTrigger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VoiceGreetTrigger.d.ts","sourceRoot":"","sources":["../../src/components/VoiceGreetTrigger.tsx"],"names":[],"mappings":"AACA,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAUvE,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,oBAAoB,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAAE,sBAAsB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAwH/F"}