@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.1.3 → 0.1.4

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 (91) hide show
  1. package/dist/ChatBot.d.ts +5 -0
  2. package/dist/ChatBot.d.ts.map +1 -0
  3. package/dist/ChatBot.js +35 -0
  4. package/dist/assets/images/adiSideItalicDataUri.d.ts +2 -0
  5. package/dist/assets/images/adiSideItalicDataUri.d.ts.map +1 -0
  6. package/dist/assets/images/adiSideItalicDataUri.js +1 -0
  7. package/dist/context/ChatBotContext.d.ts +5 -0
  8. package/dist/context/ChatBotContext.d.ts.map +1 -0
  9. package/dist/context/ChatBotContext.js +2908 -0
  10. package/dist/index.d.ts +5 -13
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -11
  13. package/dist/legacy/chatbot/context/ChatBotContext.d.ts.map +1 -1
  14. package/dist/legacy/chatbot/context/ChatBotContext.js +14 -0
  15. package/dist/types/ChatBotTypes.d.ts +166 -0
  16. package/dist/types/ChatBotTypes.d.ts.map +1 -0
  17. package/dist/types/ChatBotTypes.js +1 -0
  18. package/dist/ui/BerifyMeModal.d.ts +17 -0
  19. package/dist/ui/BerifyMeModal.d.ts.map +1 -0
  20. package/dist/ui/BerifyMeModal.js +110 -0
  21. package/dist/ui/ChatBotHeader.d.ts +15 -0
  22. package/dist/ui/ChatBotHeader.d.ts.map +1 -0
  23. package/dist/ui/ChatBotHeader.js +62 -0
  24. package/dist/ui/ChatBotUI.d.ts +3 -0
  25. package/dist/ui/ChatBotUI.d.ts.map +1 -0
  26. package/dist/ui/ChatBotUI.js +557 -0
  27. package/dist/ui/MessageInput.d.ts +3 -0
  28. package/dist/ui/MessageInput.d.ts.map +1 -0
  29. package/dist/ui/MessageInput.js +321 -0
  30. package/dist/ui/MessageList.d.ts +4 -0
  31. package/dist/ui/MessageList.d.ts.map +1 -0
  32. package/dist/ui/MessageList.js +455 -0
  33. package/dist/ui/ModelSelector.d.ts +4 -0
  34. package/dist/ui/ModelSelector.d.ts.map +1 -0
  35. package/dist/ui/ModelSelector.js +122 -0
  36. package/dist/ui/NotificationModal.d.ts +15 -0
  37. package/dist/ui/NotificationModal.d.ts.map +1 -0
  38. package/dist/ui/NotificationModal.js +53 -0
  39. package/dist/ui/PermissionForm.d.ts +8 -0
  40. package/dist/ui/PermissionForm.d.ts.map +1 -0
  41. package/dist/ui/PermissionForm.js +465 -0
  42. package/dist/ui/PresetMessages.d.ts +4 -0
  43. package/dist/ui/PresetMessages.d.ts.map +1 -0
  44. package/dist/ui/PresetMessages.js +33 -0
  45. package/dist/ui/VoiceModePanel.d.ts +3 -0
  46. package/dist/ui/VoiceModePanel.d.ts.map +1 -0
  47. package/dist/ui/VoiceModePanel.js +95 -0
  48. package/dist/ui/chatBotHeaderParts.d.ts +15 -0
  49. package/dist/ui/chatBotHeaderParts.d.ts.map +1 -0
  50. package/dist/ui/chatBotHeaderParts.js +50 -0
  51. package/dist/ui/index.d.ts +9 -0
  52. package/dist/ui/index.d.ts.map +1 -0
  53. package/dist/ui/index.js +8 -0
  54. package/dist/ui/styles/isolatedStyles.d.ts +73 -0
  55. package/dist/ui/styles/isolatedStyles.d.ts.map +1 -0
  56. package/dist/ui/styles/isolatedStyles.js +985 -0
  57. package/package.json +2 -2
  58. package/src/{legacy/chatbot/context → context}/ChatBotContext.tsx +0 -1
  59. package/src/index.ts +17 -40
  60. package/src/{legacy/chatbot/ui → ui}/ModelSelector.tsx +1 -1
  61. package/src/{legacy/chatbot/ui → ui}/VoiceModePanel.tsx +1 -1
  62. package/src/ui/index.ts +8 -0
  63. package/src/NxtlinqAgentChat.tsx +0 -79
  64. package/src/components/AgentAssistantShell.tsx +0 -104
  65. package/src/components/AgentComposer.tsx +0 -134
  66. package/src/components/AgentMessageList.tsx +0 -78
  67. package/src/components/AgentRemoteAudio.tsx +0 -34
  68. package/src/components/AgentVoiceBar.tsx +0 -173
  69. package/src/components/PresetMessageChips.tsx +0 -41
  70. package/src/context/AgentAssistantContext.tsx +0 -294
  71. package/src/legacy/index.ts +0 -26
  72. package/src/theme/defaultTheme.ts +0 -22
  73. package/src/types.ts +0 -65
  74. package/src/voice/useVoiceConnectOrchestration.ts +0 -117
  75. package/src/voice/useVoiceMicState.ts +0 -117
  76. package/src/voice/useVoiceTranscriptMessages.ts +0 -188
  77. package/src/voice/voiceMicConstants.ts +0 -13
  78. package/src/voice/voiceUserBubble.ts +0 -71
  79. /package/src/{legacy/chatbot/ChatBot.tsx → ChatBot.tsx} +0 -0
  80. /package/src/{legacy/assets → assets}/images/adiSideItalicDataUri.ts +0 -0
  81. /package/src/{legacy/chatbot/types → types}/ChatBotTypes.ts +0 -0
  82. /package/src/{legacy/chatbot/ui → ui}/BerifyMeModal.tsx +0 -0
  83. /package/src/{legacy/chatbot/ui → ui}/ChatBotHeader.tsx +0 -0
  84. /package/src/{legacy/chatbot/ui → ui}/ChatBotUI.tsx +0 -0
  85. /package/src/{legacy/chatbot/ui → ui}/MessageInput.tsx +0 -0
  86. /package/src/{legacy/chatbot/ui → ui}/MessageList.tsx +0 -0
  87. /package/src/{legacy/chatbot/ui → ui}/NotificationModal.tsx +0 -0
  88. /package/src/{legacy/chatbot/ui → ui}/PermissionForm.tsx +0 -0
  89. /package/src/{legacy/chatbot/ui → ui}/PresetMessages.tsx +0 -0
  90. /package/src/{legacy/chatbot/ui → ui}/chatBotHeaderParts.tsx +0 -0
  91. /package/src/{legacy/chatbot/ui → ui}/styles/isolatedStyles.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytexbyte/nxtlinq-ai-agent-ui-react-development",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Official React Web UI for nxtlinq AI Agent — drop-in chat widget",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@bytexbyte/nxtlinq-ai-agent-core-development": "workspace:^",
