@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.0 → 0.4.2

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 (146) hide show
  1. package/dist/context/ChatBotContext.d.ts.map +1 -1
  2. package/dist/context/ChatBotContext.js +84 -405
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/permissionState.d.ts +53 -0
  7. package/dist/permissionState.d.ts.map +1 -0
  8. package/dist/permissionState.js +217 -0
  9. package/dist/types/ChatBotTypes.d.ts +0 -5
  10. package/dist/types/ChatBotTypes.d.ts.map +1 -1
  11. package/dist/ui/ChatBotHeader.d.ts.map +1 -1
  12. package/dist/ui/ChatBotHeader.js +6 -2
  13. package/dist/ui/ChatBotUI.d.ts.map +1 -1
  14. package/dist/ui/ChatBotUI.js +13 -4
  15. package/dist/ui/MessageList.d.ts.map +1 -1
  16. package/dist/ui/MessageList.js +11 -6
  17. package/dist/ui/PermissionForm.d.ts.map +1 -1
  18. package/dist/ui/PermissionForm.js +6 -9
  19. package/dist/ui/styles/isolatedStyles.d.ts.map +1 -1
  20. package/dist/ui/styles/isolatedStyles.js +1 -0
  21. package/package.json +3 -3
  22. package/src/context/ChatBotContext.tsx +92 -437
  23. package/src/index.ts +5 -0
  24. package/src/types/ChatBotTypes.ts +2 -6
  25. package/src/ui/ChatBotHeader.tsx +9 -3
  26. package/src/ui/ChatBotUI.tsx +14 -5
  27. package/src/ui/MessageList.tsx +6 -0
  28. package/src/ui/PermissionForm.tsx +7 -12
  29. package/src/ui/styles/isolatedStyles.ts +1 -0
  30. package/dist/NxtlinqAgentChat.d.ts +0 -26
  31. package/dist/NxtlinqAgentChat.d.ts.map +0 -1
  32. package/dist/NxtlinqAgentChat.js +0 -28
  33. package/dist/components/AgentAssistantShell.d.ts +0 -5
  34. package/dist/components/AgentAssistantShell.d.ts.map +0 -1
  35. package/dist/components/AgentAssistantShell.js +0 -52
  36. package/dist/components/AgentComposer.d.ts +0 -3
  37. package/dist/components/AgentComposer.d.ts.map +0 -1
  38. package/dist/components/AgentComposer.js +0 -60
  39. package/dist/components/AgentMessageList.d.ts +0 -3
  40. package/dist/components/AgentMessageList.d.ts.map +0 -1
  41. package/dist/components/AgentMessageList.js +0 -37
  42. package/dist/components/AgentRemoteAudio.d.ts +0 -4
  43. package/dist/components/AgentRemoteAudio.d.ts.map +0 -1
  44. package/dist/components/AgentRemoteAudio.js +0 -34
  45. package/dist/components/AgentVoiceBar.d.ts +0 -3
  46. package/dist/components/AgentVoiceBar.d.ts.map +0 -1
  47. package/dist/components/AgentVoiceBar.js +0 -91
  48. package/dist/components/PresetMessageChips.d.ts +0 -3
  49. package/dist/components/PresetMessageChips.d.ts.map +0 -1
  50. package/dist/components/PresetMessageChips.js +0 -23
  51. package/dist/context/AgentAssistantContext.d.ts +0 -32
  52. package/dist/context/AgentAssistantContext.d.ts.map +0 -1
  53. package/dist/context/AgentAssistantContext.js +0 -179
  54. package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts +0 -2
  55. package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts.map +0 -1
  56. package/dist/legacy/assets/images/adiSideItalicDataUri.js +0 -1
  57. package/dist/legacy/chatbot/ChatBot.d.ts +0 -5
  58. package/dist/legacy/chatbot/ChatBot.d.ts.map +0 -1
  59. package/dist/legacy/chatbot/ChatBot.js +0 -35
  60. package/dist/legacy/chatbot/context/ChatBotContext.d.ts +0 -5
  61. package/dist/legacy/chatbot/context/ChatBotContext.d.ts.map +0 -1
  62. package/dist/legacy/chatbot/context/ChatBotContext.js +0 -2922
  63. package/dist/legacy/chatbot/types/ChatBotTypes.d.ts +0 -166
  64. package/dist/legacy/chatbot/types/ChatBotTypes.d.ts.map +0 -1
  65. package/dist/legacy/chatbot/types/ChatBotTypes.js +0 -1
  66. package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts +0 -17
  67. package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts.map +0 -1
  68. package/dist/legacy/chatbot/ui/BerifyMeModal.js +0 -110
  69. package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts +0 -15
  70. package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts.map +0 -1
  71. package/dist/legacy/chatbot/ui/ChatBotHeader.js +0 -62
  72. package/dist/legacy/chatbot/ui/ChatBotUI.d.ts +0 -3
  73. package/dist/legacy/chatbot/ui/ChatBotUI.d.ts.map +0 -1
  74. package/dist/legacy/chatbot/ui/ChatBotUI.js +0 -557
  75. package/dist/legacy/chatbot/ui/MessageInput.d.ts +0 -3
  76. package/dist/legacy/chatbot/ui/MessageInput.d.ts.map +0 -1
  77. package/dist/legacy/chatbot/ui/MessageInput.js +0 -321
  78. package/dist/legacy/chatbot/ui/MessageList.d.ts +0 -4
  79. package/dist/legacy/chatbot/ui/MessageList.d.ts.map +0 -1
  80. package/dist/legacy/chatbot/ui/MessageList.js +0 -455
  81. package/dist/legacy/chatbot/ui/ModelSelector.d.ts +0 -4
  82. package/dist/legacy/chatbot/ui/ModelSelector.d.ts.map +0 -1
  83. package/dist/legacy/chatbot/ui/ModelSelector.js +0 -122
  84. package/dist/legacy/chatbot/ui/NotificationModal.d.ts +0 -15
  85. package/dist/legacy/chatbot/ui/NotificationModal.d.ts.map +0 -1
  86. package/dist/legacy/chatbot/ui/NotificationModal.js +0 -53
  87. package/dist/legacy/chatbot/ui/PermissionForm.d.ts +0 -8
  88. package/dist/legacy/chatbot/ui/PermissionForm.d.ts.map +0 -1
  89. package/dist/legacy/chatbot/ui/PermissionForm.js +0 -465
  90. package/dist/legacy/chatbot/ui/PresetMessages.d.ts +0 -4
  91. package/dist/legacy/chatbot/ui/PresetMessages.d.ts.map +0 -1
  92. package/dist/legacy/chatbot/ui/PresetMessages.js +0 -33
  93. package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts +0 -3
  94. package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts.map +0 -1
  95. package/dist/legacy/chatbot/ui/VoiceModePanel.js +0 -95
  96. package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts +0 -15
  97. package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts.map +0 -1
  98. package/dist/legacy/chatbot/ui/chatBotHeaderParts.js +0 -50
  99. package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts +0 -73
  100. package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts.map +0 -1
  101. package/dist/legacy/chatbot/ui/styles/isolatedStyles.js +0 -985
  102. package/dist/legacy/index.d.ts +0 -14
  103. package/dist/legacy/index.d.ts.map +0 -1
  104. package/dist/legacy/index.js +0 -12
  105. package/dist/theme/defaultTheme.d.ts +0 -3
  106. package/dist/theme/defaultTheme.d.ts.map +0 -1
  107. package/dist/theme/defaultTheme.js +0 -20
  108. package/dist/types.d.ts +0 -62
  109. package/dist/types.d.ts.map +0 -1
  110. package/dist/types.js +0 -1
  111. package/dist/voice/useVoiceConnectOrchestration.d.ts +0 -21
  112. package/dist/voice/useVoiceConnectOrchestration.d.ts.map +0 -1
  113. package/dist/voice/useVoiceConnectOrchestration.js +0 -86
  114. package/dist/voice/useVoiceMicState.d.ts +0 -15
  115. package/dist/voice/useVoiceMicState.d.ts.map +0 -1
  116. package/dist/voice/useVoiceMicState.js +0 -94
  117. package/dist/voice/useVoiceSilenceCommit.d.ts +0 -10
  118. package/dist/voice/useVoiceSilenceCommit.d.ts.map +0 -1
  119. package/dist/voice/useVoiceSilenceCommit.js +0 -67
  120. package/dist/voice/useVoiceTranscriptMessages.d.ts +0 -18
  121. package/dist/voice/useVoiceTranscriptMessages.d.ts.map +0 -1
  122. package/dist/voice/useVoiceTranscriptMessages.js +0 -132
  123. package/dist/voice/useWsRealtimeAudio.d.ts +0 -18
  124. package/dist/voice/useWsRealtimeAudio.d.ts.map +0 -1
  125. package/dist/voice/useWsRealtimeAudio.js +0 -102
  126. package/dist/voice/voiceMicConstants.d.ts +0 -4
  127. package/dist/voice/voiceMicConstants.d.ts.map +0 -1
  128. package/dist/voice/voiceMicConstants.js +0 -10
  129. package/dist/voice/voiceUserBubble.d.ts +0 -10
  130. package/dist/voice/voiceUserBubble.d.ts.map +0 -1
  131. package/dist/voice/voiceUserBubble.js +0 -52
  132. package/dist/voice/ws/BrowserWsPcmPlayer.d.ts +0 -23
  133. package/dist/voice/ws/BrowserWsPcmPlayer.d.ts.map +0 -1
  134. package/dist/voice/ws/BrowserWsPcmPlayer.js +0 -137
  135. package/dist/voice/ws/BrowserWsPcmRecorder.d.ts +0 -17
  136. package/dist/voice/ws/BrowserWsPcmRecorder.d.ts.map +0 -1
  137. package/dist/voice/ws/BrowserWsPcmRecorder.js +0 -71
  138. package/dist/voice/ws/float32ToPcm16.d.ts +0 -2
  139. package/dist/voice/ws/float32ToPcm16.d.ts.map +0 -1
  140. package/dist/voice/ws/float32ToPcm16.js +0 -8
  141. package/dist/voice/ws/voiceSilenceConstants.d.ts +0 -5
  142. package/dist/voice/ws/voiceSilenceConstants.d.ts.map +0 -1
  143. package/dist/voice/ws/voiceSilenceConstants.js +0 -4
  144. package/dist/voice/ws/wsRealtimeConstants.d.ts +0 -2
  145. package/dist/voice/ws/wsRealtimeConstants.d.ts.map +0 -1
  146. package/dist/voice/ws/wsRealtimeConstants.js +0 -1
