@bytexbyte/nxtlinq-ai-agent-sdk 1.6.16 → 1.6.18

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../../src/components/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;AAI/B,eAAO,MAAM,UAAU,0BAMtB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAohElD,CAAC"}
1
+ {"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../../src/components/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;AAM/B,eAAO,MAAM,UAAU,0BAMtB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAwvElD,CAAC"}
@@ -9,6 +9,7 @@ import useSessionStorage from '../../core/lib/useSessionStorage';
9
9
  import { useSpeechToTextFromMic } from '../../core/lib/useSpeechToTextFromMic';
10
10
  import metakeepClient from '../../core/metakeepClient';
11
11
  import { sleep } from '../../core/utils';
12
+ const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
12
13
  const ChatBotContext = React.createContext(undefined);
13
14
  export const useChatBot = () => {
14
15
  const context = React.useContext(ChatBotContext);
@@ -36,7 +37,7 @@ isStopRecordingOnSend = false, }) => {
36
37
  setApiHosts(environment);
37
38
  const nxtlinqApi = React.useMemo(() => createNxtlinqApi(apiKey, apiSecret), [apiKey, apiSecret]);
38
39
  // Custom hook
39
- const { isRecording, transcript, partialTranscript, start: startRecording, stop: stopRecording, clear: clearRecording } = useSpeechToTextFromMic({
40
+ const { isMicEnabled, transcript, partialTranscript, start: startRecording, stop: stopRecording, clear: clearRecording } = useSpeechToTextFromMic({
40
41
  apiKey,
41
42
  apiSecret
42
43
  });
@@ -69,6 +70,8 @@ isStopRecordingOnSend = false, }) => {
69
70
  ? useSessionStorage('chatbot-suggestions', presetMessages)
70
71
  : React.useState(presetMessages);
71
72
  const [isAITEnabling, setIsAITEnabling] = React.useState(false);
73
+ const [isAwaitingMicGesture, setIsAwaitingMicGesture] = React.useState(false);
74
+ const [autoSendEnabled, setAutoSendEnabled] = useLocalStorage('chatbot-auto-send-enabled', true);
72
75
  // Use refs to get latest state values in hasPermission function
73
76
  const hitAddressRef = React.useRef(hitAddress);
74
77
  const aitRef = React.useRef(ait);
@@ -76,9 +79,15 @@ isStopRecordingOnSend = false, }) => {
76
79
  const nxtlinqAITServiceAccessTokenRef = React.useRef(nxtlinqAITServiceAccessToken);
77
80
  const signerRef = React.useRef(signer);
78
81
  // Refs for input value and recording state
79
- const isRecordingRef = React.useRef(false);
82
+ const isMicEnabledRef = React.useRef(false);
83
+ const isRestoringRecordingRef = React.useRef(false);
84
+ const hasSyncedMicStateRef = React.useRef(false);
85
+ const isReacquiringMicRef = React.useRef(false);
86
+ const pendingMicAutoStartRef = React.useRef(false);
87
+ const autoStartGestureCleanupRef = React.useRef(null);
80
88
  const textInputRef = React.useRef(null);
81
89
  const lastPartialRangeRef = React.useRef(null);
90
+ const lastAutoSentTranscriptRef = React.useRef('');
82
91
  function insertPartial(input, partial, caret) {
83
92
  let start = caret;
84
93
  let end = caret;
@@ -160,17 +169,214 @@ isStopRecordingOnSend = false, }) => {
160
169
  if (!textInputRef.current)
161
170
  return;
162
171
  const { next, caret } = finalizePartial(inputValue, transcript);
163
- setInputValue(normalizeTranscript(next));
172
+ const normalizedText = normalizeTranscript(next);
173
+ setInputValue(normalizedText);
164
174
  setTimeout(() => {
165
175
  if (textInputRef.current) {
166
176
  textInputRef.current.selectionStart = caret;
167
177
  textInputRef.current.selectionEnd = caret;
168
178
  }
169
179
  }, 0);
170
- }, [transcript]);
180
+ // Auto send on speech complete (if user enabled the toggle)
181
+ if (autoSendEnabled && isMicEnabled && normalizedText.trim()) {
182
+ // Avoid duplicate sends by checking if this transcript was already sent
183
+ if (lastAutoSentTranscriptRef.current !== normalizedText.trim()) {
184
+ lastAutoSentTranscriptRef.current = normalizedText.trim();
185
+ // Small delay to ensure input value is updated
186
+ setTimeout(() => {
187
+ if (normalizedText.trim() && !isLoading) {
188
+ sendMessage(normalizedText.trim()).catch(error => {
189
+ console.error('Failed to auto-send message from speech:', error);
190
+ // Reset the ref so user can try again
191
+ lastAutoSentTranscriptRef.current = '';
192
+ });
193
+ }
194
+ }, 100);
195
+ }
196
+ }
197
+ // eslint-disable-next-line react-hooks/exhaustive-deps
198
+ }, [transcript, autoSendEnabled, isMicEnabled, isLoading]);
199
+ React.useEffect(() => {
200
+ isMicEnabledRef.current = isMicEnabled;
201
+ // Reset auto-sent transcript ref when starting new recording
202
+ if (isMicEnabled) {
203
+ lastAutoSentTranscriptRef.current = '';
204
+ // Hide gesture prompt once mic is actually enabled
205
+ setIsAwaitingMicGesture(false);
206
+ // Remove any pending gesture listeners
207
+ if (autoStartGestureCleanupRef.current) {
208
+ autoStartGestureCleanupRef.current();
209
+ autoStartGestureCleanupRef.current = null;
210
+ }
211
+ }
212
+ }, [isMicEnabled]);
213
+ React.useEffect(() => {
214
+ if (typeof window === 'undefined') {
215
+ return;
216
+ }
217
+ if (!useSessionStorageMode) {
218
+ sessionStorage.removeItem(MIC_ENABLED_SESSION_KEY);
219
+ hasSyncedMicStateRef.current = false;
220
+ return;
221
+ }
222
+ if (!hasSyncedMicStateRef.current) {
223
+ return;
224
+ }
225
+ try {
226
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(isMicEnabled));
227
+ }
228
+ catch (error) {
229
+ console.warn('Failed to persist recording state to sessionStorage:', error);
230
+ }
231
+ }, [isMicEnabled, useSessionStorageMode]);
171
232
  React.useEffect(() => {
172
- isRecordingRef.current = isRecording;
173
- }, [isRecording]);
233
+ if (typeof window === 'undefined') {
234
+ return;
235
+ }
236
+ const cleanupAutoStartListeners = () => {
237
+ if (autoStartGestureCleanupRef.current) {
238
+ autoStartGestureCleanupRef.current();
239
+ autoStartGestureCleanupRef.current = null;
240
+ }
241
+ };
242
+ if (!useSessionStorageMode) {
243
+ cleanupAutoStartListeners();
244
+ pendingMicAutoStartRef.current = false;
245
+ setIsAwaitingMicGesture(false);
246
+ return;
247
+ }
248
+ if (hasSyncedMicStateRef.current || isReacquiringMicRef.current) {
249
+ return;
250
+ }
251
+ const rawStoredValue = sessionStorage.getItem(MIC_ENABLED_SESSION_KEY);
252
+ const shouldRestore = rawStoredValue === 'true';
253
+ if (!shouldRestore || isMicEnabledRef.current || isRestoringRecordingRef.current) {
254
+ hasSyncedMicStateRef.current = true;
255
+ setIsAwaitingMicGesture(false);
256
+ return;
257
+ }
258
+ pendingMicAutoStartRef.current = true;
259
+ function scheduleRetryOnUserGesture() {
260
+ if (autoStartGestureCleanupRef.current)
261
+ return;
262
+ setIsAwaitingMicGesture(true);
263
+ const events = ['pointerdown', 'keydown', 'touchstart'];
264
+ const handler = async () => {
265
+ cleanupAutoStartListeners();
266
+ await attemptAutoStart('user-gesture');
267
+ };
268
+ events.forEach(event => window.addEventListener(event, handler, { once: true }));
269
+ autoStartGestureCleanupRef.current = () => {
270
+ events.forEach(event => window.removeEventListener(event, handler));
271
+ autoStartGestureCleanupRef.current = null;
272
+ };
273
+ }
274
+ async function attemptAutoStart(reason) {
275
+ isReacquiringMicRef.current = true;
276
+ // For initial restore, show gesture prompt immediately while checking
277
+ // This gives user early feedback and allows them to click anytime
278
+ if (reason === 'initial') {
279
+ scheduleRetryOnUserGesture();
280
+ }
281
+ if (typeof navigator !== 'undefined' && navigator.mediaDevices?.getUserMedia) {
282
+ try {
283
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
284
+ stream.getTracks().forEach(track => track.stop());
285
+ }
286
+ catch (error) {
287
+ console.error('Failed to reacquire microphone stream:', error);
288
+ const errorName = error?.name || '';
289
+ const errorMessage = error instanceof Error ? error.message : String(error);
290
+ if (reason === 'initial') {
291
+ const isPermissionPermanentlyDenied = errorName === 'NotAllowedError' &&
292
+ /permission.*denied|blocked|not.*granted/i.test(errorMessage);
293
+ if (isPermissionPermanentlyDenied) {
294
+ // Permission permanently denied, hide the prompt
295
+ pendingMicAutoStartRef.current = false;
296
+ setIsAwaitingMicGesture(false);
297
+ try {
298
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(false));
299
+ }
300
+ catch (storageError) {
301
+ console.warn('Failed to reset recording state in sessionStorage:', storageError);
302
+ }
303
+ cleanupAutoStartListeners();
304
+ }
305
+ // Otherwise, keep the prompt showing (already shown above)
306
+ isReacquiringMicRef.current = false;
307
+ return;
308
+ }
309
+ // For user-gesture retry, treat as error
310
+ pendingMicAutoStartRef.current = false;
311
+ setIsAwaitingMicGesture(false);
312
+ try {
313
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(false));
314
+ }
315
+ catch (storageError) {
316
+ console.warn('Failed to reset recording state in sessionStorage:', storageError);
317
+ }
318
+ isReacquiringMicRef.current = false;
319
+ hasSyncedMicStateRef.current = true;
320
+ cleanupAutoStartListeners();
321
+ return;
322
+ }
323
+ }
324
+ else if (reason === 'initial') {
325
+ // getUserMedia not available, prompt already shown above
326
+ isReacquiringMicRef.current = false;
327
+ return;
328
+ }
329
+ isRestoringRecordingRef.current = true;
330
+ // Intercept console.warn to detect AudioContext warnings
331
+ let audioContextWarningDetected = false;
332
+ const originalWarn = console.warn;
333
+ console.warn = (...args) => {
334
+ const message = args.join(' ');
335
+ if (message.includes('AudioContext was not allowed to start')) {
336
+ audioContextWarningDetected = true;
337
+ }
338
+ originalWarn.apply(console, args);
339
+ };
340
+ try {
341
+ await startRecording();
342
+ console.warn = originalWarn;
343
+ // Check mic status after a short delay
344
+ await new Promise(resolve => setTimeout(resolve, 300));
345
+ // If mic started successfully, hide the prompt
346
+ if (isMicEnabledRef.current) {
347
+ pendingMicAutoStartRef.current = false;
348
+ setIsAwaitingMicGesture(false);
349
+ cleanupAutoStartListeners();
350
+ }
351
+ else if (reason === 'initial') {
352
+ // Mic didn't start, keep the prompt showing (already shown earlier)
353
+ isRestoringRecordingRef.current = false;
354
+ return;
355
+ }
356
+ }
357
+ catch (error) {
358
+ console.error('startRecording threw an error:', error);
359
+ try {
360
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(false));
361
+ }
362
+ catch (storageError) {
363
+ console.warn('Failed to reset recording state in sessionStorage:', storageError);
364
+ }
365
+ }
366
+ finally {
367
+ console.warn = originalWarn;
368
+ isRestoringRecordingRef.current = false;
369
+ isReacquiringMicRef.current = false;
370
+ hasSyncedMicStateRef.current = true;
371
+ }
372
+ }
373
+ attemptAutoStart('initial');
374
+ return () => {
375
+ cleanupAutoStartListeners();
376
+ pendingMicAutoStartRef.current = false;
377
+ setIsAwaitingMicGesture(false);
378
+ };
379
+ }, [useSessionStorageMode, startRecording]);
174
380
  const [notification, setNotification] = React.useState({
175
381
  show: false,
176
382
  type: 'info',
@@ -1282,15 +1488,17 @@ isStopRecordingOnSend = false, }) => {
1282
1488
  const handleSubmit = async (e) => {
1283
1489
  e.preventDefault();
1284
1490
  if (isStopRecordingOnSend) {
1285
- while (isRecordingRef.current) {
1491
+ while (isMicEnabledRef.current) {
1286
1492
  stopRecording();
1287
1493
  await sleep(1000);
1288
1494
  }
1289
1495
  }
1290
1496
  else {
1291
- if (isRecordingRef.current) {
1497
+ if (isMicEnabledRef.current) {
1292
1498
  await sleep(1000);
1293
1499
  clearRecording();
1500
+ // Reset auto-sent transcript ref when clearing recording
1501
+ lastAutoSentTranscriptRef.current = '';
1294
1502
  }
1295
1503
  }
1296
1504
  if (!textInputRef.current)
@@ -1816,9 +2024,11 @@ isStopRecordingOnSend = false, }) => {
1816
2024
  isWalletLoading,
1817
2025
  isAutoConnecting,
1818
2026
  notification,
1819
- isRecording,
2027
+ isMicEnabled,
2028
+ isAwaitingMicGesture,
1820
2029
  transcript,
1821
2030
  textInputRef,
2031
+ autoSendEnabled,
1822
2032
  // AI Model related state
1823
2033
  availableModels: effectiveAvailableModels,
1824
2034
  selectedModelIndex,
@@ -1837,6 +2047,7 @@ isStopRecordingOnSend = false, }) => {
1837
2047
  // AI Model related actions
1838
2048
  setSelectedModelIndex,
1839
2049
  setSuggestions,
2050
+ setAutoSendEnabled,
1840
2051
  // Functions
1841
2052
  connectWallet,
1842
2053
  signInWallet,
@@ -81,9 +81,11 @@ export interface ChatBotContextType {
81
81
  autoHide?: boolean;
82
82
  duration?: number;
83
83
  };
84
- isRecording: boolean;
84
+ isMicEnabled: boolean;
85
+ isAwaitingMicGesture: boolean;
85
86
  transcript: string;
86
87
  textInputRef: React.RefObject<HTMLInputElement>;
88
+ autoSendEnabled: boolean;
87
89
  availableModels: AIModel[];
88
90
  selectedModelIndex: number;
89
91
  showModelSelector: boolean;
@@ -98,6 +100,7 @@ export interface ChatBotContextType {
98
100
  setNotification: (notification: any) => void;
99
101
  setSelectedModelIndex: (index: number) => void;
100
102
  setSuggestions: (suggestions: PresetMessage[]) => void;
103
+ setAutoSendEnabled: (enabled: boolean) => void;
101
104
  connectWallet: (autoShowSignInMessage?: boolean) => Promise<string | false | undefined>;
102
105
  signInWallet: (autoShowSuccessMessage?: boolean) => Promise<void>;
103
106
  sendMessage: (content: string, retryCount?: number) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"ChatBotTypes.d.ts","sourceRoot":"","sources":["../../../src/components/types/ChatBotTypes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ChatBotTypes.d.ts","sourceRoot":"","sources":["../../../src/components/types/ChatBotTypes.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ /** @jsxImportSource @emotion/react */
1
2
  import React from 'react';
2
3
  interface BerifyMeModalProps {
3
4
  isOpen: boolean;
@@ -10,6 +11,10 @@ declare global {
10
11
  interface Window {
11
12
  BerifyMeSDK?: {
12
13
  modal: any;
14
+ environment: {
15
+ Staging: any;
16
+ Production: any;
17
+ };
13
18
  };
14
19
  }
15
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BerifyMeModal.d.ts","sourceRoot":"","sources":["../../../src/components/ui/BerifyMeModal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAGjD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,sBAAsB,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CAChC;AASD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAoHtD,CAAC;AAGF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,WAAW,CAAC,EAAE;YACZ,KAAK,EAAE,GAAG,CAAC;SACZ,CAAC;KACH;CACF"}
1
+ {"version":3,"file":"BerifyMeModal.d.ts","sourceRoot":"","sources":["../../../src/components/ui/BerifyMeModal.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAA4B,MAAM,OAAO,CAAC;AAIjD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,sBAAsB,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CAChC;AASD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsHtD,CAAC;AAGF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,WAAW,CAAC,EAAE;YACZ,KAAK,EAAE,GAAG,CAAC;YACX,WAAW,EAAE;gBACX,OAAO,EAAE,GAAG,CAAC;gBACb,UAAU,EAAE,GAAG,CAAC;aACjB,CAAC;SACH,CAAC;KACH;CACF"}
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
2
  /** @jsxImportSource @emotion/react */
3
- import { css } from '@emotion/react';
4
3
  import React, { useEffect, useRef } from 'react';
4
+ import { css } from '@emotion/react';
5
5
  import { modalOverlay } from './styles/isolatedStyles';
6
6
  // Built-in BerifyMe configuration
7
7
  const BUILT_IN_BERIFYME_CONFIG = {
8
- apiKeyId: 'idv_XjN7vvuQVfHnaUkVQAEhdTuxAsQeKoa9',
9
- secretKey: 'aaa444b1-087c-4b17-821a-9a6974286905',
10
- environment: 'idv'
8
+ apiKeyId: 'staging_83dc824cb50b4e76747e352b4228f2ee',
9
+ secretKey: 'ee96158a-9ae2-42ad-8c30-f7b23edbf258',
10
+ environment: 'staging'
11
11
  };
12
12
  export const BerifyMeModal = ({ isOpen, onClose, onVerificationComplete, mode = 'built-in' }) => {
13
13
  const modalRef = useRef(null);
@@ -71,12 +71,14 @@ export const BerifyMeModal = ({ isOpen, onClose, onVerificationComplete, mode =
71
71
  const redirectUrl = `${window.location.origin}${window.location.pathname}?isAutoConnect=true&method=berifyme&returnUrl=${encodeURIComponent(currentUrl)}`;
72
72
  // Create BerifyMe modal using built-in config
73
73
  const berifyMeModal = window.BerifyMeSDK.modal;
74
+ // Always use staging environment for now
75
+ const environment = window.BerifyMeSDK.environment.Staging;
74
76
  // Use React 18's createRoot
75
77
  const { createRoot } = require('react-dom/client');
76
78
  const root = createRoot(modalRef.current);
77
79
  berifyMeModalRef.current = root;
78
80
  root.render(React.createElement(berifyMeModal, {
79
- environment: BUILT_IN_BERIFYME_CONFIG.environment,
81
+ environment,
80
82
  apiKeyId: BUILT_IN_BERIFYME_CONFIG.apiKeyId,
81
83
  secretKey: BUILT_IN_BERIFYME_CONFIG.secretKey,
82
84
  redirectUrl,
@@ -1 +1 @@
1
- {"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../../src/components/ui/MessageInput.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAoGhC,CAAC"}
1
+ {"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../../src/components/ui/MessageInput.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAwKhC,CAAC"}
@@ -1,13 +1,14 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
2
  /** @jsxImportSource @emotion/react */
3
3
  import { css } from '@emotion/react';
4
4
  import MicIcon from '@mui/icons-material/Mic';
5
5
  import MicOffIcon from '@mui/icons-material/MicOff';
6
- import { IconButton, InputBase } from '@mui/material';
6
+ import SendIcon from '@mui/icons-material/Send';
7
+ import { IconButton, InputBase, Tooltip } from '@mui/material';
7
8
  import { useChatBot } from '../context/ChatBotContext';
8
9
  import { actionButton } from './styles/isolatedStyles';
9
10
  export const MessageInput = () => {
10
- const { inputValue, setInputValue, isLoading, isAITLoading, handleSubmit, isRecording, startRecording, stopRecording, textInputRef, props: { placeholder = 'Type a message...' } } = useChatBot();
11
+ const { inputValue, setInputValue, isLoading, isAITLoading, handleSubmit, isMicEnabled, isAwaitingMicGesture, startRecording, stopRecording, textInputRef, autoSendEnabled, setAutoSendEnabled, props: { placeholder = 'Type a message...' } } = useChatBot();
11
12
  const isDisabled = isLoading || isAITLoading;
12
13
  const inputPlaceholder = isAITLoading ? 'Loading wallet configuration...' : placeholder;
13
14
  const handleKeyPress = (e) => {
@@ -16,47 +17,68 @@ export const MessageInput = () => {
16
17
  handleSubmit(e);
17
18
  }
18
19
  };
19
- return (_jsxs("div", { css: css `
20
- padding: 15px !important;
21
- display: flex !important;
22
- align-items: center !important;
23
- gap: 10px !important;
24
- border-top: 1px solid #eee !important;
25
- `, children: [_jsx(InputBase, { value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: handleKeyPress, placeholder: inputPlaceholder, fullWidth: true, inputProps: {
26
- ref: textInputRef
27
- }, endAdornment: _jsx(IconButton, { onClick: () => isRecording ? stopRecording() : startRecording(), children: isRecording ? _jsx(MicIcon, {}) : _jsx(MicOffIcon, {}) }), css: css `
28
- flex: 1 !important;
29
- padding: 10px !important;
30
- border: 1px solid #ddd !important;
31
- border-radius: 20px !important;
32
- outline: none !important;
33
- font-size: 14px !important;
34
- background-color: #fff !important;
35
- height: 40px !important;
36
- box-sizing: border-box !important;
37
- ` }), _jsxs("button", { onClick: (e) => handleSubmit(e), disabled: isDisabled || !inputValue.trim(), css: css `
38
- ${actionButton}
39
- padding: 10px 20px !important;
40
- border-radius: 20px !important;
41
- position: relative !important;
20
+ return (_jsxs(_Fragment, { children: [isAwaitingMicGesture && (_jsx("div", { role: "status", "aria-live": "polite", css: css `
21
+ margin: 12px 15px 0 !important;
22
+ padding: 10px 14px !important;
23
+ border-radius: 8px !important;
24
+ border: 1px solid #ffeeba !important;
25
+ background-color: #fff3cd !important;
26
+ color: #856404 !important;
27
+ font-size: 13px !important;
28
+ line-height: 1.4 !important;
29
+ z-index: 1000 !important;
30
+ position: relative !important;
31
+ display: block !important;
32
+ visibility: visible !important;
33
+ opacity: 1 !important;
34
+ `, children: "\u26A0\uFE0F The microphone needs a user interaction to re-enable. Please click on the page or press any key." })), _jsxs("div", { css: css `
35
+ padding: 15px !important;
42
36
  display: flex !important;
43
37
  align-items: center !important;
44
- justify-content: center !important;
45
- height: 40px !important;
46
- box-sizing: border-box !important;
47
-
48
- &:disabled {
49
- background-color: #e9ecef !important;
50
- color: #6c757d !important;
51
- cursor: not-allowed !important;
52
- }
53
-
54
- &:hover:not(:disabled) {
55
- background-color: #0056b3 !important;
56
- }
57
- `, children: ["Send", (isLoading || isAITLoading) && (_jsx("span", { css: css `
58
- margin-left: 8px !important;
38
+ gap: 10px !important;
39
+ border-top: 1px solid #eee !important;
40
+ `, children: [_jsx(InputBase, { value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: handleKeyPress, placeholder: inputPlaceholder, fullWidth: true, inputProps: {
41
+ ref: textInputRef
42
+ }, endAdornment: _jsxs(_Fragment, { children: [isMicEnabled && (_jsx(Tooltip, { title: autoSendEnabled ? 'Auto send enabled' : 'Auto send disabled', children: _jsx(IconButton, { size: "small", onClick: (e) => {
43
+ e.stopPropagation();
44
+ setAutoSendEnabled(!autoSendEnabled);
45
+ }, css: css `
46
+ padding: 4px !important;
47
+ margin-right: 4px !important;
48
+ color: ${autoSendEnabled ? '#1976d2' : '#9e9e9e'} !important;
49
+ `, children: _jsx(SendIcon, { fontSize: "small" }) }) })), _jsx(IconButton, { onClick: () => (isMicEnabled ? stopRecording() : startRecording()), children: isMicEnabled ? _jsx(MicIcon, {}) : _jsx(MicOffIcon, {}) })] }), css: css `
50
+ flex: 1 !important;
51
+ padding: 10px !important;
52
+ border: 1px solid #ddd !important;
53
+ border-radius: 20px !important;
54
+ outline: none !important;
55
+ font-size: 14px !important;
56
+ background-color: #fff !important;
57
+ height: 40px !important;
58
+ box-sizing: border-box !important;
59
+ ` }), _jsxs("button", { onClick: (e) => handleSubmit(e), disabled: isDisabled || !inputValue.trim(), css: css `
60
+ ${actionButton}
61
+ padding: 10px 20px !important;
62
+ border-radius: 20px !important;
63
+ position: relative !important;
59
64
  display: flex !important;
60
65
  align-items: center !important;
61
- `, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 50 50", children: _jsx("circle", { cx: "25", cy: "25", r: "20", fill: "none", stroke: "#fff", strokeWidth: "4", strokeDasharray: "31.4 31.4", strokeLinecap: "round", children: _jsx("animateTransform", { attributeName: "transform", type: "rotate", from: "0 25 25", to: "360 25 25", dur: "1s", repeatCount: "indefinite" }) }) }) }))] })] }));
66
+ justify-content: center !important;
67
+ height: 40px !important;
68
+ box-sizing: border-box !important;
69
+
70
+ &:disabled {
71
+ background-color: #e9ecef !important;
72
+ color: #6c757d !important;
73
+ cursor: not-allowed !important;
74
+ }
75
+
76
+ &:hover:not(:disabled) {
77
+ background-color: #0056b3 !important;
78
+ }
79
+ `, children: ["Send", (isLoading || isAITLoading) && (_jsx("span", { css: css `
80
+ margin-left: 8px !important;
81
+ display: flex !important;
82
+ align-items: center !important;
83
+ `, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 50 50", children: _jsx("circle", { cx: "25", cy: "25", r: "20", fill: "none", stroke: "#fff", strokeWidth: "4", strokeDasharray: "31.4 31.4", strokeLinecap: "round", children: _jsx("animateTransform", { attributeName: "transform", type: "rotate", from: "0 25 25", to: "360 25 25", dur: "1s", repeatCount: "indefinite" }) }) }) }))] })] })] }));
62
84
  };
@@ -2,10 +2,10 @@ import { SpeechRecognizer } from 'microsoft-cognitiveservices-speech-sdk';
2
2
  import { Dispatch, SetStateAction } from 'react';
3
3
  /**
4
4
  * Start speech recognition
5
- * - partialTranscript: 暫存逐字 (recognizing)
6
- * - setSpeechToTextArray: 完整句子陣列 (recognized)
5
+ * - partialTranscript: temporary partial transcription (recognizing)
6
+ * - setSpeechToTextArray: finalized sentence array (recognized)
7
7
  */
8
- export declare const startSpeechToTextFromMic: (setSpeechToTextArray: Dispatch<SetStateAction<string[]>>, // 完整句子
8
+ export declare const startSpeechToTextFromMic: (setSpeechToTextArray: Dispatch<SetStateAction<string[]>>, // finalized sentences
9
9
  config: {
10
10
  apiKey: string;
11
11
  apiSecret: string;
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../../src/core/lib/useSpeechToTextFromMic/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,gBAAgB,EACjB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAIjD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,sBAAsB,QAAQ,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,EAAI,OAAO;AACnE,QAAQ;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAC7C,YAAY,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,EAC5C,UAAU,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,EACxC,uBAAuB,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,KACtD,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAsDtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,YAAY,gBAAgB,GAAG,SAAS,SAIvE,CAAC;AAEF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;GAwBxE"}
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../../src/core/lib/useSpeechToTextFromMic/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,gBAAgB,EACjB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAIjD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,sBAAsB,QAAQ,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,EAAI,sBAAsB;AAClF,QAAQ;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAC7C,YAAY,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,EAC5C,UAAU,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,EACxC,uBAAuB,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,KACtD,OAAO,CAAC,gBAAgB,GAAG,SAAS,CA2DtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,YAAY,gBAAgB,GAAG,SAAS,SAIvE,CAAC;AAEF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;GAwBxE"}
@@ -3,11 +3,11 @@ import Cookie from 'universal-cookie';
3
3
  import { createNxtlinqApi } from '../../../api/nxtlinq-api';
4
4
  /**
5
5
  * Start speech recognition
6
- * - partialTranscript: 暫存逐字 (recognizing)
7
- * - setSpeechToTextArray: 完整句子陣列 (recognized)
6
+ * - partialTranscript: temporary partial transcription (recognizing)
7
+ * - setSpeechToTextArray: finalized sentence array (recognized)
8
8
  */
9
- export const startSpeechToTextFromMic = async (setSpeechToTextArray, // 完整句子
10
- config, historyRef, indexRef, setPartialTranscript // 新增:暫存逐字
9
+ export const startSpeechToTextFromMic = async (setSpeechToTextArray, // finalized sentences
10
+ config, historyRef, indexRef, setPartialTranscript // temporary partial transcription state updater
11
11
  ) => {
12
12
  const tokenRes = await getTokenOrRefresh(config.apiKey, config.apiSecret);
13
13
  if (!tokenRes.authToken || !tokenRes.region) {
@@ -16,25 +16,25 @@ config, historyRef, indexRef, setPartialTranscript // ✅ 新增:暫存逐字
16
16
  }
17
17
  const speechConfig = SpeechConfig.fromAuthorizationToken(tokenRes.authToken, tokenRes.region);
18
18
  speechConfig.speechRecognitionLanguage = 'en-US';
19
- // 靜音判斷與 segmentation 設定
19
+ // silence detection and segmentation configuration
20
20
  speechConfig.setProperty(PropertyId.SpeechServiceConnection_InitialSilenceTimeoutMs, '10000');
21
21
  speechConfig.setProperty(PropertyId.SpeechServiceConnection_EndSilenceTimeoutMs, '86400000');
22
22
  speechConfig.setProperty(PropertyId.Speech_SegmentationSilenceTimeoutMs, '1000');
23
23
  const audioConfig = AudioConfig.fromDefaultMicrophoneInput();
24
24
  const recognizer = new SpeechRecognizer(speechConfig, audioConfig);
25
- // 暫存逐字
25
+ // temporary partial transcription
26
26
  recognizer.recognizing = (_s, e) => {
27
27
  if (setPartialTranscript) {
28
28
  setPartialTranscript(e.result.text);
29
29
  }
30
30
  };
31
- // 完整句子
31
+ // finalized sentences
32
32
  recognizer.recognized = (_s, e) => {
33
33
  if (e.result.reason === ResultReason.RecognizedSpeech) {
34
34
  const text = e.result.text;
35
35
  historyRef.current[indexRef.current] = text;
36
36
  indexRef.current += 1;
37
- // 關鍵:只把「本次完整句子」回推給 UI
37
+ // return only the latest finalized sentence to the UI
38
38
  setSpeechToTextArray([text]);
39
39
  if (setPartialTranscript)
40
40
  setPartialTranscript('');
@@ -47,10 +47,12 @@ config, historyRef, indexRef, setPartialTranscript // ✅ 新增:暫存逐字
47
47
  console.error(`Error details: ${e.errorDetails}`);
48
48
  }
49
49
  };
50
- recognizer.sessionStopped = () => {
51
- console.log('Speech recognition session stopped');
52
- };
53
- recognizer.startContinuousRecognitionAsync();
50
+ await new Promise((resolve, reject) => {
51
+ recognizer.startContinuousRecognitionAsync(() => resolve(), error => {
52
+ console.error('Failed to start continuous speech recognition:', error);
53
+ reject(error);
54
+ });
55
+ });
54
56
  return recognizer;
55
57
  };
56
58
  /**
@@ -7,7 +7,7 @@ type UseSpeechToTextFromMicResult = {
7
7
  start: () => Promise<void>;
8
8
  stop: () => void;
9
9
  clear: () => void;
10
- isRecording: boolean;
10
+ isMicEnabled: boolean;
11
11
  transcript: string;
12
12
  partialTranscript: string;
13
13
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/lib/useSpeechToTextFromMic/index.ts"],"names":[],"mappings":"AAIA,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,KAAK,4BAA4B,GAAG;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,SAAS,EACT,mBAA0B,GAC3B,EAAE,KAAK,GAAG,4BAA4B,CAuEtC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/lib/useSpeechToTextFromMic/index.ts"],"names":[],"mappings":"AAIA,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,KAAK,4BAA4B,GAAG;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,SAAS,EACT,mBAA0B,GAC3B,EAAE,KAAK,GAAG,4BAA4B,CA4EtC"}