53
- "@bytexbyte/nxtlinq-ai-agent-web-development": "workspace:^",
53
+ "@bytexbyte/nxtlinq-ai-agent-web-development": "0.1.3",
54
54
  "@types/react": "^18.2.64",
55
55
  "@types/react-dom": "^18.2.25",
56
56
  "react": "^18.2.0",
@@ -82,7 +82,6 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
82
82
  // Set API hosts immediately based on environment (before any API calls)
83
83
  setApiHosts(environment);
84
84
 
85
-
86
85
  const nxtlinqApi = React.useMemo(() => createNxtlinqApi(apiKey, apiSecret), [apiKey, apiSecret]);
87
86
 
88
87
  const apiKeyRef = React.useRef(apiKey);
package/src/index.ts CHANGED
@@ -1,10 +1,7 @@
1
- export { NxtlinqAgentChat } from './NxtlinqAgentChat';
1
+ export { ChatBot } from './ChatBot';
2
+ export { ChatBotProvider, useChatBot } from './context/ChatBotContext';
2
3
 
3
4
  export {
4
- ChatBot,
5
- NxtlinqChatBot,
6
- ChatBotProvider,
7
- useChatBot,
8
5
  ChatBotUI,
9
6
  MessageInput,
10
7
  MessageList,
@@ -13,7 +10,7 @@ export {
13
10
  PermissionForm,
14
11
  PresetMessages,
15
12
  BerifyMeModal,
16
- } from './legacy';
13
+ } from './ui/index';
17
14
 