@@ -121,21 +121,27 @@ export const ChatBotHeader: React.FC<ChatBotHeaderProps> = ({
121
121
  z-index: 2 !important;
122
122
  display: flex !important;
123
123
  justify-content: space-between !important;
124
- align-items: center !important;
124
+ align-items: flex-start !important;
125
125
  width: 100% !important;
126
126
  pointer-events: none !important;
127
127
  `}>
128
128
  <div css={css`
129
129
  display: flex !important;
130
130
  align-items: center !important;
131
+ flex-wrap: wrap !important;
132
+ row-gap: 8px !important;
131
133
  gap: 10px !important;
132
134
  min-width: 0 !important;
135
+ flex: 1 1 auto !important;
136
+ padding-top: 2px !important;
133
137
  pointer-events: none !important;
134
138
  `}>
135
- <h3 css={headerTitle}>AI Agent</h3>
139
+ <h3 css={[headerTitle, css`flex-shrink: 0 !important;`]}>AI Agent</h3>
136
140
  {metaChips}
137
141
  </div>
138
- {actions}
142
+ <div css={css`flex-shrink: 0 !important;`}>
143
+ {actions}
144
+ </div>
139
145
  </div>
140
146
  )}
141
147
  </div>
@@ -1,6 +1,7 @@
1
1
  /** @jsxImportSource @emotion/react */
2
2
  import { css } from '@emotion/react';
3
3
  import * as React from 'react';
4
+ import { hasRecordedWalletVerification } from '@bytexbyte/nxtlinq-ai-agent-core-development';
4
5
  import { useDraggable, useLocalStorage, useResizable, walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
5
6
  import { useChatBot } from '../context/ChatBotContext';
6
7
  import { ChatBotHeader } from './ChatBotHeader';
@@ -33,6 +34,12 @@ import {
33
34
  const MOBILE_BREAKPOINT = 768;
34
35
  const MOBILE_EDGE_MARGIN = 12;
35
36
  const MOBILE_FAB_POSITION = { right: MOBILE_EDGE_MARGIN, bottom: MOBILE_EDGE_MARGIN } as const;
37
+ // Below this widget width, the desktop header's single-row layout (title + model/PII chips +
38
+ // voice/settings/close actions, all in one line) has no room left and the chips start
39
+ // overlapping the actions. The user can drag the window this narrow (MIN_WIDTH is 320px) even
40
+ // on a wide desktop viewport, so this must be driven by the widget's own width, not just
41
+ // window.innerWidth — falling back to the same stacked layout used on mobile viewports.
42
+ const HEADER_COMPACT_WIDTH = 420;
36
43
 
37
44
  const isMobileViewport = (): boolean =>
38
45
  typeof window !== 'undefined' && window.innerWidth <= MOBILE_BREAKPOINT;
@@ -123,6 +130,7 @@ export const ChatBotUI: React.FC = () => {
123
130
  const { position, handleDragStart, isDragging, updatePosition } = useDraggable(dimensions);
124
131
 
125
132
  const [mobileLayout, setMobileLayout] = React.useState(isMobileViewport);
133
+ const headerCompactLayout = mobileLayout || dimensions.width < HEADER_COMPACT_WIDTH;
126
134
  React.useEffect(() => {
127
135
  if (typeof window === 'undefined') return;
128
136
  const mq = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`);
@@ -154,7 +162,8 @@ export const ChatBotUI: React.FC = () => {
154
162
  const urlParams = new URLSearchParams(window.location.search);
155
163
  const hasBerifymeToken = urlParams.get('token') && urlParams.get('method') === 'berifyme';
156
164
 
157
- const isWalletVerifiedWithBerifyme = walletInfo?.id && walletInfo?.method === 'berifyme';
165
+ const hasRecordedVerification = Boolean(walletInfo?.id)
166
+ && hasRecordedWalletVerification(walletInfo);
158
167
 
159
168
  // Helper function to update IDV suggestion state based on storage value
160
169
  const updateIDVSuggestionState = React.useCallback((dismissedValue: string | null) => {
@@ -214,7 +223,7 @@ export const ChatBotUI: React.FC = () => {
214
223
  const shouldShowBanner = hitAddress &&
215
224
  !props.requireWalletIDVVerification &&
216
225
  !hasBerifymeToken &&
217
- !isWalletVerifiedWithBerifyme &&
226
+ !hasRecordedVerification &&
218
227
  !isNeedSignInWithWallet;
219
228
 
220
229
  if (shouldShowBanner) {
@@ -222,7 +231,7 @@ export const ChatBotUI: React.FC = () => {
222
231
  const shouldShowBannerAfterDelay = hitAddress &&
223
232
  !props.requireWalletIDVVerification &&
224
233
  !hasBerifymeToken &&
225
- !isWalletVerifiedWithBerifyme &&
234
+ !hasRecordedVerification &&
226
235
  !isNeedSignInWithWallet;
227
236
 
228
237
  if (shouldShowBannerAfterDelay) {
@@ -709,7 +718,7 @@ export const ChatBotUI: React.FC = () => {
709
718
  )}
710
719
 
711
720
  <ChatBotHeader
712
- mobileLayout={mobileLayout}
721
+ mobileLayout={headerCompactLayout}
713
722
  isDragging={isDragging}
714
723
  onDragStart={handleDragStart}
715
724
  isVoiceMode={isVoiceMode}
@@ -721,7 +730,7 @@ export const ChatBotUI: React.FC = () => {
721
730
  onClose={handleClose}
722
731
  />
723
732
 
724
- {showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !isWalletVerifiedWithBerifyme && (
733
+ {showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !hasRecordedVerification && (
725
734
  <div
726
735
  data-idv-banner
727
736
  css={idvBanner}>
@@ -330,6 +330,8 @@ export const MessageList: React.FC = () => {
330
330
  availableModels,
331
331
  serviceId,
332
332
  piiDisplayMode,
333
+ setShowPermissionForm,
334
+ setIsPermissionFormOpen,
333
335
  } = useChatBot();
334
336
  const messagesEndRef = React.useRef<HTMLDivElement>(null);
335
337
 
@@ -367,6 +369,9 @@ export const MessageList: React.FC = () => {
367
369
  }
368
370
  }
369
371
  }
372
+ } else if (buttonType === 'verifyWallet') {
373
+ setIsPermissionFormOpen(true);
374
+ setShowPermissionForm(true);
370
375
  } else if (buttonType === 'continue') {
371
376
  const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
372
377
  if (lastUserMsg && lastUserMsg.content) {
@@ -672,6 +677,7 @@ export const MessageList: React.FC = () => {
672
677
  {isAutoConnecting ? 'Connecting...' :
673
678
  message.button === 'connectWallet' ? (Boolean(hitAddress) ? 'Connected' : walletTextUtils.getWalletText('Connect Wallet', serviceId)) :
674
679
  message.button === 'signIn' ? (!isNeedSignInWithWallet ? 'Signed In' : 'Sign In') :
680
+ message.button === 'verifyWallet' ? 'Verify Wallet' :
675
681
  message.button === 'continue' ? 'Continue' :
676
682
  message.button === 'enableAIT' ?
677
683
  ((isAITLoading || isAITEnabling) ? 'Enabling...' :
@@ -3,6 +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 { hasRecordedWalletVerification } from '@bytexbyte/nxtlinq-ai-agent-core-development';
6
7
  import { actionButton, modalOverlay } from './styles/isolatedStyles';
7
8
 
8
9
  interface PermissionFormProps {
@@ -103,13 +104,10 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
103
104
 
104
105
  // Check if wallet IDV verification is required based on configuration
105
106
  const requireWalletIDVVerification = props.requireWalletIDVVerification ?? true; // Default to true
106
- const isWalletVerified = Boolean(walletInfo?.id);
107
- const isWalletVerifiedWithBerifyme = walletInfo?.method === 'berifyme';
108
-
109
- // Show verification prompt if:
110
- // 1. IDV verification is required AND wallet is not verified, OR
111
- // 2. IDV verification is required AND wallet is verified but not with Berifyme
112
- const shouldShowVerificationPrompt = requireWalletIDVVerification && (!isWalletVerified || !isWalletVerifiedWithBerifyme);
107
+ // Any recorded verification source is valid. Custom additionally requires
108
+ // its explicit username evidence so synthetic AIT-provisioned rows do not count.
109
+ const shouldShowVerificationPrompt = requireWalletIDVVerification
110
+ && (!walletInfo?.id || !hasRecordedWalletVerification(walletInfo));
113
111
 
114
112
  const handleSave = async () => {
115
113
  setIsSaving(true);
@@ -415,10 +413,7 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
415
413
  font-size: 16px !important;
416
414
  color: #666 !important;
417
415
  `}>
418
- {isWalletVerified && !isWalletVerifiedWithBerifyme
419
- ? walletTextUtils.getWalletText('Your wallet is verified with custom method, but Berify.me verification is required to continue.', serviceId)
420
- : walletTextUtils.getWalletText('Please verify your wallet with Berify.me to continue', serviceId)
421
- }
416
+ {walletTextUtils.getWalletText('Please verify your wallet identity to continue', serviceId)}
422
417
  </p>
423
418
  <button
424
419
  onClick={() => onVerifyWallet('berifyme')}
@@ -629,4 +624,4 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
629
624
  )}
630
625
  </div>
631
626
  );
632
- };
627
+ };
@@ -252,6 +252,7 @@ export const headerTitle = css`
252
252
  font-weight: 600 !important;
253
253
  color: white !important;
254
254
  font-family: inherit !important;
255
+ white-space: nowrap !important;
255
256
  `;
256
257
 
257
258
  // Header button styles
@@ -1,26 +0,0 @@
1
- import React from 'react';
2
- import type { NxtlinqAgentChatProps } from './types';
3
- /**
4
- * Drop-in React Web assistant UI wired to `@bytexbyte/nxtlinq-ai-agent-web-development`.
5
- *
6
- * @example
7
- * ```tsx
8
- * import { NxtlinqAgentChat } from '@bytexbyte/nxtlinq-ai-agent-ui-react-development';
9
- *
10
- * export default function Page() {
11
- * return (
12
- * <NxtlinqAgentChat
13
- * style={{ height: '100vh' }}
14
- * serviceId="..."
15
- * apiKey="..."
16
- * apiSecret="..."
17
- * environment="staging"
18
- * pseudoId={userId}
19
- * loadHistoryOnMount
20
- * />
21
- * );
22
- * }
23
- * ```
24
- */
25
- export declare function NxtlinqAgentChat({ title, placeholder, presetMessages, loadHistoryOnMount, historyLast, enableVoice, enableFileUpload, startInVoiceMode, startWithMicMuted, holdMicDuringAssistant, theme, style, headerStyle, onMessage, onError, onToolUse, children, fetchImpl, getTimezone, resetOnIdentityChange, ...agentConfig }: NxtlinqAgentChatProps): React.ReactElement;
26
- //# sourceMappingURL=NxtlinqAgentChat.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NxtlinqAgentChat.d.ts","sourceRoot":"","sources":["../src/NxtlinqAgentChat.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,WAAW,EACX,cAAc,EACd,kBAA0B,EAC1B,WAAW,EACX,WAAkB,EAClB,gBAAuB,EACvB,gBAAwB,EACxB,iBAAwB,EACxB,sBAA6B,EAC7B,KAAK,EACL,KAAK,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,SAAS,EACT,WAAW,EACX,qBAAqB,EACrB,GAAG,WAAW,EACf,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CA6B5C"}
@@ -1,28 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
- import { NxtlinqAgentProvider } from '@bytexbyte/nxtlinq-ai-agent-web-development';
3
- import { AgentAssistantShell } from './components/AgentAssistantShell';
4
- /**
5
- * Drop-in React Web assistant UI wired to `@bytexbyte/nxtlinq-ai-agent-web-development`.
6
- *
7
- * @example
8
- * ```tsx
9
- * import { NxtlinqAgentChat } from '@bytexbyte/nxtlinq-ai-agent-ui-react-development';
10
- *
11
- * export default function Page() {
12
- * return (
13
- * <NxtlinqAgentChat
14
- * style={{ height: '100vh' }}
15
- * serviceId="..."
16
- * apiKey="..."
17
- * apiSecret="..."
18
- * environment="staging"
19
- * pseudoId={userId}
20
- * loadHistoryOnMount
21
- * />
22
- * );
23
- * }
24
- * ```
25
- */
26
- export function NxtlinqAgentChat({ title, placeholder, presetMessages, loadHistoryOnMount = false, historyLast, enableVoice = true, enableFileUpload = true, startInVoiceMode = false, startWithMicMuted = true, holdMicDuringAssistant = true, theme, style, headerStyle, onMessage, onError, onToolUse, children, fetchImpl, getTimezone, resetOnIdentityChange, ...agentConfig }) {
27
- return (_jsxs(NxtlinqAgentProvider, { fetchImpl: fetchImpl, getTimezone: getTimezone, resetOnIdentityChange: resetOnIdentityChange, onMessage: onMessage, onError: onError, onToolUse: onToolUse, ...agentConfig, children: [_jsx(AgentAssistantShell, { title: title, placeholder: placeholder, presetMessages: presetMessages, loadHistoryOnMount: loadHistoryOnMount, historyLast: historyLast, enableVoice: enableVoice, enableFileUpload: enableFileUpload, startInVoiceMode: startInVoiceMode, startWithMicMuted: startWithMicMuted, holdMicDuringAssistant: holdMicDuringAssistant, theme: theme, style: style, headerStyle: headerStyle }), children] }));
28
- }
@@ -1,5 +0,0 @@
1
- import React from 'react';
2
- import type { NxtlinqAgentChatProps } from '../types';
3
- export type AgentAssistantShellProps = Pick<NxtlinqAgentChatProps, 'title' | 'placeholder' | 'presetMessages' | 'enableVoice' | 'enableFileUpload' | 'theme' | 'style' | 'headerStyle' | 'loadHistoryOnMount' | 'historyLast' | 'startInVoiceMode' | 'startWithMicMuted' | 'holdMicDuringAssistant'>;
4
- export declare function AgentAssistantShell(props: AgentAssistantShellProps): React.ReactElement;
5
- //# sourceMappingURL=AgentAssistantShell.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AgentAssistantShell.d.ts","sourceRoot":"","sources":["../../src/components/AgentAssistantShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAK3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAOtD,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,qBAAqB,EACnB,OAAO,GACP,aAAa,GACb,gBAAgB,GAChB,aAAa,GACb,kBAAkB,GAClB,OAAO,GACP,OAAO,GACP,aAAa,GACb,oBAAoB,GACpB,aAAa,GACb,kBAAkB,GAClB,mBAAmB,GACnB,wBAAwB,CAC3B,CAAC;AA2DF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,KAAK,CAAC,YAAY,CAiBvF"}
@@ -1,52 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
- import { useEffect, useRef, useState } from 'react';
3
- import { AgentAssistantProvider, useAgentAssistant, } from '../context/AgentAssistantContext';
4
- import { AgentComposer } from './AgentComposer';
5
- import { AgentMessageList } from './AgentMessageList';
6
- import { AgentRemoteAudio } from './AgentRemoteAudio';
7
- import { AgentVoiceBar } from './AgentVoiceBar';
8
- import { PresetMessageChips } from './PresetMessageChips';
9
- function AgentAssistantInner({ title, headerStyle, style, loadHistoryOnMount, historyLast, startInVoiceMode, }) {
10
- const { theme, loadHistory, startVoice, isVoiceAvailable } = useAgentAssistant();
11
- const [historyReady, setHistoryReady] = useState(!loadHistoryOnMount);
12
- const voiceAutoStartRef = useRef(false);
13
- useEffect(() => {
14
- if (loadHistoryOnMount) {
15
- void loadHistory({ last: historyLast ?? 50 }).finally(() => setHistoryReady(true));
16
- }
17
- }, [loadHistory, loadHistoryOnMount, historyLast]);
18
- useEffect(() => {
19
- if (!startInVoiceMode || !isVoiceAvailable || voiceAutoStartRef.current)
20
- return;
21
- voiceAutoStartRef.current = true;
22
- void startVoice();
23
- }, [startInVoiceMode, isVoiceAvailable, startVoice]);
24
- return (_jsxs("div", { style: {
25
- display: 'flex',
26
- flexDirection: 'column',
27
- flex: 1,
28
- minHeight: 0,
29
- backgroundColor: theme.colors.background,
30
- ...style,
31
- }, children: [_jsx("header", { style: {
32
- padding: theme.spacing.md,
33
- borderBottom: `1px solid ${theme.colors.border}`,
34
- backgroundColor: theme.colors.surface,
35
- fontSize: theme.typography.titleSize,
36
- fontWeight: 600,
37
- color: theme.colors.assistantText,
38
- ...headerStyle,
39
- }, children: title ?? 'AI Assistant' }), _jsx(PresetMessageChips, {}), historyReady ? _jsx(AgentMessageList, {}) : _jsx("div", { style: { flex: 1 } }), _jsx(AgentVoiceBar, {}), _jsx(AgentComposer, {}), _jsx(AgentRemoteAudio, {})] }));
40
- }
41
- export function AgentAssistantShell(props) {
42
- return (_jsx(AgentAssistantProvider, { ui: {
43
- title: props.title,
44
- placeholder: props.placeholder,
45
- presetMessages: props.presetMessages,
46
- enableVoice: props.enableVoice,
47
- enableFileUpload: props.enableFileUpload,
48
- theme: props.theme,
49
- startWithMicMuted: props.startWithMicMuted,
50
- holdMicDuringAssistant: props.holdMicDuringAssistant,
51
- }, children: _jsx(AgentAssistantInner, { ...props }) }));
52
- }
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export declare function AgentComposer(): React.ReactElement | null;
3
- //# sourceMappingURL=AgentComposer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AgentComposer.d.ts","sourceRoot":"","sources":["../../src/components/AgentComposer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,wBAAgB,aAAa,IAAI,KAAK,CAAC,YAAY,GAAG,IAAI,CAkIzD"}
@@ -1,60 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
- import React from 'react';
3
- import { useAgentAssistant } from '../context/AgentAssistantContext';
4
- export function AgentComposer() {
5
- const { theme, placeholder, inputText, setInputText, sendText, isLoading, isVoiceAvailable, interactionMode, startVoice, isVoiceConnecting, enableFileUpload, sendMessage, } = useAgentAssistant();
6
- const fileInputRef = React.useRef(null);
7
- if (interactionMode === 'voice') {
8
- return null;
9
- }
10
- const handleFiles = async (files) => {
11
- if (!files?.length)
12
- return;
13
- const { fileToAttachment } = await import('@bytexbyte/nxtlinq-ai-agent-web-development');
14
- const attachments = await Promise.all(Array.from(files).map((file) => fileToAttachment(file)));
15
- await sendMessage(attachments.length ? `Uploaded ${attachments.length} file(s)` : '', { attachments });
16
- if (fileInputRef.current)
17
- fileInputRef.current.value = '';
18
- };
19
- return (_jsxs("div", { style: {
20
- borderTop: `1px solid ${theme.colors.border}`,
21
- backgroundColor: theme.colors.surface,
22
- padding: theme.spacing.md,
23
- }, children: [_jsxs("div", { style: { display: 'flex', gap: theme.spacing.sm, alignItems: 'flex-end' }, children: [enableFileUpload ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => fileInputRef.current?.click(), disabled: isLoading, style: {
24
- width: 36,
25
- height: 36,
26
- borderRadius: theme.radius.button,
27
- border: `1px solid ${theme.colors.border}`,
28
- backgroundColor: theme.colors.surface,
29
- cursor: 'pointer',
30
- }, "aria-label": "Upload file", children: "+" }), _jsx("input", { ref: fileInputRef, type: "file", multiple: true, accept: "image/*,.pdf,.doc,.docx,.txt", style: { display: 'none' }, onChange: (e) => void handleFiles(e.target.files) })] })) : null, _jsx("textarea", { value: inputText, onChange: (e) => setInputText(e.target.value), placeholder: placeholder, rows: 2, disabled: isLoading, maxLength: 4000, style: {
31
- flex: 1,
32
- resize: 'none',
33
- border: `1px solid ${theme.colors.border}`,
34
- borderRadius: theme.radius.panel,
35
- padding: theme.spacing.sm,
36
- fontSize: theme.typography.bodySize,
37
- color: theme.colors.assistantText,
38
- } }), _jsx("button", { type: "button", onClick: () => void sendText(), disabled: isLoading || !inputText.trim(), style: {
39
- backgroundColor: theme.colors.primary,
40
- color: theme.colors.primaryText,
41
- border: 'none',
42
- borderRadius: theme.radius.button,
43
- padding: `${theme.spacing.sm}px ${theme.spacing.md}px`,
44
- fontWeight: 600,
45
- opacity: isLoading || !inputText.trim() ? 0.5 : 1,
46
- cursor: 'pointer',
47
- }, children: "Send" })] }), isVoiceAvailable ? (_jsx("button", { type: "button", onClick: () => {
48
- startVoice().catch((err) => {
49
- console.warn('[nxtlinq] startVoice failed:', err);
50
- });
51
- }, disabled: isVoiceConnecting, style: {
52
- display: 'block',
53
- margin: `${theme.spacing.sm}px auto 0`,
54
- border: 'none',
55
- background: 'none',
56
- color: theme.colors.primary,
57
- fontSize: theme.typography.captionSize,
58
- cursor: 'pointer',
59
- }, children: isVoiceConnecting ? 'Connecting voice…' : 'Switch to voice mode' })) : null] }));
60
- }
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export declare function AgentMessageList(): React.ReactElement;
3
- //# sourceMappingURL=AgentMessageList.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AgentMessageList.d.ts","sourceRoot":"","sources":["../../src/components/AgentMessageList.tsx"],"names":[],"mappings":"AACA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAyCjD,wBAAgB,gBAAgB,IAAI,KAAK,CAAC,YAAY,CAmCrD"}
@@ -1,37 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
- import { useEffect, useRef } from 'react';
3
- import { useAgentAssistant } from '../context/AgentAssistantContext';
4
- function MessageBubble({ message }) {
5
- const { theme } = useAgentAssistant();
6
- const isUser = message.role === 'user';
7
- const displayText = message.partialContent && message.isStreaming
8
- ? message.partialContent
9
- : message.content;
10
- return (_jsx("div", { style: {
11
- display: 'flex',
12
- justifyContent: isUser ? 'flex-end' : 'flex-start',
13
- marginBottom: theme.spacing.sm,
14
- }, children: _jsxs("div", { style: {
15
- maxWidth: '85%',
16
- padding: `${theme.spacing.sm}px ${theme.spacing.md}px`,
17
- borderRadius: theme.radius.bubble,
18
- backgroundColor: isUser ? theme.colors.userBubble : theme.colors.assistantBubble,
19
- color: isUser ? theme.colors.userText : theme.colors.assistantText,
20
- fontSize: theme.typography.bodySize,
21
- whiteSpace: 'pre-wrap',
22
- }, children: [displayText || ' ', message.error ? (_jsx("div", { style: { color: theme.colors.error, fontSize: theme.typography.captionSize }, children: message.error })) : null] }) }));
23
- }
24
- export function AgentMessageList() {
25
- const { messages, isLoading, theme } = useAgentAssistant();
26
- const endRef = useRef(null);
27
- useEffect(() => {
28
- endRef.current?.scrollIntoView({ behavior: 'smooth' });
29
- }, [messages.length, messages[messages.length - 1]?.content, messages[messages.length - 1]?.partialContent]);
30
- return (_jsxs("div", { style: {
31
- flex: 1,
32
- minHeight: 0,
33
- overflowY: 'auto',
34
- padding: theme.spacing.md,
35
- backgroundColor: theme.colors.background,
36
- }, children: [messages.length === 0 ? (_jsx("p", { style: { textAlign: 'center', color: theme.colors.mutedText, marginTop: 40 }, children: "Send a message to start the conversation." })) : (messages.map((message) => (_jsx(MessageBubble, { message: message }, message.id)))), isLoading ? (_jsx("p", { style: { color: theme.colors.mutedText, fontSize: theme.typography.captionSize }, children: "Thinking\u2026" })) : null, _jsx("div", { ref: endRef })] }));
37
- }
@@ -1,4 +0,0 @@
1
- import React from 'react';
2
- /** Plays assistant WebRTC remote audio with retry until stream is ready. */
3
- export declare function AgentRemoteAudio(): React.ReactElement | null;
4
- //# sourceMappingURL=AgentRemoteAudio.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AgentRemoteAudio.d.ts","sourceRoot":"","sources":["../../src/components/AgentRemoteAudio.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAGjD,4EAA4E;AAC5E,wBAAgB,gBAAgB,IAAI,KAAK,CAAC,YAAY,GAAG,IAAI,CA6B5D"}
@@ -1,34 +0,0 @@
1
- import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
- import { useEffect, useRef } from 'react';
3
- import { useAgentAssistant } from '../context/AgentAssistantContext';
4
- /** Plays assistant WebRTC remote audio with retry until stream is ready. */
5
- export function AgentRemoteAudio() {
6
- const { getRemoteAudioStream, isVoiceActive, isVoiceChannelReady } = useAgentAssistant();
7
- const audioRef = useRef(null);
8
- useEffect(() => {
9
- const audio = audioRef.current;
10
- if (!audio || !isVoiceActive)
11
- return;
12
- let cancelled = false;
13
- const attach = () => {
14
- if (cancelled)
15
- return;
16
- const stream = getRemoteAudioStream();
17
- if (!stream)
18
- return;
19
- audio.srcObject = stream;
20
- void audio.play().catch(() => undefined);
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
- if (!isVoiceActive)
32
- return null;
33
- return _jsx("audio", { ref: audioRef, autoPlay: true, playsInline: true, style: { display: 'none' } });
34
- }
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export declare function AgentVoiceBar(): React.ReactElement | null;
3
- //# sourceMappingURL=AgentVoiceBar.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AgentVoiceBar.d.ts","sourceRoot":"","sources":["../../src/components/AgentVoiceBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAsC3C,wBAAgB,aAAa,IAAI,KAAK,CAAC,YAAY,GAAG,IAAI,CAsIzD"}
@@ -1,91 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
- import { useCallback } from 'react';
3
- import { useAgentAssistant, voiceStatusLabel, } from '../context/AgentAssistantContext';
4
- function statusDotColor(status, theme) {
5
- switch (status) {
6
- case 'listening':
7
- return theme.colors.voiceActive;
8
- case 'speaking':
9
- return theme.colors.voiceSpeaking;
10
- case 'idle':
11
- return theme.colors.mutedText;
12
- default:
13
- return theme.colors.primary;
14
- }
15
- }
16
- function MicIcon({ color }) {
17
- return (_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: color, "aria-hidden": true, children: _jsx("path", { d: "M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5-3c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z" }) }));
18
- }
19
- function MicOffIcon() {
20
- return (_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "#ef4444", "aria-hidden": true, children: _jsx("path", { d: "M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3s-3 1.34-3 3v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z" }) }));
21
- }
22
- export function AgentVoiceBar() {
23
- const { theme, interactionMode, voiceStatus, voiceSessionId, isVoiceConnecting, isVoiceChannelReady, stopVoice, isVoiceAvailable, isMicMuted, isMicHeldForAssistant, toggleVoiceMicMute, } = useAgentAssistant();
24
- const returnToTextMode = useCallback(() => {
25
- void stopVoice();
26
- }, [stopVoice]);
27
- if (!isVoiceAvailable)
28
- return null;
29
- if (interactionMode !== 'voice' && !isVoiceConnecting)
30
- return null;
31
- const showConnecting = isVoiceConnecting;
32
- const awaitingChannel = Boolean(voiceSessionId) &&
33
- !isVoiceConnecting &&
34
- !isVoiceChannelReady &&
35
- voiceStatus === 'idle';
36
- const statusHint = showConnecting
37
- ? 'Tap Back to text mode below to cancel'
38
- : awaitingChannel
39
- ? 'Waiting for voice channel…'
40
- : isMicHeldForAssistant
41
- ? 'Assistant is responding — tap mic to interrupt and speak'
42
- : isMicMuted
43
- ? 'Mic is off — tap the mic when ready to speak'
44
- : voiceStatus === 'listening'
45
- ? 'Start speaking'
46
- : voiceStatus === 'speaking'
47
- ? 'Assistant is speaking…'
48
- : voiceStatus === 'thinking'
49
- ? 'Thinking…'
50
- : '';
51
- return (_jsxs("div", { style: {
52
- borderTop: `1px solid ${theme.colors.border}`,
53
- backgroundColor: theme.colors.surface,
54
- padding: theme.spacing.md,
55
- }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10 }, children: [_jsxs("div", { style: {
56
- flex: 1,
57
- minWidth: 0,
58
- border: `1px solid ${theme.colors.border}`,
59
- borderRadius: 12,
60
- backgroundColor: theme.colors.background,
61
- padding: '8px 10px',
62
- }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [_jsx("span", { style: {
63
- width: 8,
64
- height: 8,
65
- borderRadius: 4,
66
- backgroundColor: statusDotColor(voiceStatus, theme),
67
- flexShrink: 0,
68
- } }), _jsx("span", { style: {
69
- color: theme.colors.assistantText,
70
- fontSize: theme.typography.captionSize,
71
- fontWeight: 600,
72
- }, children: showConnecting || awaitingChannel ? 'Connecting' : voiceStatusLabel(voiceStatus) }), showConnecting ? (_jsx("span", { style: { color: theme.colors.mutedText, fontSize: theme.typography.captionSize }, children: "\u2026" })) : null] }), statusHint ? (_jsx("p", { style: {
73
- margin: '4px 0 0',
74
- color: theme.colors.mutedText,
75
- fontSize: theme.typography.captionSize - 1,
76
- }, children: statusHint })) : null] }), _jsx("button", { type: "button", onClick: toggleVoiceMicMute, disabled: showConnecting || awaitingChannel, style: {
77
- border: 'none',
78
- background: 'none',
79
- padding: 8,
80
- cursor: showConnecting || awaitingChannel ? 'not-allowed' : 'pointer',
81
- opacity: showConnecting || awaitingChannel ? 0.45 : 1,
82
- }, "aria-label": isMicMuted ? 'Unmute microphone' : 'Mute microphone', children: isMicMuted ? _jsx(MicOffIcon, {}) : _jsx(MicIcon, { color: theme.colors.assistantText }) })] }), _jsx("button", { type: "button", onClick: returnToTextMode, style: {
83
- display: 'block',
84
- margin: `${theme.spacing.sm}px auto 0`,
85
- border: 'none',
86
- background: 'none',
87
- color: theme.colors.primary,
88
- fontSize: theme.typography.captionSize,
89
- cursor: 'pointer',
90
- }, children: "Back to text mode" })] }));
91
- }
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export declare function PresetMessageChips(): React.ReactElement | null;
3
- //# sourceMappingURL=PresetMessageChips.d.ts.map
@@ -1 +0,0 @@
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,CAoC9D"}
@@ -1,23 +0,0 @@
1
- import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
- import { useAgentAssistant } from '../context/AgentAssistantContext';
3
- export function PresetMessageChips() {
4
- const { presetMessages, selectPreset, theme, interactionMode } = useAgentAssistant();
5
- if (!presetMessages.length || interactionMode === 'voice') {
6
- return null;
7
- }
8
- return (_jsx("div", { style: {
9
- display: 'flex',
10
- flexWrap: 'wrap',
11
- gap: theme.spacing.sm,
12
- padding: `${theme.spacing.sm}px ${theme.spacing.md}px`,
13
- borderBottom: `1px solid ${theme.colors.border}`,
14
- }, children: presetMessages.map((preset, index) => (_jsx("button", { type: "button", onClick: () => void selectPreset(preset), style: {
15
- border: `1px solid ${theme.colors.border}`,
16
- borderRadius: theme.radius.button,
17
- padding: `${theme.spacing.xs}px ${theme.spacing.sm}px`,
18
- backgroundColor: theme.colors.surface,
19
- color: theme.colors.assistantText,
20
- fontSize: theme.typography.captionSize,
21
- cursor: 'pointer',
22
- }, children: preset.text }, `${preset.text}-${index}`))) }));
23
- }