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

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,CAovElD,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] = React.useState(false);
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,211 @@ 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
+ }
205
+ else {
206
+ // Reset auto send when mic is disabled
207
+ setAutoSendEnabled(false);
208
+ }
209
+ }, [isMicEnabled]);
171
210
  React.useEffect(() => {
172
- isRecordingRef.current = isRecording;
173
- }, [isRecording]);
211
+ if (typeof window === 'undefined') {
212
+ return;
213
+ }
214
+ if (!useSessionStorageMode) {
215
+ sessionStorage.removeItem(MIC_ENABLED_SESSION_KEY);
216
+ hasSyncedMicStateRef.current = false;
217
+ return;
218
+ }
219
+ if (!hasSyncedMicStateRef.current) {
220
+ return;
221
+ }
222
+ try {
223
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(isMicEnabled));
224
+ }
225
+ catch (error) {
226
+ console.warn('Failed to persist recording state to sessionStorage:', error);
227
+ }
228
+ }, [isMicEnabled, useSessionStorageMode]);
229
+ React.useEffect(() => {
230
+ if (typeof window === 'undefined') {
231
+ return;
232
+ }
233
+ const cleanupAutoStartListeners = () => {
234
+ if (autoStartGestureCleanupRef.current) {
235
+ autoStartGestureCleanupRef.current();
236
+ autoStartGestureCleanupRef.current = null;
237
+ }
238
+ };
239
+ if (!useSessionStorageMode) {
240
+ cleanupAutoStartListeners();
241
+ pendingMicAutoStartRef.current = false;
242
+ setIsAwaitingMicGesture(false);
243
+ return;
244
+ }
245
+ if (hasSyncedMicStateRef.current || isReacquiringMicRef.current) {
246
+ return;
247
+ }
248
+ const rawStoredValue = sessionStorage.getItem(MIC_ENABLED_SESSION_KEY);
249
+ const shouldRestore = rawStoredValue === 'true';
250
+ if (!shouldRestore || isMicEnabledRef.current || isRestoringRecordingRef.current) {
251
+ hasSyncedMicStateRef.current = true;
252
+ setIsAwaitingMicGesture(false);
253
+ return;
254
+ }
255
+ pendingMicAutoStartRef.current = true;
256
+ function scheduleRetryOnUserGesture() {
257
+ if (autoStartGestureCleanupRef.current)
258
+ return;
259
+ setIsAwaitingMicGesture(true);
260
+ const events = ['pointerdown', 'keydown', 'touchstart'];
261
+ const handler = async () => {
262
+ cleanupAutoStartListeners();
263
+ await attemptAutoStart('user-gesture');
264
+ };
265
+ events.forEach(event => window.addEventListener(event, handler, { once: true }));
266
+ autoStartGestureCleanupRef.current = () => {
267
+ events.forEach(event => window.removeEventListener(event, handler));
268
+ autoStartGestureCleanupRef.current = null;
269
+ };
270
+ }
271
+ async function attemptAutoStart(reason) {
272
+ isReacquiringMicRef.current = true;
273
+ // For initial restore, show gesture prompt immediately while checking
274
+ // This gives user early feedback and allows them to click anytime
275
+ if (reason === 'initial') {
276
+ scheduleRetryOnUserGesture();
277
+ }
278
+ if (typeof navigator !== 'undefined' && navigator.mediaDevices?.getUserMedia) {
279
+ try {
280
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
281
+ stream.getTracks().forEach(track => track.stop());
282
+ }
283
+ catch (error) {
284
+ console.error('Failed to reacquire microphone stream:', error);
285
+ const errorName = error?.name || '';
286
+ const errorMessage = error instanceof Error ? error.message : String(error);
287
+ if (reason === 'initial') {
288
+ const isPermissionPermanentlyDenied = errorName === 'NotAllowedError' &&
289
+ /permission.*denied|blocked|not.*granted/i.test(errorMessage);
290
+ if (isPermissionPermanentlyDenied) {
291
+ // Permission permanently denied, hide the prompt
292
+ pendingMicAutoStartRef.current = false;
293
+ setIsAwaitingMicGesture(false);
294
+ try {
295
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(false));
296
+ }
297
+ catch (storageError) {
298
+ console.warn('Failed to reset recording state in sessionStorage:', storageError);
299
+ }
300
+ cleanupAutoStartListeners();
301
+ }
302
+ // Otherwise, keep the prompt showing (already shown above)
303
+ isReacquiringMicRef.current = false;
304
+ return;
305
+ }
306
+ // For user-gesture retry, treat as error
307
+ pendingMicAutoStartRef.current = false;
308
+ setIsAwaitingMicGesture(false);
309
+ try {
310
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(false));
311
+ }
312
+ catch (storageError) {
313
+ console.warn('Failed to reset recording state in sessionStorage:', storageError);
314
+ }
315
+ isReacquiringMicRef.current = false;
316
+ hasSyncedMicStateRef.current = true;
317
+ cleanupAutoStartListeners();
318
+ return;
319
+ }
320
+ }
321
+ else if (reason === 'initial') {
322
+ // getUserMedia not available, prompt already shown above
323
+ isReacquiringMicRef.current = false;
324
+ return;
325
+ }
326
+ isRestoringRecordingRef.current = true;
327
+ // Intercept console.warn to detect AudioContext warnings
328
+ let audioContextWarningDetected = false;
329
+ const originalWarn = console.warn;
330
+ console.warn = (...args) => {
331
+ const message = args.join(' ');
332
+ if (message.includes('AudioContext was not allowed to start')) {
333
+ audioContextWarningDetected = true;
334
+ }
335
+ originalWarn.apply(console, args);
336
+ };
337
+ try {
338
+ await startRecording();
339
+ console.warn = originalWarn;
340
+ // Check mic status after a short delay
341
+ await new Promise(resolve => setTimeout(resolve, 300));
342
+ // If mic started successfully, hide the prompt
343
+ if (isMicEnabled) {
344
+ pendingMicAutoStartRef.current = false;
345
+ setIsAwaitingMicGesture(false);
346
+ cleanupAutoStartListeners();
347
+ }
348
+ else if (reason === 'initial') {
349
+ // Mic didn't start, keep the prompt showing (already shown earlier)
350
+ isRestoringRecordingRef.current = false;
351
+ return;
352
+ }
353
+ }
354
+ catch (error) {
355
+ console.error('startRecording threw an error:', error);
356
+ try {
357
+ sessionStorage.setItem(MIC_ENABLED_SESSION_KEY, JSON.stringify(false));
358
+ }
359
+ catch (storageError) {
360
+ console.warn('Failed to reset recording state in sessionStorage:', storageError);
361
+ }
362
+ }
363
+ finally {
364
+ console.warn = originalWarn;
365
+ isRestoringRecordingRef.current = false;
366
+ isReacquiringMicRef.current = false;
367
+ hasSyncedMicStateRef.current = true;
368
+ }
369
+ }
370
+ attemptAutoStart('initial');
371
+ return () => {
372
+ cleanupAutoStartListeners();
373
+ pendingMicAutoStartRef.current = false;
374
+ setIsAwaitingMicGesture(false);
375
+ };
376
+ }, [useSessionStorageMode, startRecording]);
174
377
  const [notification, setNotification] = React.useState({
175
378
  show: false,
176
379
  type: 'info',
@@ -1282,15 +1485,17 @@ isStopRecordingOnSend = false, }) => {
1282
1485
  const handleSubmit = async (e) => {
1283
1486
  e.preventDefault();
1284
1487
  if (isStopRecordingOnSend) {
1285
- while (isRecordingRef.current) {
1488
+ while (isMicEnabledRef.current) {
1286
1489
  stopRecording();
1287
1490
  await sleep(1000);
1288
1491
  }
1289
1492
  }
1290
1493
  else {
1291
- if (isRecordingRef.current) {
1494
+ if (isMicEnabledRef.current) {
1292
1495
  await sleep(1000);
1293
1496
  clearRecording();
1497
+ // Reset auto-sent transcript ref when clearing recording
1498
+ lastAutoSentTranscriptRef.current = '';
1294
1499
  }
1295
1500
  }
1296
1501
  if (!textInputRef.current)
@@ -1816,9 +2021,11 @@ isStopRecordingOnSend = false, }) => {
1816
2021
  isWalletLoading,
1817
2022
  isAutoConnecting,
1818
2023
  notification,
1819
- isRecording,
2024
+ isMicEnabled,
2025
+ isAwaitingMicGesture,
1820
2026
  transcript,
1821
2027
  textInputRef,
2028
+ autoSendEnabled,
1822
2029
  // AI Model related state
1823
2030
  availableModels: effectiveAvailableModels,
1824
2031
  selectedModelIndex,
@@ -1837,6 +2044,7 @@ isStopRecordingOnSend = false, }) => {
1837
2044
  // AI Model related actions
1838
2045
  setSelectedModelIndex,
1839
2046
  setSuggestions,
2047
+ setAutoSendEnabled,
1840
2048
  // Functions
1841
2049
  connectWallet,
1842
2050
  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"}