18
15
  export type {
19
16
  AIModel,
@@ -23,41 +20,9 @@ export type {
23
20
  NovaError,
24
21
  NovaResponse,
25
22
  ToolCall,
26
- } from './legacy';
27
-
28
- export type {
29
- NxtlinqAgentChatProps,
30
- AgentAssistantTheme,
31
- PresetMessage,
32
- Message,
33
23
  ToolUse,
34
- } from './types';
35
-
36
- export { defaultAgentAssistantTheme } from './theme/defaultTheme';
37
-
38
- export {
39
- AgentAssistantProvider,
40
- useAgentAssistant,
41
- voiceStatusLabel,
42
- type AgentAssistantContextValue,
43
- type InteractionMode,
44
- } from './context/AgentAssistantContext';
45
-
46
- export { AgentMessageList } from './components/AgentMessageList';
47
- export { AgentComposer } from './components/AgentComposer';
48
- export { AgentVoiceBar } from './components/AgentVoiceBar';
49
- export { AgentRemoteAudio } from './components/AgentRemoteAudio';
50
- export { PresetMessageChips } from './components/PresetMessageChips';
51
- export { AgentAssistantShell } from './components/AgentAssistantShell';
52
-
53
- export {
54
- NxtlinqAgentProvider,
55
- useNxtlinqAgent,
56
- useNxtlinqVoice,
57
- createNxtlinqAgentWeb,
58
- fileToAttachment,
59
- type NxtlinqAgentProviderProps,
60
- } from '@bytexbyte/nxtlinq-ai-agent-web-development';
24
+ PresetMessage,
25
+ } from './types/ChatBotTypes';
61
26
 
