@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/NxtlinqAgentChat.d.ts +26 -0
- package/dist/NxtlinqAgentChat.d.ts.map +1 -0
- package/dist/NxtlinqAgentChat.js +28 -0
- package/dist/components/AgentAssistantShell.d.ts +5 -0
- package/dist/components/AgentAssistantShell.d.ts.map +1 -0
- package/dist/components/AgentAssistantShell.js +52 -0
- package/dist/components/AgentComposer.d.ts +3 -0
- package/dist/components/AgentComposer.d.ts.map +1 -0
- package/dist/components/AgentComposer.js +60 -0
- package/dist/components/AgentMessageList.d.ts +3 -0
- package/dist/components/AgentMessageList.d.ts.map +1 -0
- package/dist/components/AgentMessageList.js +37 -0
- package/dist/components/AgentRemoteAudio.d.ts +4 -0
- package/dist/components/AgentRemoteAudio.d.ts.map +1 -0
- package/dist/components/AgentRemoteAudio.js +34 -0
- package/dist/components/AgentVoiceBar.d.ts +3 -0
- package/dist/components/AgentVoiceBar.d.ts.map +1 -0
- package/dist/components/AgentVoiceBar.js +91 -0
- package/dist/components/PresetMessageChips.d.ts +3 -0
- package/dist/components/PresetMessageChips.d.ts.map +1 -0
- package/dist/components/PresetMessageChips.js +23 -0
- package/dist/context/AgentAssistantContext.d.ts +32 -0
- package/dist/context/AgentAssistantContext.d.ts.map +1 -0
- package/dist/context/AgentAssistantContext.js +159 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts +2 -0
- package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts.map +1 -0
- package/dist/legacy/assets/images/adiSideItalicDataUri.js +1 -0
- package/dist/legacy/chatbot/ChatBot.d.ts +5 -0
- package/dist/legacy/chatbot/ChatBot.d.ts.map +1 -0
- package/dist/legacy/chatbot/ChatBot.js +35 -0
- package/dist/legacy/chatbot/context/ChatBotContext.d.ts +5 -0
- package/dist/legacy/chatbot/context/ChatBotContext.d.ts.map +1 -0
- package/dist/legacy/chatbot/context/ChatBotContext.js +2908 -0
- package/dist/legacy/chatbot/types/ChatBotTypes.d.ts +166 -0
- package/dist/legacy/chatbot/types/ChatBotTypes.d.ts.map +1 -0
- package/dist/legacy/chatbot/types/ChatBotTypes.js +1 -0
- package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts +17 -0
- package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/BerifyMeModal.js +110 -0
- package/dist/legacy/chatbot/ui/ChatBotUI.d.ts +3 -0
- package/dist/legacy/chatbot/ui/ChatBotUI.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/ChatBotUI.js +625 -0
- package/dist/legacy/chatbot/ui/MessageInput.d.ts +3 -0
- package/dist/legacy/chatbot/ui/MessageInput.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/MessageInput.js +321 -0
- package/dist/legacy/chatbot/ui/MessageList.d.ts +4 -0
- package/dist/legacy/chatbot/ui/MessageList.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/MessageList.js +455 -0
- package/dist/legacy/chatbot/ui/ModelSelector.d.ts +4 -0
- package/dist/legacy/chatbot/ui/ModelSelector.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/ModelSelector.js +122 -0
- package/dist/legacy/chatbot/ui/NotificationModal.d.ts +15 -0
- package/dist/legacy/chatbot/ui/NotificationModal.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/NotificationModal.js +53 -0
- package/dist/legacy/chatbot/ui/PermissionForm.d.ts +8 -0
- package/dist/legacy/chatbot/ui/PermissionForm.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/PermissionForm.js +465 -0
- package/dist/legacy/chatbot/ui/PresetMessages.d.ts +4 -0
- package/dist/legacy/chatbot/ui/PresetMessages.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/PresetMessages.js +33 -0
- package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts +3 -0
- package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/VoiceModePanel.js +95 -0
- package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts +73 -0
- package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/styles/isolatedStyles.js +985 -0
- package/dist/legacy/index.d.ts +14 -0
- package/dist/legacy/index.d.ts.map +1 -0
- package/dist/legacy/index.js +12 -0
- package/dist/theme/defaultTheme.d.ts +3 -0
- package/dist/theme/defaultTheme.d.ts.map +1 -0
- package/dist/theme/defaultTheme.js +20 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/voice/useVoiceConnectOrchestration.d.ts +21 -0
- package/dist/voice/useVoiceConnectOrchestration.d.ts.map +1 -0
- package/dist/voice/useVoiceConnectOrchestration.js +86 -0
- package/dist/voice/useVoiceMicState.d.ts +15 -0
- package/dist/voice/useVoiceMicState.d.ts.map +1 -0
- package/dist/voice/useVoiceMicState.js +94 -0
- package/dist/voice/useVoiceSilenceCommit.d.ts +10 -0
- package/dist/voice/useVoiceSilenceCommit.d.ts.map +1 -0
- package/dist/voice/useVoiceSilenceCommit.js +67 -0
- package/dist/voice/useVoiceTranscriptMessages.d.ts +16 -0
- package/dist/voice/useVoiceTranscriptMessages.d.ts.map +1 -0
- package/dist/voice/useVoiceTranscriptMessages.js +129 -0
- package/dist/voice/useWsRealtimeAudio.d.ts +18 -0
- package/dist/voice/useWsRealtimeAudio.d.ts.map +1 -0
- package/dist/voice/useWsRealtimeAudio.js +102 -0
- package/dist/voice/voiceMicConstants.d.ts +4 -0
- package/dist/voice/voiceMicConstants.d.ts.map +1 -0
- package/dist/voice/voiceMicConstants.js +10 -0
- package/dist/voice/ws/BrowserWsPcmPlayer.d.ts +23 -0
- package/dist/voice/ws/BrowserWsPcmPlayer.d.ts.map +1 -0
- package/dist/voice/ws/BrowserWsPcmPlayer.js +137 -0
- package/dist/voice/ws/BrowserWsPcmRecorder.d.ts +17 -0
- package/dist/voice/ws/BrowserWsPcmRecorder.d.ts.map +1 -0
- package/dist/voice/ws/BrowserWsPcmRecorder.js +71 -0
- package/dist/voice/ws/float32ToPcm16.d.ts +2 -0
- package/dist/voice/ws/float32ToPcm16.d.ts.map +1 -0
- package/dist/voice/ws/float32ToPcm16.js +8 -0
- package/dist/voice/ws/voiceSilenceConstants.d.ts +5 -0
- package/dist/voice/ws/voiceSilenceConstants.d.ts.map +1 -0
- package/dist/voice/ws/voiceSilenceConstants.js +4 -0
- package/dist/voice/ws/wsRealtimeConstants.d.ts +2 -0
- package/dist/voice/ws/wsRealtimeConstants.d.ts.map +1 -0
- package/dist/voice/ws/wsRealtimeConstants.js +1 -0
- package/package.json +60 -0
- package/src/NxtlinqAgentChat.tsx +79 -0
- package/src/components/AgentAssistantShell.tsx +104 -0
- package/src/components/AgentComposer.tsx +134 -0
- package/src/components/AgentMessageList.tsx +78 -0
- package/src/components/AgentRemoteAudio.tsx +34 -0
- package/src/components/AgentVoiceBar.tsx +173 -0
- package/src/components/PresetMessageChips.tsx +41 -0
- package/src/context/AgentAssistantContext.tsx +276 -0
- package/src/index.ts +78 -0
- package/src/legacy/assets/images/adiSideItalicDataUri.ts +1 -0
- package/src/legacy/chatbot/ChatBot.tsx +61 -0
- package/src/legacy/chatbot/context/ChatBotContext.tsx +3227 -0
- package/src/legacy/chatbot/types/ChatBotTypes.ts +195 -0
- package/src/legacy/chatbot/ui/BerifyMeModal.tsx +145 -0
- package/src/legacy/chatbot/ui/ChatBotUI.tsx +949 -0
- package/src/legacy/chatbot/ui/MessageInput.tsx +517 -0
- package/src/legacy/chatbot/ui/MessageList.tsx +764 -0
- package/src/legacy/chatbot/ui/ModelSelector.tsx +190 -0
- package/src/legacy/chatbot/ui/NotificationModal.tsx +110 -0
- package/src/legacy/chatbot/ui/PermissionForm.tsx +632 -0
- package/src/legacy/chatbot/ui/PresetMessages.tsx +50 -0
- package/src/legacy/chatbot/ui/VoiceModePanel.tsx +168 -0
- package/src/legacy/chatbot/ui/styles/isolatedStyles.ts +1058 -0
- package/src/legacy/index.ts +26 -0
- package/src/theme/defaultTheme.ts +22 -0
- package/src/types.ts +65 -0
- package/src/voice/useVoiceConnectOrchestration.ts +117 -0
- package/src/voice/useVoiceMicState.ts +117 -0
- package/src/voice/useVoiceTranscriptMessages.ts +173 -0
- package/src/voice/voiceMicConstants.ts +13 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { css } from '@emotion/react';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { useDraggable, useLocalStorage, useResizable, walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
|
|
6
|
+
import { useChatBot } from '../context/ChatBotContext';
|
|
7
|
+
import GraphicEqIcon from '@mui/icons-material/GraphicEq';
|
|
8
|
+
import { MessageInput } from './MessageInput';
|
|
9
|
+
import { MessageList } from './MessageList';
|
|
10
|
+
import { ModelSelector } from './ModelSelector';
|
|
11
|
+
import { PermissionForm } from './PermissionForm';
|
|
12
|
+
import { PresetMessages } from './PresetMessages';
|
|
13
|
+
import { VoiceModePanel } from './VoiceModePanel';
|
|
14
|
+
import { chatHeader, chatWindow, closeButton, errorToast, floatingButton, headerButton, headerTitle, idvBanner, idvBannerText, idvBannerTitle, idvDismissButton, idvVerifyButton, infoToast, loadingSpinner, modalOverlay, resizeHandleNE, resizeHandleNW, resizeHandleSE, resizeHandleSW, sdkContainer, successToast, toastCloseButton, warningToast } from './styles/isolatedStyles';
|
|
15
|
+
/** Header drag band inset so it does not overlap corner resize hit areas */
|
|
16
|
+
const DRAG_CORNER_EXCLUSION_PX = 20;
|
|
17
|
+
const MOBILE_BREAKPOINT = 768;
|
|
18
|
+
const MOBILE_EDGE_MARGIN = 12;
|
|
19
|
+
const MOBILE_FAB_POSITION = { right: MOBILE_EDGE_MARGIN, bottom: MOBILE_EDGE_MARGIN };
|
|
20
|
+
const isMobileViewport = () => typeof window !== 'undefined' && window.innerWidth <= MOBILE_BREAKPOINT;
|
|
21
|
+
// Toast Notification Component
|
|
22
|
+
const ToastNotification = ({ type, message, onClose, isChatOpen = false }) => {
|
|
23
|
+
const getToastStyles = () => {
|
|
24
|
+
const baseStyles = css `
|
|
25
|
+
${isChatOpen ? css `
|
|
26
|
+
top: 20px !important;
|
|
27
|
+
right: 20px !important;
|
|
28
|
+
max-width: 480px !important;
|
|
29
|
+
` : css `
|
|
30
|
+
bottom: 20px !important;
|
|
31
|
+
right: 20px !important;
|
|
32
|
+
`}
|
|
33
|
+
`;
|
|
34
|
+
switch (type) {
|
|
35
|
+
case 'success':
|
|
36
|
+
return css `${successToast} ${baseStyles}`;
|
|
37
|
+
case 'error':
|
|
38
|
+
return css `${errorToast} ${baseStyles}`;
|
|
39
|
+
case 'warning':
|
|
40
|
+
return css `${warningToast} ${baseStyles}`;
|
|
41
|
+
default:
|
|
42
|
+
return css `${infoToast} ${baseStyles}`;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const getIcon = () => {
|
|
46
|
+
switch (type) {
|
|
47
|
+
case 'success':
|
|
48
|
+
return '✅';
|
|
49
|
+
case 'error':
|
|
50
|
+
return '❌';
|
|
51
|
+
case 'warning':
|
|
52
|
+
return '⚠️';
|
|
53
|
+
default:
|
|
54
|
+
return 'ℹ️';
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return (_jsxs("div", { css: getToastStyles(), children: [_jsx("span", { css: css `font-size: 18px !important;`, children: getIcon() }), _jsx("span", { css: css `flex: 1 !important;`, children: message }), _jsx("button", { onClick: onClose, css: toastCloseButton, children: "\u00D7" })] }));
|
|
58
|
+
};
|
|
59
|
+
export const ChatBotUI = () => {
|
|
60
|
+
const { isOpen, setIsOpen, showPermissionForm, setShowPermissionForm, notification, setNotification, isAITLoading, hitAddress, walletInfo, onVerifyWallet, serviceId, isNeedSignInWithWallet, piiDisplayMode, isVoiceMode, isVoiceConnecting, enterVoiceMode, exitVoiceMode, props } = useChatBot();
|
|
61
|
+
const positionRef = React.useRef({ x: 0, y: 0 });
|
|
62
|
+
const { dimensions, handleResizeStart, positionAdjustment } = useResizable(positionRef);
|
|
63
|
+
const { position, handleDragStart, isDragging, updatePosition } = useDraggable(dimensions);
|
|
64
|
+
const [mobileLayout, setMobileLayout] = React.useState(isMobileViewport);
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
if (typeof window === 'undefined')
|
|
67
|
+
return;
|
|
68
|
+
const mq = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`);
|
|
69
|
+
const sync = () => setMobileLayout(mq.matches);
|
|
70
|
+
sync();
|
|
71
|
+
mq.addEventListener('change', sync);
|
|
72
|
+
return () => mq.removeEventListener('change', sync);
|
|
73
|
+
}, []);
|
|
74
|
+
// Update position ref when position changes
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
positionRef.current = position;
|
|
77
|
+
}, [position]);
|
|
78
|
+
// Update position when resizing to keep bottom-right corner fixed
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
if (positionAdjustment) {
|
|
81
|
+
updatePosition(positionAdjustment);
|
|
82
|
+
}
|
|
83
|
+
}, [positionAdjustment, updatePosition]);
|
|
84
|
+
// IDV suggestion banner state
|
|
85
|
+
const [showIDVSuggestion, setShowIDVSuggestion] = React.useState(true);
|
|
86
|
+
const [dismissUntil, setDismissUntil] = React.useState(null);
|
|
87
|
+
const [timeRemaining, setTimeRemaining] = React.useState('');
|
|
88
|
+
const countdownIntervalRef = React.useRef(null);
|
|
89
|
+
// Check if there's a berifyme token in URL (indicating recent berifyme verification)
|
|
90
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
91
|
+
const hasBerifymeToken = urlParams.get('token') && urlParams.get('method') === 'berifyme';
|
|
92
|
+
const isWalletVerifiedWithBerifyme = walletInfo?.id && walletInfo?.method === 'berifyme';
|
|
93
|
+
// Helper function to update IDV suggestion state based on storage value
|
|
94
|
+
const updateIDVSuggestionState = React.useCallback((dismissedValue) => {
|
|
95
|
+
// Clear any existing countdown interval
|
|
96
|
+
if (countdownIntervalRef.current) {
|
|
97
|
+
clearInterval(countdownIntervalRef.current);
|
|
98
|
+
countdownIntervalRef.current = null;
|
|
99
|
+
}
|
|
100
|
+
if (dismissedValue) {
|
|
101
|
+
const dismissTime = parseInt(dismissedValue);
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const timeLeft = dismissTime - now;
|
|
104
|
+
if (timeLeft > 0) {
|
|
105
|
+
setShowIDVSuggestion(false);
|
|
106
|
+
setDismissUntil(dismissTime);
|
|
107
|
+
// Calculate initial time remaining immediately
|
|
108
|
+
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
|
|
109
|
+
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
|
|
110
|
+
setTimeRemaining(`${hours}h ${minutes}m`);
|
|
111
|
+
// Set up countdown interval to update time remaining every minute
|
|
112
|
+
countdownIntervalRef.current = setInterval(() => {
|
|
113
|
+
const remaining = dismissTime - Date.now();
|
|
114
|
+
if (remaining > 0) {
|
|
115
|
+
const hours = Math.floor(remaining / (1000 * 60 * 60));
|
|
116
|
+
const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60));
|
|
117
|
+
setTimeRemaining(`${hours}h ${minutes}m`);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
setShowIDVSuggestion(true);
|
|
121
|
+
setDismissUntil(null);
|
|
122
|
+
setTimeRemaining('');
|
|
123
|
+
localStorage.removeItem('idv-suggestion-dismissed');
|
|
124
|
+
if (countdownIntervalRef.current) {
|
|
125
|
+
clearInterval(countdownIntervalRef.current);
|
|
126
|
+
countdownIntervalRef.current = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}, 60000);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
localStorage.removeItem('idv-suggestion-dismissed');
|
|
133
|
+
setShowIDVSuggestion(true);
|
|
134
|
+
setDismissUntil(null);
|
|
135
|
+
setTimeRemaining('');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
setShowIDVSuggestion(true);
|
|
140
|
+
setDismissUntil(null);
|
|
141
|
+
setTimeRemaining('');
|
|
142
|
+
}
|
|
143
|
+
}, []);
|
|
144
|
+
// Check if IDV suggestion should be shown
|
|
145
|
+
React.useEffect(() => {
|
|
146
|
+
const shouldShowBanner = hitAddress &&
|
|
147
|
+
!props.requireWalletIDVVerification &&
|
|
148
|
+
!hasBerifymeToken &&
|
|
149
|
+
!isWalletVerifiedWithBerifyme &&
|
|
150
|
+
!isNeedSignInWithWallet;
|
|
151
|
+
if (shouldShowBanner) {
|
|
152
|
+
const timer = setTimeout(() => {
|
|
153
|
+
const shouldShowBannerAfterDelay = hitAddress &&
|
|
154
|
+
!props.requireWalletIDVVerification &&
|
|
155
|
+
!hasBerifymeToken &&
|
|
156
|
+
!isWalletVerifiedWithBerifyme &&
|
|
157
|
+
!isNeedSignInWithWallet;
|
|
158
|
+
if (shouldShowBannerAfterDelay) {
|
|
159
|
+
const dismissed = localStorage.getItem('idv-suggestion-dismissed');
|
|
160
|
+
updateIDVSuggestionState(dismissed);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
setShowIDVSuggestion(false);
|
|
164
|
+
setDismissUntil(null);
|
|
165
|
+
setTimeRemaining('');
|
|
166
|
+
}
|
|
167
|
+
}, 1000);
|
|
168
|
+
return () => {
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
// Clear countdown interval on cleanup
|
|
171
|
+
if (countdownIntervalRef.current) {
|
|
172
|
+
clearInterval(countdownIntervalRef.current);
|
|
173
|
+
countdownIntervalRef.current = null;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// Don't show when:
|
|
179
|
+
// - No wallet connected (no hitAddress)
|
|
180
|
+
// - Wallet is already verified (walletInfo?.id exists)
|
|
181
|
+
setShowIDVSuggestion(false);
|
|
182
|
+
setDismissUntil(null);
|
|
183
|
+
setTimeRemaining('');
|
|
184
|
+
}
|
|
185
|
+
}, [hitAddress, walletInfo, isNeedSignInWithWallet, props.requireWalletIDVVerification, updateIDVSuggestionState]);
|
|
186
|
+
const handleDismissIDV = () => {
|
|
187
|
+
const dismissSeconds = props.idvBannerDismissSeconds || 86400;
|
|
188
|
+
const dismissTime = Date.now() + (dismissSeconds * 1000);
|
|
189
|
+
localStorage.setItem('idv-suggestion-dismissed', dismissTime.toString());
|
|
190
|
+
setShowIDVSuggestion(false);
|
|
191
|
+
setDismissUntil(dismissTime);
|
|
192
|
+
// Calculate and set initial time remaining
|
|
193
|
+
const initialHours = Math.floor(dismissSeconds / 3600);
|
|
194
|
+
const initialMinutes = Math.floor((dismissSeconds % 3600) / 60);
|
|
195
|
+
setTimeRemaining(`${initialHours}h ${initialMinutes}m`);
|
|
196
|
+
// Set up countdown interval
|
|
197
|
+
if (countdownIntervalRef.current) {
|
|
198
|
+
clearInterval(countdownIntervalRef.current);
|
|
199
|
+
}
|
|
200
|
+
countdownIntervalRef.current = setInterval(() => {
|
|
201
|
+
const remaining = dismissTime - Date.now();
|
|
202
|
+
if (remaining > 0) {
|
|
203
|
+
const remainingHours = Math.floor(remaining / (1000 * 60 * 60));
|
|
204
|
+
const remainingMinutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60));
|
|
205
|
+
setTimeRemaining(`${remainingHours}h ${remainingMinutes}m`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
setShowIDVSuggestion(true);
|
|
209
|
+
setDismissUntil(null);
|
|
210
|
+
setTimeRemaining('');
|
|
211
|
+
localStorage.removeItem('idv-suggestion-dismissed');
|
|
212
|
+
if (countdownIntervalRef.current) {
|
|
213
|
+
clearInterval(countdownIntervalRef.current);
|
|
214
|
+
countdownIntervalRef.current = null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}, 60000);
|
|
218
|
+
const hours = Math.floor(dismissSeconds / 3600);
|
|
219
|
+
const minutes = Math.floor((dismissSeconds % 3600) / 60);
|
|
220
|
+
const timeText = hours > 0
|
|
221
|
+
? (minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`)
|
|
222
|
+
: `${minutes}m`;
|
|
223
|
+
setNotification({
|
|
224
|
+
show: true,
|
|
225
|
+
type: 'info',
|
|
226
|
+
message: walletTextUtils.getWalletText(`IDV suggestion hidden for ${timeText}. You can still verify your wallet anytime.`, serviceId),
|
|
227
|
+
autoHide: true,
|
|
228
|
+
duration: 3000
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
// Add CSS animation for loading spinner
|
|
232
|
+
React.useEffect(() => {
|
|
233
|
+
const style = document.createElement('style');
|
|
234
|
+
style.textContent = `
|
|
235
|
+
@keyframes spin {
|
|
236
|
+
0% { transform: rotate(0deg); }
|
|
237
|
+
100% { transform: rotate(360deg); }
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
document.head.appendChild(style);
|
|
241
|
+
return () => {
|
|
242
|
+
document.head.removeChild(style);
|
|
243
|
+
};
|
|
244
|
+
}, []);
|
|
245
|
+
const handleClose = () => {
|
|
246
|
+
if (typeof window !== 'undefined') {
|
|
247
|
+
if (mobileLayout) {
|
|
248
|
+
setButtonPosition(MOBILE_FAB_POSITION);
|
|
249
|
+
latestButtonPositionRef.current = MOBILE_FAB_POSITION;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
const windowRight = window.innerWidth - (position.x + dimensions.width);
|
|
253
|
+
const windowBottom = window.innerHeight - (position.y + dimensions.height);
|
|
254
|
+
const newButtonPosition = clampButtonPosition({ right: windowRight, bottom: windowBottom });
|
|
255
|
+
setButtonPosition(newButtonPosition);
|
|
256
|
+
if (hasLoadedButtonPosition.current) {
|
|
257
|
+
setSavedButtonPosition(newButtonPosition);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
setIsOpen(false);
|
|
262
|
+
};
|
|
263
|
+
const handleOpen = () => {
|
|
264
|
+
if (typeof window !== 'undefined') {
|
|
265
|
+
if (mobileLayout) {
|
|
266
|
+
updatePosition({ x: MOBILE_EDGE_MARGIN, y: MOBILE_EDGE_MARGIN });
|
|
267
|
+
}
|
|
268
|
+
else if (buttonRef.current) {
|
|
269
|
+
// Window's bottom-right corner aligns with the FAB
|
|
270
|
+
const buttonRect = buttonRef.current.getBoundingClientRect();
|
|
271
|
+
const buttonRight = window.innerWidth - buttonRect.right;
|
|
272
|
+
const buttonBottom = window.innerHeight - buttonRect.bottom;
|
|
273
|
+
const windowLeft = window.innerWidth - dimensions.width - buttonRight;
|
|
274
|
+
const windowTop = window.innerHeight - dimensions.height - buttonBottom;
|
|
275
|
+
const clampedLeft = Math.max(BUTTON_MARGIN, Math.min(windowLeft, window.innerWidth - dimensions.width - BUTTON_MARGIN));
|
|
276
|
+
const clampedTop = Math.max(BUTTON_MARGIN, Math.min(windowTop, window.innerHeight - dimensions.height - BUTTON_MARGIN));
|
|
277
|
+
updatePosition({ x: clampedLeft, y: clampedTop });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
setIsOpen(true);
|
|
281
|
+
};
|
|
282
|
+
const handleCloseNotification = () => {
|
|
283
|
+
setNotification((prev) => ({ ...prev, show: false }));
|
|
284
|
+
};
|
|
285
|
+
// Auto-hide notification when autoHide is true and duration is set
|
|
286
|
+
React.useEffect(() => {
|
|
287
|
+
if (notification.show && notification.autoHide && notification.duration && notification.duration > 0) {
|
|
288
|
+
const timer = setTimeout(() => {
|
|
289
|
+
setNotification((prev) => ({ ...prev, show: false }));
|
|
290
|
+
}, notification.duration);
|
|
291
|
+
return () => clearTimeout(timer);
|
|
292
|
+
}
|
|
293
|
+
}, [notification.show, notification.autoHide, notification.duration, setNotification]);
|
|
294
|
+
const handleSettingsClick = () => {
|
|
295
|
+
setShowPermissionForm(true);
|
|
296
|
+
};
|
|
297
|
+
// Constants for button positioning
|
|
298
|
+
const BUTTON_MARGIN = 20;
|
|
299
|
+
const BUTTON_MIN_MARGIN = 5;
|
|
300
|
+
const DRAG_THRESHOLD = 5; // Minimum distance in pixels to distinguish drag from click
|
|
301
|
+
// Track window size for responsive button positioning and dragging
|
|
302
|
+
const buttonRef = React.useRef(null);
|
|
303
|
+
const buttonDragState = React.useRef(null);
|
|
304
|
+
const [isButtonDragging, setIsButtonDragging] = React.useState(false);
|
|
305
|
+
const [isButtonDragActive, setIsButtonDragActive] = React.useState(false);
|
|
306
|
+
const hasLoadedButtonPosition = React.useRef(false);
|
|
307
|
+
const buttonDragDistance = React.useRef(0);
|
|
308
|
+
// Track latest button position for saving (to avoid stale closure issues)
|
|
309
|
+
const latestButtonPositionRef = React.useRef({ right: BUTTON_MARGIN, bottom: BUTTON_MARGIN });
|
|
310
|
+
// Load saved position synchronously on initialization to avoid position jump
|
|
311
|
+
const getInitialButtonPosition = () => {
|
|
312
|
+
if (typeof window === 'undefined') {
|
|
313
|
+
return { right: BUTTON_MARGIN, bottom: BUTTON_MARGIN };
|
|
314
|
+
}
|
|
315
|
+
if (isMobileViewport()) {
|
|
316
|
+
return { ...MOBILE_FAB_POSITION };
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const saved = localStorage.getItem('ai-agent-button-position');
|
|
320
|
+
if (saved) {
|
|
321
|
+
const parsed = JSON.parse(saved);
|
|
322
|
+
if (parsed && typeof parsed.right === 'number' && typeof parsed.bottom === 'number') {
|
|
323
|
+
return parsed;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
catch (e) {
|
|
328
|
+
// Ignore parse errors, use default
|
|
329
|
+
}
|
|
330
|
+
return { right: BUTTON_MARGIN, bottom: BUTTON_MARGIN };
|
|
331
|
+
};
|
|
332
|
+
const [, setSavedButtonPosition, isButtonPositionInitialized] = useLocalStorage('ai-agent-button-position', null);
|
|
333
|
+
const [buttonPosition, setButtonPosition] = React.useState(getInitialButtonPosition);
|
|
334
|
+
// Keep ref in sync with button position state and mark as loaded when initialized
|
|
335
|
+
React.useEffect(() => {
|
|
336
|
+
latestButtonPositionRef.current = buttonPosition;
|
|
337
|
+
if (isButtonPositionInitialized && !hasLoadedButtonPosition.current) {
|
|
338
|
+
hasLoadedButtonPosition.current = true;
|
|
339
|
+
}
|
|
340
|
+
}, [buttonPosition, isButtonPositionInitialized]);
|
|
341
|
+
// Fast clamp calculation helper (used in both drag and resize)
|
|
342
|
+
const clampButtonPositionFast = React.useCallback((right, bottom, buttonWidth, buttonHeight) => {
|
|
343
|
+
const viewportWidth = window.innerWidth;
|
|
344
|
+
const viewportHeight = window.innerHeight;
|
|
345
|
+
// Clamp horizontally
|
|
346
|
+
const maxRight = viewportWidth - buttonWidth - BUTTON_MARGIN;
|
|
347
|
+
let clampedRight = right;
|
|
348
|
+
if (clampedRight > maxRight) {
|
|
349
|
+
clampedRight = Math.max(BUTTON_MIN_MARGIN, maxRight);
|
|
350
|
+
}
|
|
351
|
+
else if (clampedRight < BUTTON_MARGIN) {
|
|
352
|
+
clampedRight = BUTTON_MARGIN;
|
|
353
|
+
}
|
|
354
|
+
// Clamp vertically
|
|
355
|
+
const maxBottom = viewportHeight - buttonHeight - BUTTON_MARGIN;
|
|
356
|
+
let clampedBottom = bottom;
|
|
357
|
+
if (clampedBottom > maxBottom) {
|
|
358
|
+
clampedBottom = Math.max(BUTTON_MIN_MARGIN, maxBottom);
|
|
359
|
+
}
|
|
360
|
+
else if (clampedBottom < BUTTON_MARGIN) {
|
|
361
|
+
clampedBottom = BUTTON_MARGIN;
|
|
362
|
+
}
|
|
363
|
+
return { right: clampedRight, bottom: clampedBottom };
|
|
364
|
+
}, []);
|
|
365
|
+
// Clamp button position to keep it within viewport
|
|
366
|
+
const clampButtonPosition = React.useCallback((pos, buttonWidth, buttonHeight) => {
|
|
367
|
+
if (typeof window === 'undefined') {
|
|
368
|
+
return pos;
|
|
369
|
+
}
|
|
370
|
+
// Use provided dimensions or get from DOM (avoid getBoundingClientRect during drag)
|
|
371
|
+
let width = buttonWidth;
|
|
372
|
+
let height = buttonHeight;
|
|
373
|
+
if (width === undefined || height === undefined) {
|
|
374
|
+
if (!buttonRef.current) {
|
|
375
|
+
return pos;
|
|
376
|
+
}
|
|
377
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
378
|
+
width = rect.width;
|
|
379
|
+
height = rect.height;
|
|
380
|
+
}
|
|
381
|
+
return clampButtonPositionFast(pos.right, pos.bottom, width, height);
|
|
382
|
+
}, [clampButtonPositionFast]);
|
|
383
|
+
const handleButtonDragStart = React.useCallback((event) => {
|
|
384
|
+
if (mobileLayout) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
event.stopPropagation();
|
|
388
|
+
if (buttonRef.current) {
|
|
389
|
+
// Cache button dimensions to avoid getBoundingClientRect during drag
|
|
390
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
391
|
+
buttonDragState.current = {
|
|
392
|
+
startX: event.clientX,
|
|
393
|
+
startY: event.clientY,
|
|
394
|
+
startRight: buttonPosition.right,
|
|
395
|
+
startBottom: buttonPosition.bottom,
|
|
396
|
+
buttonWidth: rect.width,
|
|
397
|
+
buttonHeight: rect.height
|
|
398
|
+
};
|
|
399
|
+
buttonDragDistance.current = 0;
|
|
400
|
+
setIsButtonDragging(false);
|
|
401
|
+
setIsButtonDragActive(true);
|
|
402
|
+
document.body.style.userSelect = 'none';
|
|
403
|
+
}
|
|
404
|
+
}, [buttonPosition, mobileLayout]);
|
|
405
|
+
React.useEffect(() => {
|
|
406
|
+
if (!mobileLayout) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
setButtonPosition(MOBILE_FAB_POSITION);
|
|
410
|
+
latestButtonPositionRef.current = MOBILE_FAB_POSITION;
|
|
411
|
+
}, [mobileLayout]);
|
|
412
|
+
// Handle button drag move and end
|
|
413
|
+
React.useEffect(() => {
|
|
414
|
+
if (mobileLayout || !isButtonDragActive) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const handlePointerMove = (event) => {
|
|
418
|
+
if (!buttonDragState.current)
|
|
419
|
+
return;
|
|
420
|
+
const deltaX = event.clientX - buttonDragState.current.startX;
|
|
421
|
+
const deltaY = event.clientY - buttonDragState.current.startY;
|
|
422
|
+
const dragDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
423
|
+
buttonDragDistance.current = dragDistance;
|
|
424
|
+
// Only start dragging if moved more than threshold (to distinguish from click)
|
|
425
|
+
if (dragDistance > DRAG_THRESHOLD && !isButtonDragging) {
|
|
426
|
+
setIsButtonDragging(true);
|
|
427
|
+
document.body.style.cursor = 'grabbing';
|
|
428
|
+
}
|
|
429
|
+
if (dragDistance > DRAG_THRESHOLD) {
|
|
430
|
+
// Calculate new position (right decreases when moving right, bottom decreases when moving down)
|
|
431
|
+
const newRight = buttonDragState.current.startRight - deltaX;
|
|
432
|
+
const newBottom = buttonDragState.current.startBottom - deltaY;
|
|
433
|
+
// Use fast clamp calculation with cached dimensions
|
|
434
|
+
const clampedPos = clampButtonPositionFast(newRight, newBottom, buttonDragState.current.buttonWidth, buttonDragState.current.buttonHeight);
|
|
435
|
+
latestButtonPositionRef.current = clampedPos;
|
|
436
|
+
// Update state immediately (same as window drag) for responsive dragging
|
|
437
|
+
setButtonPosition(clampedPos);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
const handlePointerUp = () => {
|
|
441
|
+
const wasDragging = buttonDragDistance.current > DRAG_THRESHOLD;
|
|
442
|
+
// Save position to localStorage if we were dragging
|
|
443
|
+
if (wasDragging && hasLoadedButtonPosition.current) {
|
|
444
|
+
setSavedButtonPosition(latestButtonPositionRef.current);
|
|
445
|
+
}
|
|
446
|
+
setIsButtonDragging(false);
|
|
447
|
+
setIsButtonDragActive(false);
|
|
448
|
+
buttonDragDistance.current = 0;
|
|
449
|
+
document.body.style.userSelect = '';
|
|
450
|
+
document.body.style.cursor = '';
|
|
451
|
+
buttonDragState.current = null;
|
|
452
|
+
};
|
|
453
|
+
document.addEventListener('pointermove', handlePointerMove);
|
|
454
|
+
document.addEventListener('pointerup', handlePointerUp);
|
|
455
|
+
return () => {
|
|
456
|
+
document.removeEventListener('pointermove', handlePointerMove);
|
|
457
|
+
document.removeEventListener('pointerup', handlePointerUp);
|
|
458
|
+
document.body.style.userSelect = '';
|
|
459
|
+
document.body.style.cursor = '';
|
|
460
|
+
};
|
|
461
|
+
}, [isButtonDragActive, isButtonDragging, setSavedButtonPosition, clampButtonPositionFast, mobileLayout]);
|
|
462
|
+
// Update button position on window resize to ensure it stays within viewport
|
|
463
|
+
React.useEffect(() => {
|
|
464
|
+
if (typeof window === 'undefined')
|
|
465
|
+
return;
|
|
466
|
+
const handleResize = () => {
|
|
467
|
+
requestAnimationFrame(() => {
|
|
468
|
+
if (mobileLayout) {
|
|
469
|
+
setButtonPosition(MOBILE_FAB_POSITION);
|
|
470
|
+
latestButtonPositionRef.current = MOBILE_FAB_POSITION;
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const clampedPos = clampButtonPosition(buttonPosition);
|
|
474
|
+
if (clampedPos.right !== buttonPosition.right || clampedPos.bottom !== buttonPosition.bottom) {
|
|
475
|
+
setButtonPosition(clampedPos);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
};
|
|
479
|
+
window.addEventListener('resize', handleResize);
|
|
480
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
481
|
+
}, [buttonPosition, clampButtonPosition, mobileLayout]);
|
|
482
|
+
const getButtonStyle = React.useMemo(() => {
|
|
483
|
+
if (mobileLayout) {
|
|
484
|
+
return css `
|
|
485
|
+
position: fixed !important;
|
|
486
|
+
bottom: calc(${MOBILE_EDGE_MARGIN}px + env(safe-area-inset-bottom, 0px)) !important;
|
|
487
|
+
right: calc(${MOBILE_EDGE_MARGIN}px + env(safe-area-inset-right, 0px)) !important;
|
|
488
|
+
left: auto !important;
|
|
489
|
+
top: auto !important;
|
|
490
|
+
touch-action: manipulation !important;
|
|
491
|
+
`;
|
|
492
|
+
}
|
|
493
|
+
return css `
|
|
494
|
+
position: fixed !important;
|
|
495
|
+
bottom: ${buttonPosition.bottom}px !important;
|
|
496
|
+
right: ${buttonPosition.right}px !important;
|
|
497
|
+
`;
|
|
498
|
+
}, [buttonPosition, mobileLayout]);
|
|
499
|
+
// Show floating button when chat is closed
|
|
500
|
+
if (!isOpen) {
|
|
501
|
+
return (_jsxs("div", { css: sdkContainer, children: [_jsx("button", { ref: buttonRef, onClick: (e) => {
|
|
502
|
+
// Don't open if we just finished dragging (moved more than threshold)
|
|
503
|
+
if (buttonDragDistance.current > DRAG_THRESHOLD) {
|
|
504
|
+
e.preventDefault();
|
|
505
|
+
e.stopPropagation();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
handleOpen();
|
|
509
|
+
}, onPointerDown: mobileLayout ? undefined : handleButtonDragStart, css: [floatingButton, getButtonStyle, css `
|
|
510
|
+
cursor: ${mobileLayout ? 'pointer' : isButtonDragging ? 'grabbing' : 'grab'} !important;
|
|
511
|
+
user-select: none !important;
|
|
512
|
+
`], title: mobileLayout ? 'Open AI Agent Chat' : 'Drag to move or click to open AI Agent Chat', children: "AI Agent" }), notification.show && (_jsx(ToastNotification, { type: notification.type, message: notification.message, onClose: handleCloseNotification, isChatOpen: isOpen }))] }));
|
|
513
|
+
}
|
|
514
|
+
return (_jsxs("div", { css: sdkContainer, children: [_jsxs("div", { css: [chatWindow, css `
|
|
515
|
+
width: ${dimensions.width}px !important;
|
|
516
|
+
height: ${dimensions.height}px !important;
|
|
517
|
+
left: ${position.x}px !important;
|
|
518
|
+
top: ${position.y}px !important;
|
|
519
|
+
bottom: auto !important;
|
|
520
|
+
right: auto !important;
|
|
521
|
+
${mobileLayout ? css `
|
|
522
|
+
width: calc(100vw - ${MOBILE_EDGE_MARGIN * 2}px) !important;
|
|
523
|
+
height: calc(100dvh - ${MOBILE_EDGE_MARGIN * 2}px) !important;
|
|
524
|
+
max-width: calc(100vw - ${MOBILE_EDGE_MARGIN * 2}px) !important;
|
|
525
|
+
max-height: calc(100dvh - ${MOBILE_EDGE_MARGIN * 2}px) !important;
|
|
526
|
+
left: ${MOBILE_EDGE_MARGIN}px !important;
|
|
527
|
+
top: ${MOBILE_EDGE_MARGIN}px !important;
|
|
528
|
+
` : ''}
|
|
529
|
+
`], children: [!mobileLayout && (_jsxs(_Fragment, { children: [_jsx("div", { css: resizeHandleNW, onPointerDown: handleResizeStart('nw'), title: "Resize", "aria-label": "Resize chat window from top-left" }), _jsx("div", { css: resizeHandleNE, onPointerDown: handleResizeStart('ne'), title: "Resize", "aria-label": "Resize chat window from top-right" }), _jsx("div", { css: resizeHandleSW, onPointerDown: handleResizeStart('sw'), title: "Resize", "aria-label": "Resize chat window from bottom-left" }), _jsx("div", { css: resizeHandleSE, onPointerDown: handleResizeStart('se'), title: "Resize", "aria-label": "Resize chat window from bottom-right" })] })), _jsxs("div", { css: [chatHeader, css `
|
|
530
|
+
position: relative !important;
|
|
531
|
+
`], children: [_jsx("div", { css: css `
|
|
532
|
+
position: absolute !important;
|
|
533
|
+
left: ${DRAG_CORNER_EXCLUSION_PX}px !important;
|
|
534
|
+
right: ${DRAG_CORNER_EXCLUSION_PX}px !important;
|
|
535
|
+
top: 0 !important;
|
|
536
|
+
bottom: 0 !important;
|
|
537
|
+
z-index: 1 !important;
|
|
538
|
+
cursor: ${isDragging ? 'grabbing' : 'grab'} !important;
|
|
539
|
+
user-select: none !important;
|
|
540
|
+
`, onPointerDown: handleDragStart, title: "Drag to move", "aria-hidden": true }), _jsxs("div", { css: css `
|
|
541
|
+
position: relative !important;
|
|
542
|
+
z-index: 2 !important;
|
|
543
|
+
display: flex !important;
|
|
544
|
+
justify-content: space-between !important;
|
|
545
|
+
align-items: center !important;
|
|
546
|
+
width: 100% !important;
|
|
547
|
+
pointer-events: none !important;
|
|
548
|
+
`, children: [_jsxs("div", { css: css `
|
|
549
|
+
display: flex !important;
|
|
550
|
+
align-items: center !important;
|
|
551
|
+
gap: 10px !important;
|
|
552
|
+
pointer-events: none !important;
|
|
553
|
+
`, children: [_jsx("h3", { css: headerTitle, children: "AI Agent" }), !isVoiceMode && (_jsx("div", { css: css `
|
|
554
|
+
position: relative !important;
|
|
555
|
+
pointer-events: auto !important;
|
|
556
|
+
`, children: _jsx(ModelSelector, {}) })), !isVoiceMode && piiDisplayMode === 'redacted' && (_jsxs("div", { css: css `
|
|
557
|
+
display: inline-flex !important;
|
|
558
|
+
align-items: center !important;
|
|
559
|
+
gap: 4px !important;
|
|
560
|
+
padding: 2px 8px !important;
|
|
561
|
+
background-color: rgba(255, 255, 255, 0.2) !important;
|
|
562
|
+
border: 1px solid rgba(255, 255, 255, 0.4) !important;
|
|
563
|
+
border-radius: 10px !important;
|
|
564
|
+
font-size: 10px !important;
|
|
565
|
+
font-weight: 600 !important;
|
|
566
|
+
color: #ffffff !important;
|
|
567
|
+
white-space: nowrap !important;
|
|
568
|
+
line-height: 1.4 !important;
|
|
569
|
+
user-select: none !important;
|
|
570
|
+
`, title: "PII Protection is active \u2014 sensitive data is automatically anonymized before sending to AI", children: [_jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "#ffffff", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z" }) }), "PII Protected"] })), isAITLoading && (_jsxs("div", { css: css `
|
|
571
|
+
display: flex !important;
|
|
572
|
+
align-items: center !important;
|
|
573
|
+
gap: 5px !important;
|
|
574
|
+
font-size: 12px !important;
|
|
575
|
+
opacity: 0.8 !important;
|
|
576
|
+
`, children: [_jsx("div", { css: loadingSpinner }), "Loading..."] }))] }), _jsxs("div", { css: css `
|
|
577
|
+
display: flex !important;
|
|
578
|
+
align-items: center !important;
|
|
579
|
+
gap: 10px !important;
|
|
580
|
+
pointer-events: auto !important;
|
|
581
|
+
`, children: [_jsxs("button", { onClick: () => void ((isVoiceMode || isVoiceConnecting) ? exitVoiceMode() : enterVoiceMode()), css: [headerButton, css `
|
|
582
|
+
width: auto !important;
|
|
583
|
+
min-width: 82px !important;
|
|
584
|
+
padding: 0 10px !important;
|
|
585
|
+
gap: 6px !important;
|
|
586
|
+
font-size: 12px !important;
|
|
587
|
+
font-weight: 600 !important;
|
|
588
|
+
line-height: 1 !important;
|
|
589
|
+
white-space: nowrap !important;
|
|
590
|
+
`], title: isVoiceConnecting
|
|
591
|
+
? 'Cancel voice connection and return to text mode'
|
|
592
|
+
: isVoiceMode
|
|
593
|
+
? 'Switch to text mode'
|
|
594
|
+
: 'Switch to voice mode', onPointerDown: (e) => e.stopPropagation(), children: [_jsx(GraphicEqIcon, { css: css `font-size: 16px !important; color: #fff !important;` }), (isVoiceMode || isVoiceConnecting) ? 'Text Mode' : 'Voice Mode'] }), _jsx("button", { onClick: handleSettingsClick, css: headerButton, title: "AIT Settings", onPointerDown: (e) => e.stopPropagation(), children: "\u2699\uFE0F" }), _jsx("button", { onClick: handleClose, css: closeButton, onPointerDown: (e) => e.stopPropagation(), title: "Minimize", children: "\u2212" })] })] })] }), showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !isWalletVerifiedWithBerifyme && (_jsxs("div", { "data-idv-banner": true, css: idvBanner, children: [_jsx("div", { css: css `
|
|
595
|
+
font-size: 20px !important;
|
|
596
|
+
color: #f39c12 !important;
|
|
597
|
+
`, children: "\uD83D\uDCA1" }), _jsxs("div", { css: css `flex: 1 !important;`, children: [_jsx("h5", { css: idvBannerTitle, children: walletTextUtils.getWalletText('Recommended: Verify Your Wallet', serviceId) }), _jsx("p", { css: idvBannerText, children: walletTextUtils.getWalletText('While not required, verifying your wallet with Berify.me provides additional security and trust for your AI agent interactions.', serviceId) }), _jsx("button", { onClick: () => onVerifyWallet('berifyme'), css: idvVerifyButton, children: walletTextUtils.getWalletText('Verify Wallet', serviceId) })] }), _jsx("button", { onClick: handleDismissIDV, css: idvDismissButton, title: `Hide for ${Math.floor((props.idvBannerDismissSeconds || 86400) / 3600)} hours`, children: "\u00D7" })] })), !showIDVSuggestion && dismissUntil && timeRemaining && (_jsxs("div", { css: css `
|
|
598
|
+
background-color: #f8f9fa !important;
|
|
599
|
+
border: 1px solid #e9ecef !important;
|
|
600
|
+
margin: 16px !important;
|
|
601
|
+
padding: 12px !important;
|
|
602
|
+
border-radius: 8px !important;
|
|
603
|
+
text-align: center !important;
|
|
604
|
+
font-size: 13px !important;
|
|
605
|
+
color: #6c757d !important;
|
|
606
|
+
`, children: [_jsxs("span", { children: ["\uD83D\uDCA1 IDV suggestion hidden. Will show again in ", timeRemaining] }), _jsx("button", { onClick: () => {
|
|
607
|
+
localStorage.removeItem('idv-suggestion-dismissed');
|
|
608
|
+
setShowIDVSuggestion(true);
|
|
609
|
+
setDismissUntil(null);
|
|
610
|
+
setTimeRemaining('');
|
|
611
|
+
}, css: css `
|
|
612
|
+
background: none !important;
|
|
613
|
+
border: none !important;
|
|
614
|
+
color: #007bff !important;
|
|
615
|
+
font-size: 12px !important;
|
|
616
|
+
cursor: pointer !important;
|
|
617
|
+
margin-left: 8px !important;
|
|
618
|
+
text-decoration: underline !important;
|
|
619
|
+
`, title: "Show now", children: "Show now" })] })), _jsx(MessageList, {}), !isVoiceMode && _jsx(PresetMessages, {}), isVoiceMode ? _jsx(VoiceModePanel, {}) : _jsx(MessageInput, {})] }), showPermissionForm && (_jsx("div", { css: modalOverlay, onClick: (e) => {
|
|
620
|
+
// Close modal when clicking on background
|
|
621
|
+
if (e.target === e.currentTarget) {
|
|
622
|
+
setShowPermissionForm(false);
|
|
623
|
+
}
|
|
624
|
+
}, children: _jsx(PermissionForm, { onClose: () => setShowPermissionForm(false) }) })), notification.show && (_jsx(ToastNotification, { type: notification.type, message: notification.message, onClose: handleCloseNotification, isChatOpen: isOpen }))] }));
|
|
625
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../../../src/legacy/chatbot/ui/MessageInput.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAofhC,CAAC"}
|