62
27
  export type {
63
28
  AgentEnvironment,
@@ -76,3 +41,15 @@ export {
76
41
  VoiceNotSupportedError,
77
42
  STORAGE_KEYS,
78
43
  } from '@bytexbyte/nxtlinq-ai-agent-core-development';
44
+
45
+ export {
46
+ createNxtlinqApi,
47
+ useLocalStorage,
48
+ useSessionStorage,
49
+ useSpeechToTextFromMic,
50
+ useVoiceMode,
51
+ metakeepClient,
52
+ getEthers,
53
+ sleep,
54
+ walletTextUtils,
55
+ } from '@bytexbyte/nxtlinq-ai-agent-web-development';
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { css } from '@emotion/react';
4
4
  import { useChatBot } from '../context/ChatBotContext';
5
5
  import { walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
6
- import { adiSideItalicDataUri } from '../../assets/images/adiSideItalicDataUri';
6
+ import { adiSideItalicDataUri } from '../assets/images/adiSideItalicDataUri';
7
7
 
8
8
  export const ModelSelector: React.FC = () => {
9
9
  const {
@@ -134,7 +134,7 @@ export const VoiceModePanel: React.FC = () => {
134
134
  <span>
135
135
  <IconButton
136
136
  onClick={toggleVoiceMicMute}
137
- disabled={isVoiceConnecting || isMicHeldForAssistant}
137
+ disabled={voiceStatus !== 'listening' || isVoiceConnecting || isMicHeldForAssistant}
138
138
  size="small"
139
139
  css={css`color: ${isMicMuted ? '#ef4444' : '#4b5563'} !important;`}
140
140
  >
@@ -0,0 +1,8 @@
1
+ export { ChatBotUI } from './ChatBotUI';
2
+ export { MessageInput } from './MessageInput';
3
+ export { MessageList } from './MessageList';
4
+ export { ModelSelector } from './ModelSelector';
5
+ export { NotificationModal } from './NotificationModal';
6
+ export { PermissionForm } from './PermissionForm';
7
+ export { PresetMessages } from './PresetMessages';
8
+ export { BerifyMeModal } from './BerifyMeModal';
@@ -1,79 +0,0 @@
1
- import { NxtlinqAgentProvider } from '@bytexbyte/nxtlinq-ai-agent-web-development';
2
- import React from 'react';
3
- import { AgentAssistantShell } from './components/AgentAssistantShell';
4
- import type { NxtlinqAgentChatProps } from './types';
5
-
6
- /**
7
- * Drop-in React Web assistant UI wired to `@bytexbyte/nxtlinq-ai-agent-web-development`.
8
- *
9
- * @example
10
- * ```tsx
11
- * import { NxtlinqAgentChat } from '@bytexbyte/nxtlinq-ai-agent-ui-react-development';
12
- *
13
- * export default function Page() {
14
- * return (
15
- * <NxtlinqAgentChat
16
- * style={{ height: '100vh' }}
17
- * serviceId="..."
18
- * apiKey="..."
19
- * apiSecret="..."
20
- * environment="staging"
21
- * pseudoId={userId}
22
- * loadHistoryOnMount
23
- * />
24
- * );
25
- * }
26
- * ```
27
- */
28
- export function NxtlinqAgentChat({
29
- title,
30
- placeholder,
31
- presetMessages,
32
- loadHistoryOnMount = false,
33
- historyLast,
34
- enableVoice = true,
35
- enableFileUpload = true,
36
- startInVoiceMode = false,
37
- startWithMicMuted = true,
38
- holdMicDuringAssistant = true,
39
- theme,
40
- style,
41
- headerStyle,
42
- onMessage,
43
- onError,
44
- onToolUse,
45
- children,
46
- fetchImpl,
47
- getTimezone,
48
- resetOnIdentityChange,
49
- ...agentConfig
50
- }: NxtlinqAgentChatProps): React.ReactElement {
51
- return (
52
- <NxtlinqAgentProvider
53
- fetchImpl={fetchImpl}
54
- getTimezone={getTimezone}
55
- resetOnIdentityChange={resetOnIdentityChange}
56
- onMessage={onMessage}
57
- onError={onError}
58
- onToolUse={onToolUse}
59
- {...agentConfig}
60
- >
61
- <AgentAssistantShell
62
- title={title}
63
- placeholder={placeholder}
64
- presetMessages={presetMessages}
65
- loadHistoryOnMount={loadHistoryOnMount}
66
- historyLast={historyLast}
67
- enableVoice={enableVoice}
68
- enableFileUpload={enableFileUpload}
69
- startInVoiceMode={startInVoiceMode}
70
- startWithMicMuted={startWithMicMuted}
71
- holdMicDuringAssistant={holdMicDuringAssistant}
72
- theme={theme}
73
- style={style}
74
- headerStyle={headerStyle}
75
- />
76
- {children}
77
- </NxtlinqAgentProvider>
78
- );
79
- }
@@ -1,104 +0,0 @@
1
- import React, { useEffect, useRef, useState } from 'react';
2
- import {
3
- AgentAssistantProvider,
4
- useAgentAssistant,
5
- } from '../context/AgentAssistantContext';
6
- import type { NxtlinqAgentChatProps } from '../types';
7
- import { AgentComposer } from './AgentComposer';
8
- import { AgentMessageList } from './AgentMessageList';
9
- import { AgentRemoteAudio } from './AgentRemoteAudio';
10
- import { AgentVoiceBar } from './AgentVoiceBar';
11
- import { PresetMessageChips } from './PresetMessageChips';
12
-
13
- export type AgentAssistantShellProps = Pick<
14
- NxtlinqAgentChatProps,
15
- | 'title'
16
- | 'placeholder'
17
- | 'presetMessages'
18
- | 'enableVoice'
19
- | 'enableFileUpload'
20
- | 'theme'
21
- | 'style'
22
- | 'headerStyle'
23
- | 'loadHistoryOnMount'
24
- | 'historyLast'
25
- | 'startInVoiceMode'
26
- | 'startWithMicMuted'
27
- | 'holdMicDuringAssistant'
28
- >;
29
-
30
- function AgentAssistantInner({
31
- title,
32
- headerStyle,
33
- style,
34
- loadHistoryOnMount,
35
- historyLast,
36
- startInVoiceMode,
37
- }: AgentAssistantShellProps): React.ReactElement {
38
- const { theme, loadHistory, startVoice, isVoiceAvailable } = useAgentAssistant();
39
- const [historyReady, setHistoryReady] = useState(!loadHistoryOnMount);
40
- const voiceAutoStartRef = useRef(false);
41
-
42
- useEffect(() => {
43
- if (loadHistoryOnMount) {
44
- void loadHistory({ last: historyLast ?? 50 }).finally(() => setHistoryReady(true));
45
- }
46
- }, [loadHistory, loadHistoryOnMount, historyLast]);
47
-
48
- useEffect(() => {
49
- if (!startInVoiceMode || !isVoiceAvailable || voiceAutoStartRef.current) return;
50
- voiceAutoStartRef.current = true;
51
- void startVoice();
52
- }, [startInVoiceMode, isVoiceAvailable, startVoice]);
53
-
54
- return (
55
- <div
56
- style={{
57
- display: 'flex',
58
- flexDirection: 'column',
59
- flex: 1,
60
- minHeight: 0,
61
- backgroundColor: theme.colors.background,
62
- ...style,
63
- }}
64
- >
65
- <header
66
- style={{
67
- padding: theme.spacing.md,
68
- borderBottom: `1px solid ${theme.colors.border}`,
69
- backgroundColor: theme.colors.surface,
70
- fontSize: theme.typography.titleSize,
71
- fontWeight: 600,
72
- color: theme.colors.assistantText,
73
- ...headerStyle,
74
- }}
75
- >
76
- {title ?? 'AI Assistant'}
77
- </header>
78
- <PresetMessageChips />
79
- {historyReady ? <AgentMessageList /> : <div style={{ flex: 1 }} />}
80
- <AgentVoiceBar />
81
- <AgentComposer />
82
- <AgentRemoteAudio />
83
- </div>
84
- );
85
- }
86
-
87
- export function AgentAssistantShell(props: AgentAssistantShellProps): React.ReactElement {
88
- return (
89
- <AgentAssistantProvider
90
- ui={{
91
- title: props.title,
92
- placeholder: props.placeholder,
93
- presetMessages: props.presetMessages,
94
- enableVoice: props.enableVoice,
95
- enableFileUpload: props.enableFileUpload,
96
- theme: props.theme,
97
- startWithMicMuted: props.startWithMicMuted,
98
- holdMicDuringAssistant: props.holdMicDuringAssistant,
99
- }}
100
- >
101
- <AgentAssistantInner {...props} />
102
- </AgentAssistantProvider>
103
- );
104
- }
@@ -1,134 +0,0 @@
1
- import React from 'react';
2
- import { useAgentAssistant } from '../context/AgentAssistantContext';
3
-
4
- export function AgentComposer(): React.ReactElement | null {
5
- const {
6
- theme,
7
- placeholder,
8
- inputText,
9
- setInputText,
10
- sendText,
11
- isLoading,
12
- isVoiceAvailable,
13
- interactionMode,
14
- startVoice,
15
- isVoiceConnecting,
16
- enableFileUpload,
17
- sendMessage,
18
- } = useAgentAssistant();
19
- const fileInputRef = React.useRef<HTMLInputElement | null>(null);
20
-
21
- if (interactionMode === 'voice') {
22
- return null;
23
- }
24
-
25
- const handleFiles = async (files: FileList | null) => {
26
- if (!files?.length) return;
27
- const { fileToAttachment } = await import('@bytexbyte/nxtlinq-ai-agent-web-development');
28
- const attachments = await Promise.all(
29
- Array.from(files).map((file) => fileToAttachment(file)),
30
- );
31
- await sendMessage(
32
- attachments.length ? `Uploaded ${attachments.length} file(s)` : '',
33
- { attachments },
34
- );
35
- if (fileInputRef.current) fileInputRef.current.value = '';
36
- };
37
-
38
- return (
39
- <div
40
- style={{
41
- borderTop: `1px solid ${theme.colors.border}`,
42
- backgroundColor: theme.colors.surface,
43
- padding: theme.spacing.md,
44
- }}
45
- >
46
- <div style={{ display: 'flex', gap: theme.spacing.sm, alignItems: 'flex-end' }}>
47
- {enableFileUpload ? (
48
- <>
49
- <button
50
- type="button"
51
- onClick={() => fileInputRef.current?.click()}
52
- disabled={isLoading}
53
- style={{
54
- width: 36,
55
- height: 36,
56
- borderRadius: theme.radius.button,
57
- border: `1px solid ${theme.colors.border}`,
58
- backgroundColor: theme.colors.surface,
59
- cursor: 'pointer',
60
- }}
61
- aria-label="Upload file"
62
- >
63
- +
64
- </button>
65
- <input
66
- ref={fileInputRef}
67
- type="file"
68
- multiple
69
- accept="image/*,.pdf,.doc,.docx,.txt"
70
- style={{ display: 'none' }}
71
- onChange={(e) => void handleFiles(e.target.files)}
72
- />
73
- </>
74
- ) : null}
75
- <textarea
76
- value={inputText}
77
- onChange={(e) => setInputText(e.target.value)}
78
- placeholder={placeholder}
79
- rows={2}
80
- disabled={isLoading}
81
- maxLength={4000}
82
- style={{
83
- flex: 1,
84
- resize: 'none',
85
- border: `1px solid ${theme.colors.border}`,
86
- borderRadius: theme.radius.panel,
87
- padding: theme.spacing.sm,
88
- fontSize: theme.typography.bodySize,
89
- color: theme.colors.assistantText,
90
- }}
91
- />
92
- <button
93
- type="button"
94
- onClick={() => void sendText()}
95
- disabled={isLoading || !inputText.trim()}
96
- style={{
97
- backgroundColor: theme.colors.primary,
98
- color: theme.colors.primaryText,
99
- border: 'none',
100
- borderRadius: theme.radius.button,
101
- padding: `${theme.spacing.sm}px ${theme.spacing.md}px`,
102
- fontWeight: 600,
103
- opacity: isLoading || !inputText.trim() ? 0.5 : 1,
104
- cursor: 'pointer',
105
- }}
106
- >
107
- Send
108
- </button>
109
- </div>
110
- {isVoiceAvailable ? (
111
- <button
112
- type="button"
113
- onClick={() => {
114
- startVoice().catch((err: unknown) => {
115
- console.warn('[nxtlinq] startVoice failed:', err);
116
- });
117
- }}
118
- disabled={isVoiceConnecting}
119
- style={{
120
- display: 'block',
121
- margin: `${theme.spacing.sm}px auto 0`,
122
- border: 'none',
123
- background: 'none',
124
- color: theme.colors.primary,
125
- fontSize: theme.typography.captionSize,
126
- cursor: 'pointer',
127
- }}
128
- >
129
- {isVoiceConnecting ? 'Connecting voice…' : 'Switch to voice mode'}
130
- </button>
131
- ) : null}
132
- </div>
133
- );
134
- }
@@ -1,78 +0,0 @@
1
- import type { Message } from '@bytexbyte/nxtlinq-ai-agent-core-development';
2
- import React, { useEffect, useRef } from 'react';
3
- import { useAgentAssistant } from '../context/AgentAssistantContext';
4
-
5
- function MessageBubble({ message }: { message: Message }): React.ReactElement {
6
- const { theme } = useAgentAssistant();
7
- const isUser = message.role === 'user';
8
- const displayText =
9
- message.partialContent && message.isStreaming
10
- ? message.partialContent
11
- : message.content;
12
-
13
- return (
14
- <div
15
- style={{
16
- display: 'flex',
17
- justifyContent: isUser ? 'flex-end' : 'flex-start',
18
- marginBottom: theme.spacing.sm,
19
- }}
20
- >
21
- <div
22
- style={{
23
- maxWidth: '85%',
24
- padding: `${theme.spacing.sm}px ${theme.spacing.md}px`,
25
- borderRadius: theme.radius.bubble,
26
- backgroundColor: isUser ? theme.colors.userBubble : theme.colors.assistantBubble,
27
- color: isUser ? theme.colors.userText : theme.colors.assistantText,
28
- fontSize: theme.typography.bodySize,
29
- whiteSpace: 'pre-wrap',
30
- }}
31
- >
32
- {displayText || ' '}
33
- {message.error ? (
34
- <div style={{ color: theme.colors.error, fontSize: theme.typography.captionSize }}>
35
- {message.error}
36
- </div>
37
- ) : null}
38
- </div>
39
- </div>
40
- );
41
- }
42
-
43
- export function AgentMessageList(): React.ReactElement {
44
- const { messages, isLoading, theme } = useAgentAssistant();
45
- const endRef = useRef<HTMLDivElement | null>(null);
46
-
47
- useEffect(() => {
48
- endRef.current?.scrollIntoView({ behavior: 'smooth' });
49
- }, [messages.length, messages[messages.length - 1]?.content, messages[messages.length - 1]?.partialContent]);
50
-
51
- return (
52
- <div
53
- style={{
54
- flex: 1,
55
- minHeight: 0,
56
- overflowY: 'auto',
57
- padding: theme.spacing.md,
58
- backgroundColor: theme.colors.background,
59
- }}
60
- >
61
- {messages.length === 0 ? (
62
- <p style={{ textAlign: 'center', color: theme.colors.mutedText, marginTop: 40 }}>
63
- Send a message to start the conversation.
64
- </p>
65
- ) : (
66
- messages.map((message: Message) => (
67
- <MessageBubble key={message.id} message={message} />
68
- ))
69
- )}
70
- {isLoading ? (
71
- <p style={{ color: theme.colors.mutedText, fontSize: theme.typography.captionSize }}>
72
- Thinking…
73
- </p>
74
- ) : null}
75
- <div ref={endRef} />
76
- </div>
77
- );
78
- }
@@ -1,34 +0,0 @@
1
- import React, { useEffect, useRef } from 'react';
2
- import { useAgentAssistant } from '../context/AgentAssistantContext';
3
-
4
- /** Plays assistant WebRTC remote audio with retry until stream is ready. */
5
- export function AgentRemoteAudio(): React.ReactElement | null {
6
- const { getRemoteAudioStream, isVoiceActive, isVoiceChannelReady } = useAgentAssistant();
7
- const audioRef = useRef<HTMLAudioElement | null>(null);
8
-
9
- useEffect(() => {
10
- const audio = audioRef.current;
11
- if (!audio || !isVoiceActive) return;
12
-
13
- let cancelled = false;
14
- const attach = () => {
15
- if (cancelled) return;
16
- const stream = getRemoteAudioStream();
17
- if (!stream) return;
18
- audio.srcObject = stream;
19
- void audio.play().catch(() => undefined);
20
- };
21
-
22
- attach();
23
- const timer = window.setInterval(attach, 120);
24
- return () => {
25
- cancelled = true;
26
- window.clearInterval(timer);
27
- audio.pause();
28
- audio.srcObject = null;
29
- };
30
- }, [getRemoteAudioStream, isVoiceActive, isVoiceChannelReady]);
31
-
32
- if (!isVoiceActive) return null;
33
- return <audio ref={audioRef} autoPlay playsInline style={{ display: 'none' }} />;
34
- }