@bytexbyte/nxtlinq-ai-agent-web-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/context/NxtlinqAgentContext.d.ts +12 -0
- package/dist/context/NxtlinqAgentContext.d.ts.map +1 -0
- package/dist/context/NxtlinqAgentContext.js +33 -0
- package/dist/createNxtlinqAgent.d.ts +9 -0
- package/dist/createNxtlinqAgent.d.ts.map +1 -0
- package/dist/createNxtlinqAgent.js +19 -0
- package/dist/hooks/useNxtlinqAgent.d.ts +18 -0
- package/dist/hooks/useNxtlinqAgent.d.ts.map +1 -0
- package/dist/hooks/useNxtlinqAgent.js +23 -0
- package/dist/hooks/useNxtlinqVoice.d.ts +21 -0
- package/dist/hooks/useNxtlinqVoice.d.ts.map +1 -0
- package/dist/hooks/useNxtlinqVoice.js +75 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/legacy/api/nxtlinq-api.d.ts +8 -0
- package/dist/legacy/api/nxtlinq-api.d.ts.map +1 -0
- package/dist/legacy/api/nxtlinq-api.js +13 -0
- package/dist/legacy/api/voice.d.ts +11 -0
- package/dist/legacy/api/voice.d.ts.map +1 -0
- package/dist/legacy/api/voice.js +26 -0
- package/dist/legacy/core/lib/messageHistory.d.ts +2 -0
- package/dist/legacy/core/lib/messageHistory.d.ts.map +1 -0
- package/dist/legacy/core/lib/messageHistory.js +1 -0
- package/dist/legacy/core/lib/textToSpeech.d.ts +14 -0
- package/dist/legacy/core/lib/textToSpeech.d.ts.map +1 -0
- package/dist/legacy/core/lib/textToSpeech.js +82 -0
- package/dist/legacy/core/lib/useDraggable.d.ts +15 -0
- package/dist/legacy/core/lib/useDraggable.d.ts.map +1 -0
- package/dist/legacy/core/lib/useDraggable.js +158 -0
- package/dist/legacy/core/lib/useLocalStorage.d.ts +11 -0
- package/dist/legacy/core/lib/useLocalStorage.d.ts.map +1 -0
- package/dist/legacy/core/lib/useLocalStorage.js +83 -0
- package/dist/legacy/core/lib/useResizable.d.ts +17 -0
- package/dist/legacy/core/lib/useResizable.d.ts.map +1 -0
- package/dist/legacy/core/lib/useResizable.js +203 -0
- package/dist/legacy/core/lib/useSessionStorage.d.ts +11 -0
- package/dist/legacy/core/lib/useSessionStorage.d.ts.map +1 -0
- package/dist/legacy/core/lib/useSessionStorage.js +37 -0
- package/dist/legacy/core/lib/useSpeechToTextFromMic/helper.d.ts +26 -0
- package/dist/legacy/core/lib/useSpeechToTextFromMic/helper.d.ts.map +1 -0
- package/dist/legacy/core/lib/useSpeechToTextFromMic/helper.js +102 -0
- package/dist/legacy/core/lib/useSpeechToTextFromMic/index.d.ts +16 -0
- package/dist/legacy/core/lib/useSpeechToTextFromMic/index.d.ts.map +1 -0
- package/dist/legacy/core/lib/useSpeechToTextFromMic/index.js +92 -0
- package/dist/legacy/core/lib/useVoiceMode.d.ts +32 -0
- package/dist/legacy/core/lib/useVoiceMode.d.ts.map +1 -0
- package/dist/legacy/core/lib/useVoiceMode.js +373 -0
- package/dist/legacy/core/metakeepClient.d.ts +4 -0
- package/dist/legacy/core/metakeepClient.d.ts.map +1 -0
- package/dist/legacy/core/metakeepClient.js +10 -0
- package/dist/legacy/core/utils/aitUtils.d.ts +31 -0
- package/dist/legacy/core/utils/aitUtils.d.ts.map +1 -0
- package/dist/legacy/core/utils/aitUtils.js +35 -0
- package/dist/legacy/core/utils/ethersUtils.d.ts +8 -0
- package/dist/legacy/core/utils/ethersUtils.d.ts.map +1 -0
- package/dist/legacy/core/utils/ethersUtils.js +19 -0
- package/dist/legacy/core/utils/index.d.ts +3 -0
- package/dist/legacy/core/utils/index.d.ts.map +1 -0
- package/dist/legacy/core/utils/index.js +4 -0
- package/dist/legacy/core/utils/notificationUtils.d.ts +29 -0
- package/dist/legacy/core/utils/notificationUtils.d.ts.map +1 -0
- package/dist/legacy/core/utils/notificationUtils.js +47 -0
- package/dist/legacy/core/utils/urlUtils.d.ts +25 -0
- package/dist/legacy/core/utils/urlUtils.d.ts.map +1 -0
- package/dist/legacy/core/utils/urlUtils.js +135 -0
- package/dist/legacy/core/utils/walletTextUtils.d.ts +14 -0
- package/dist/legacy/core/utils/walletTextUtils.d.ts.map +1 -0
- package/dist/legacy/core/utils/walletTextUtils.js +23 -0
- package/dist/legacy/core/utils/walletUtils.d.ts +10 -0
- package/dist/legacy/core/utils/walletUtils.d.ts.map +1 -0
- package/dist/legacy/core/utils/walletUtils.js +38 -0
- package/dist/legacy/index.d.ts +19 -0
- package/dist/legacy/index.d.ts.map +1 -0
- package/dist/legacy/index.js +16 -0
- package/dist/ports/createWebPlatformPorts.d.ts +13 -0
- package/dist/ports/createWebPlatformPorts.d.ts.map +1 -0
- package/dist/ports/createWebPlatformPorts.js +25 -0
- package/dist/utils/fileToAttachment.d.ts +4 -0
- package/dist/utils/fileToAttachment.d.ts.map +1 -0
- package/dist/utils/fileToAttachment.js +28 -0
- package/dist/voice/useVoiceSilenceCommit.d.ts +11 -0
- package/dist/voice/useVoiceSilenceCommit.d.ts.map +1 -0
- package/dist/voice/useVoiceSilenceCommit.js +68 -0
- package/dist/voice/useVoiceTranscriptMessages.d.ts +16 -0
- package/dist/voice/useVoiceTranscriptMessages.d.ts.map +1 -0
- package/dist/voice/useVoiceTranscriptMessages.js +134 -0
- package/dist/voice/useWsRealtimeAudio.d.ts +18 -0
- package/dist/voice/useWsRealtimeAudio.d.ts.map +1 -0
- package/dist/voice/useWsRealtimeAudio.js +115 -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 +138 -0
- package/dist/voice/ws/BrowserWsPcmRecorder.d.ts +19 -0
- package/dist/voice/ws/BrowserWsPcmRecorder.d.ts.map +1 -0
- package/dist/voice/ws/BrowserWsPcmRecorder.js +76 -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/dist/webAgentDefaults.d.ts +9 -0
- package/dist/webAgentDefaults.d.ts.map +1 -0
- package/dist/webAgentDefaults.js +9 -0
- package/package.json +55 -0
- package/src/context/NxtlinqAgentContext.tsx +79 -0
- package/src/createNxtlinqAgent.ts +36 -0
- package/src/hooks/useNxtlinqAgent.ts +73 -0
- package/src/hooks/useNxtlinqVoice.ts +143 -0
- package/src/index.ts +84 -0
- package/src/legacy/api/nxtlinq-api.ts +32 -0
- package/src/legacy/api/voice.ts +72 -0
- package/src/legacy/core/lib/messageHistory.ts +6 -0
- package/src/legacy/core/lib/textToSpeech.ts +127 -0
- package/src/legacy/core/lib/useDraggable.ts +193 -0
- package/src/legacy/core/lib/useLocalStorage.ts +89 -0
- package/src/legacy/core/lib/useResizable.ts +256 -0
- package/src/legacy/core/lib/useSessionStorage.ts +43 -0
- package/src/legacy/core/lib/useSpeechToTextFromMic/helper.ts +132 -0
- package/src/legacy/core/lib/useSpeechToTextFromMic/index.ts +126 -0
- package/src/legacy/core/lib/useVoiceMode.ts +407 -0
- package/src/legacy/core/metakeepClient.ts +12 -0
- package/src/legacy/core/utils/aitUtils.ts +55 -0
- package/src/legacy/core/utils/ethersUtils.ts +24 -0
- package/src/legacy/core/utils/index.ts +5 -0
- package/src/legacy/core/utils/notificationUtils.ts +64 -0
- package/src/legacy/core/utils/urlUtils.ts +160 -0
- package/src/legacy/core/utils/walletTextUtils.ts +26 -0
- package/src/legacy/core/utils/walletUtils.ts +53 -0
- package/src/legacy/index.ts +35 -0
- package/src/ports/createWebPlatformPorts.ts +44 -0
- package/src/utils/fileToAttachment.ts +32 -0
- package/src/voice/useVoiceSilenceCommit.ts +84 -0
- package/src/voice/useVoiceTranscriptMessages.ts +184 -0
- package/src/voice/useWsRealtimeAudio.ts +141 -0
- package/src/voice/voiceMicConstants.ts +13 -0
- package/src/voice/ws/BrowserWsPcmPlayer.ts +139 -0
- package/src/voice/ws/BrowserWsPcmRecorder.ts +83 -0
- package/src/voice/ws/float32ToPcm16.ts +8 -0
- package/src/voice/ws/voiceSilenceConstants.ts +4 -0
- package/src/voice/ws/wsRealtimeConstants.ts +1 -0
- package/src/webAgentDefaults.ts +12 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Custom hook for managing localStorage with React state and cross-tab synchronization
|
|
4
|
+
* Automatically syncs changes across browser tabs using StorageEvent
|
|
5
|
+
*
|
|
6
|
+
* @param key - The key to store the value under in localStorage
|
|
7
|
+
* @param defaultValue - The default value to use if no value is stored
|
|
8
|
+
* @returns [storedValue, setStoredValue, isInitialized]
|
|
9
|
+
*/
|
|
10
|
+
export default function useLocalStorage<T>(key: string, defaultValue: T): [T, Dispatch<SetStateAction<T>>, boolean];
|
|
11
|
+
//# sourceMappingURL=useLocalStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLocalStorage.d.ts","sourceRoot":"","sources":["../../../../src/legacy/core/lib/useLocalStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAA+B,MAAM,OAAO,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CA8ElH"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Custom hook for managing localStorage with React state and cross-tab synchronization
|
|
4
|
+
* Automatically syncs changes across browser tabs using StorageEvent
|
|
5
|
+
*
|
|
6
|
+
* @param key - The key to store the value under in localStorage
|
|
7
|
+
* @param defaultValue - The default value to use if no value is stored
|
|
8
|
+
* @returns [storedValue, setStoredValue, isInitialized]
|
|
9
|
+
*/
|
|
10
|
+
export default function useLocalStorage(key, defaultValue) {
|
|
11
|
+
const [storedValue, setStoredValue] = useState(defaultValue);
|
|
12
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
13
|
+
// Ref to track if we're currently processing a storage event (to prevent loops)
|
|
14
|
+
// Note: StorageEvent only fires in OTHER tabs, not the current tab, so this ref
|
|
15
|
+
// is mainly used to prevent any edge cases
|
|
16
|
+
const isProcessingStorageEventRef = useRef(false);
|
|
17
|
+
// Initialize from localStorage
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
try {
|
|
20
|
+
const storageValue = localStorage.getItem(key);
|
|
21
|
+
if (storageValue !== null) {
|
|
22
|
+
const parsed = JSON.parse(storageValue);
|
|
23
|
+
setStoredValue(parsed);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.warn(`Error reading from localStorage key "${key}":`, error);
|
|
28
|
+
}
|
|
29
|
+
setIsInitialized(true);
|
|
30
|
+
}, [key]);
|
|
31
|
+
// Save to localStorage when value changes
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!isInitialized || isProcessingStorageEventRef.current)
|
|
34
|
+
return;
|
|
35
|
+
try {
|
|
36
|
+
const serialized = JSON.stringify(storedValue);
|
|
37
|
+
const currentValue = localStorage.getItem(key);
|
|
38
|
+
// Only write if the value actually changed (avoid unnecessary writes)
|
|
39
|
+
if (currentValue !== serialized) {
|
|
40
|
+
localStorage.setItem(key, serialized);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.warn(`Error writing to localStorage key "${key}":`, error);
|
|
45
|
+
}
|
|
46
|
+
}, [storedValue, isInitialized, key]);
|
|
47
|
+
// Listen for storage changes from other tabs (cross-tab synchronization)
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const handleStorageChange = (e) => {
|
|
50
|
+
// Only handle events for this key and ignore events from the current tab
|
|
51
|
+
if (e.key !== key || e.storageArea !== localStorage) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// StorageEvent only fires in OTHER tabs, not the tab that made the change
|
|
55
|
+
// So we can safely update the state here
|
|
56
|
+
try {
|
|
57
|
+
isProcessingStorageEventRef.current = true;
|
|
58
|
+
if (e.newValue !== null) {
|
|
59
|
+
const newValue = JSON.parse(e.newValue);
|
|
60
|
+
setStoredValue(newValue);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// If newValue is null, it means the item was removed
|
|
64
|
+
setStoredValue(defaultValue);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.warn(`Error parsing storage event for key "${key}":`, error);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
// Reset the flag after a brief delay to allow state updates to complete
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
isProcessingStorageEventRef.current = false;
|
|
74
|
+
}, 0);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
window.addEventListener('storage', handleStorageChange);
|
|
78
|
+
return () => {
|
|
79
|
+
window.removeEventListener('storage', handleStorageChange);
|
|
80
|
+
};
|
|
81
|
+
}, [key, defaultValue]);
|
|
82
|
+
return [storedValue, setStoredValue, isInitialized];
|
|
83
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export type ResizeCorner = 'nw' | 'ne' | 'sw' | 'se';
|
|
3
|
+
interface Position {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
interface Dimensions {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const useResizable: (currentPositionRef?: RefObject<Position>) => {
|
|
12
|
+
dimensions: Dimensions;
|
|
13
|
+
handleResizeStart: (corner: ResizeCorner) => (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
14
|
+
positionAdjustment: Position | null;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=useResizable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useResizable.d.ts","sourceRoot":"","sources":["../../../../src/legacy/core/lib/useResizable.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4C,SAAS,EAAE,MAAM,OAAO,CAAC;AAqB5E,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,UAAU,QAAQ;IAChB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AASD,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,GAAI,qBAAqB,SAAS,CAAC,QAAQ,CAAC;;gCA6ExD,YAAY,aAAa,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC;;CA0IvE,CAAC"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import useLocalStorage from './useLocalStorage';
|
|
3
|
+
// Resizable chat window defaults
|
|
4
|
+
const DEFAULT_WIDTH = 500;
|
|
5
|
+
const DEFAULT_HEIGHT = 600;
|
|
6
|
+
const ASPECT_RATIO = DEFAULT_WIDTH / DEFAULT_HEIGHT;
|
|
7
|
+
const MIN_WIDTH = 320;
|
|
8
|
+
const MIN_HEIGHT = 380;
|
|
9
|
+
const VIEWPORT_MARGIN = 40; // Keep space from viewport edges
|
|
10
|
+
const MOBILE_BREAKPOINT = 768;
|
|
11
|
+
const MOBILE_EDGE_MARGIN = 12;
|
|
12
|
+
const isMobileViewport = () => typeof window !== 'undefined' && window.innerWidth <= MOBILE_BREAKPOINT;
|
|
13
|
+
const getMobileFullscreenDimensions = () => ({
|
|
14
|
+
width: Math.max(MIN_WIDTH, window.innerWidth - MOBILE_EDGE_MARGIN * 2),
|
|
15
|
+
height: Math.max(MIN_HEIGHT, window.innerHeight - MOBILE_EDGE_MARGIN * 2),
|
|
16
|
+
});
|
|
17
|
+
export const useResizable = (currentPositionRef) => {
|
|
18
|
+
const resizeState = useRef(null);
|
|
19
|
+
const [positionAdjustment, setPositionAdjustment] = useState(null);
|
|
20
|
+
const [savedDimensions, setSavedDimensions, isDimensionsInitialized] = useLocalStorage('chat-window-dimensions', null);
|
|
21
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
22
|
+
const clampDimensions = useCallback((width, height) => {
|
|
23
|
+
if (typeof window === 'undefined') {
|
|
24
|
+
return { width, height };
|
|
25
|
+
}
|
|
26
|
+
if (isMobileViewport()) {
|
|
27
|
+
return getMobileFullscreenDimensions();
|
|
28
|
+
}
|
|
29
|
+
const maxWidth = Math.max(MIN_WIDTH, window.innerWidth - VIEWPORT_MARGIN);
|
|
30
|
+
const maxHeight = Math.max(MIN_HEIGHT, window.innerHeight - VIEWPORT_MARGIN);
|
|
31
|
+
const clampedWidth = Math.min(Math.max(width, MIN_WIDTH), maxWidth);
|
|
32
|
+
let clampedHeight = clampedWidth / ASPECT_RATIO;
|
|
33
|
+
if (clampedHeight > maxHeight) {
|
|
34
|
+
clampedHeight = maxHeight;
|
|
35
|
+
const adjustedWidth = clampedHeight * ASPECT_RATIO;
|
|
36
|
+
return { width: adjustedWidth, height: clampedHeight };
|
|
37
|
+
}
|
|
38
|
+
if (clampedHeight < MIN_HEIGHT) {
|
|
39
|
+
clampedHeight = MIN_HEIGHT;
|
|
40
|
+
const adjustedWidth = clampedHeight * ASPECT_RATIO;
|
|
41
|
+
return { width: adjustedWidth, height: clampedHeight };
|
|
42
|
+
}
|
|
43
|
+
return { width: clampedWidth, height: clampedHeight };
|
|
44
|
+
}, []);
|
|
45
|
+
const calculateDimensions = useCallback((savedDims) => {
|
|
46
|
+
if (typeof window === 'undefined') {
|
|
47
|
+
return { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT };
|
|
48
|
+
}
|
|
49
|
+
const baseDimensions = savedDims || { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT };
|
|
50
|
+
return clampDimensions(baseDimensions.width, baseDimensions.height);
|
|
51
|
+
}, [clampDimensions]);
|
|
52
|
+
const [dimensions, setDimensions] = useState(() => {
|
|
53
|
+
if (typeof window === 'undefined') {
|
|
54
|
+
return { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT };
|
|
55
|
+
}
|
|
56
|
+
if (isMobileViewport()) {
|
|
57
|
+
return getMobileFullscreenDimensions();
|
|
58
|
+
}
|
|
59
|
+
const maxWidth = Math.max(MIN_WIDTH, window.innerWidth - VIEWPORT_MARGIN);
|
|
60
|
+
const maxHeight = Math.max(MIN_HEIGHT, window.innerHeight - VIEWPORT_MARGIN);
|
|
61
|
+
let width = Math.min(Math.max(DEFAULT_WIDTH, MIN_WIDTH), maxWidth);
|
|
62
|
+
let height = width / ASPECT_RATIO;
|
|
63
|
+
if (height > maxHeight) {
|
|
64
|
+
height = maxHeight;
|
|
65
|
+
width = height * ASPECT_RATIO;
|
|
66
|
+
}
|
|
67
|
+
if (height < MIN_HEIGHT) {
|
|
68
|
+
height = MIN_HEIGHT;
|
|
69
|
+
width = height * ASPECT_RATIO;
|
|
70
|
+
}
|
|
71
|
+
return { width, height };
|
|
72
|
+
});
|
|
73
|
+
const hasLoadedFromStorage = useRef(false);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (isDimensionsInitialized && !hasLoadedFromStorage.current) {
|
|
76
|
+
const newDimensions = calculateDimensions(savedDimensions);
|
|
77
|
+
setDimensions(newDimensions);
|
|
78
|
+
hasLoadedFromStorage.current = true;
|
|
79
|
+
}
|
|
80
|
+
}, [isDimensionsInitialized, savedDimensions, calculateDimensions]);
|
|
81
|
+
const handleResizeStart = useCallback((corner) => (event) => {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
event.stopPropagation();
|
|
84
|
+
const currentPos = currentPositionRef?.current;
|
|
85
|
+
if (!currentPos) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const anchorTL = { x: currentPos.x, y: currentPos.y };
|
|
89
|
+
const anchorBR = {
|
|
90
|
+
x: currentPos.x + dimensions.width,
|
|
91
|
+
y: currentPos.y + dimensions.height
|
|
92
|
+
};
|
|
93
|
+
resizeState.current = { corner, anchorTL, anchorBR };
|
|
94
|
+
setIsResizing(true);
|
|
95
|
+
setPositionAdjustment(null);
|
|
96
|
+
document.body.style.userSelect = 'none';
|
|
97
|
+
}, [currentPositionRef, dimensions.width, dimensions.height]);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (!isResizing) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const handlePointerMove = (event) => {
|
|
103
|
+
const state = resizeState.current;
|
|
104
|
+
if (!state) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const { corner, anchorTL, anchorBR } = state;
|
|
108
|
+
const anchorBL = { x: anchorTL.x, y: anchorBR.y };
|
|
109
|
+
const anchorTR = { x: anchorBR.x, y: anchorTL.y };
|
|
110
|
+
let targetFromX;
|
|
111
|
+
let targetFromY;
|
|
112
|
+
switch (corner) {
|
|
113
|
+
case 'nw': {
|
|
114
|
+
const deltaX = anchorBR.x - event.clientX;
|
|
115
|
+
const deltaY = anchorBR.y - event.clientY;
|
|
116
|
+
targetFromX = deltaX;
|
|
117
|
+
targetFromY = deltaY * ASPECT_RATIO;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
case 'se': {
|
|
121
|
+
targetFromX = event.clientX - anchorTL.x;
|
|
122
|
+
targetFromY = (event.clientY - anchorTL.y) * ASPECT_RATIO;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case 'ne': {
|
|
126
|
+
targetFromX = event.clientX - anchorBL.x;
|
|
127
|
+
targetFromY = (anchorBL.y - event.clientY) * ASPECT_RATIO;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'sw': {
|
|
131
|
+
targetFromX = anchorTR.x - event.clientX;
|
|
132
|
+
targetFromY = (event.clientY - anchorTR.y) * ASPECT_RATIO;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
default:
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const nextWidth = (targetFromX + targetFromY) / 2;
|
|
139
|
+
const newDimensions = clampDimensions(nextWidth, nextWidth / ASPECT_RATIO);
|
|
140
|
+
let newPos;
|
|
141
|
+
switch (corner) {
|
|
142
|
+
case 'nw':
|
|
143
|
+
newPos = {
|
|
144
|
+
x: anchorBR.x - newDimensions.width,
|
|
145
|
+
y: anchorBR.y - newDimensions.height
|
|
146
|
+
};
|
|
147
|
+
break;
|
|
148
|
+
case 'se':
|
|
149
|
+
newPos = { x: anchorTL.x, y: anchorTL.y };
|
|
150
|
+
break;
|
|
151
|
+
case 'ne':
|
|
152
|
+
newPos = {
|
|
153
|
+
x: anchorBL.x,
|
|
154
|
+
y: anchorBL.y - newDimensions.height
|
|
155
|
+
};
|
|
156
|
+
break;
|
|
157
|
+
case 'sw':
|
|
158
|
+
newPos = {
|
|
159
|
+
x: anchorTR.x - newDimensions.width,
|
|
160
|
+
y: anchorTR.y
|
|
161
|
+
};
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
newPos = anchorTL;
|
|
165
|
+
}
|
|
166
|
+
setDimensions(newDimensions);
|
|
167
|
+
setPositionAdjustment(newPos);
|
|
168
|
+
};
|
|
169
|
+
const handlePointerUp = () => {
|
|
170
|
+
setIsResizing(false);
|
|
171
|
+
resizeState.current = null;
|
|
172
|
+
setPositionAdjustment(null);
|
|
173
|
+
document.body.style.userSelect = '';
|
|
174
|
+
};
|
|
175
|
+
document.addEventListener('pointermove', handlePointerMove);
|
|
176
|
+
document.addEventListener('pointerup', handlePointerUp);
|
|
177
|
+
return () => {
|
|
178
|
+
document.removeEventListener('pointermove', handlePointerMove);
|
|
179
|
+
document.removeEventListener('pointerup', handlePointerUp);
|
|
180
|
+
document.body.style.userSelect = '';
|
|
181
|
+
};
|
|
182
|
+
}, [clampDimensions, isResizing]);
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
if (!isResizing && isDimensionsInitialized && hasLoadedFromStorage.current) {
|
|
185
|
+
setSavedDimensions(dimensions);
|
|
186
|
+
}
|
|
187
|
+
}, [dimensions, isResizing, isDimensionsInitialized, setSavedDimensions]);
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const handleResize = () => {
|
|
190
|
+
setDimensions((current) => {
|
|
191
|
+
const clamped = clampDimensions(current.width, current.height);
|
|
192
|
+
return clamped;
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
window.addEventListener('resize', handleResize);
|
|
196
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
197
|
+
}, [clampDimensions]);
|
|
198
|
+
return {
|
|
199
|
+
dimensions,
|
|
200
|
+
handleResizeStart,
|
|
201
|
+
positionAdjustment
|
|
202
|
+
};
|
|
203
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Custom hook for managing sessionStorage with React state
|
|
4
|
+
* Similar to useLocalStorage but uses sessionStorage instead
|
|
5
|
+
*
|
|
6
|
+
* @param key - The key to store the value under in sessionStorage
|
|
7
|
+
* @param defaultValue - The default value to use if no value is stored
|
|
8
|
+
* @returns [storedValue, setStoredValue, isInitialized]
|
|
9
|
+
*/
|
|
10
|
+
export default function useSessionStorage<T>(key: string, defaultValue: T): [T, Dispatch<SetStateAction<T>>, boolean];
|
|
11
|
+
//# sourceMappingURL=useSessionStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSessionStorage.d.ts","sourceRoot":"","sources":["../../../../src/legacy/core/lib/useSessionStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAuB,MAAM,OAAO,CAAC;AAEtE;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,CAAC,EACzC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,GACd,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CA6B3C"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Custom hook for managing sessionStorage with React state
|
|
4
|
+
* Similar to useLocalStorage but uses sessionStorage instead
|
|
5
|
+
*
|
|
6
|
+
* @param key - The key to store the value under in sessionStorage
|
|
7
|
+
* @param defaultValue - The default value to use if no value is stored
|
|
8
|
+
* @returns [storedValue, setStoredValue, isInitialized]
|
|
9
|
+
*/
|
|
10
|
+
export default function useSessionStorage(key, defaultValue) {
|
|
11
|
+
const [storedValue, setStoredValue] = useState(defaultValue);
|
|
12
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
try {
|
|
15
|
+
const storageValue = sessionStorage.getItem(key);
|
|
16
|
+
if (storageValue !== null) {
|
|
17
|
+
setStoredValue(JSON.parse(storageValue));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.warn(`Error reading from sessionStorage key "${key}":`, error);
|
|
22
|
+
// If there's an error, keep the default value
|
|
23
|
+
}
|
|
24
|
+
setIsInitialized(true);
|
|
25
|
+
}, [key]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (isInitialized) {
|
|
28
|
+
try {
|
|
29
|
+
sessionStorage.setItem(key, JSON.stringify(storedValue));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.warn(`Error writing to sessionStorage key "${key}":`, error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}, [storedValue, isInitialized, key]);
|
|
36
|
+
return [storedValue, setStoredValue, isInitialized];
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SpeechRecognizer } from 'microsoft-cognitiveservices-speech-sdk';
|
|
2
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Start speech recognition
|
|
5
|
+
* - partialTranscript: temporary partial transcription (recognizing)
|
|
6
|
+
* - setSpeechToTextArray: finalized sentence array (recognized)
|
|
7
|
+
*/
|
|
8
|
+
export declare const startSpeechToTextFromMic: (setSpeechToTextArray: Dispatch<SetStateAction<string[]>>, // finalized sentences
|
|
9
|
+
config: {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
apiSecret: string;
|
|
12
|
+
}, historyRef: React.MutableRefObject<string[]>, indexRef: React.MutableRefObject<number>, setPartialTranscript?: Dispatch<SetStateAction<string>>) => Promise<SpeechRecognizer | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* Stop speech recognition
|
|
15
|
+
*/
|
|
16
|
+
export declare const stopRecognition: (recognizer: SpeechRecognizer | undefined) => void;
|
|
17
|
+
export declare function getTokenOrRefresh(apiKey: string, apiSecret: string): Promise<{
|
|
18
|
+
authToken: null;
|
|
19
|
+
error: string;
|
|
20
|
+
region?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
authToken: any;
|
|
23
|
+
region: any;
|
|
24
|
+
error?: undefined;
|
|
25
|
+
}>;
|
|
26
|
+
//# sourceMappingURL=helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../../../src/legacy/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,CAyEtC,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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { AudioConfig, CancellationReason, PropertyId, ResultReason, SpeechConfig, SpeechRecognizer, } from 'microsoft-cognitiveservices-speech-sdk';
|
|
2
|
+
import Cookie from 'universal-cookie';
|
|
3
|
+
import { createNxtlinqApi } from '../../../api/nxtlinq-api';
|
|
4
|
+
/**
|
|
5
|
+
* Start speech recognition
|
|
6
|
+
* - partialTranscript: temporary partial transcription (recognizing)
|
|
7
|
+
* - setSpeechToTextArray: finalized sentence array (recognized)
|
|
8
|
+
*/
|
|
9
|
+
export const startSpeechToTextFromMic = async (setSpeechToTextArray, // finalized sentences
|
|
10
|
+
config, historyRef, indexRef, setPartialTranscript // temporary partial transcription state updater
|
|
11
|
+
) => {
|
|
12
|
+
const tokenRes = await getTokenOrRefresh(config.apiKey, config.apiSecret);
|
|
13
|
+
if (!tokenRes.authToken || !tokenRes.region) {
|
|
14
|
+
console.error('Speech token retrieval failed:', tokenRes.error);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const speechConfig = SpeechConfig.fromAuthorizationToken(tokenRes.authToken, tokenRes.region);
|
|
18
|
+
speechConfig.speechRecognitionLanguage = 'en-US';
|
|
19
|
+
// silence detection and segmentation configuration
|
|
20
|
+
speechConfig.setProperty(PropertyId.SpeechServiceConnection_InitialSilenceTimeoutMs, '10000');
|
|
21
|
+
speechConfig.setProperty(PropertyId.SpeechServiceConnection_EndSilenceTimeoutMs, '86400000');
|
|
22
|
+
speechConfig.setProperty(PropertyId.Speech_SegmentationSilenceTimeoutMs, '3000');
|
|
23
|
+
const audioConfig = AudioConfig.fromDefaultMicrophoneInput();
|
|
24
|
+
const recognizer = new SpeechRecognizer(speechConfig, audioConfig);
|
|
25
|
+
// temporary partial transcription
|
|
26
|
+
recognizer.recognizing = (_s, e) => {
|
|
27
|
+
if (setPartialTranscript) {
|
|
28
|
+
setPartialTranscript(e.result.text);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
// finalized sentences
|
|
32
|
+
recognizer.recognized = (_s, e) => {
|
|
33
|
+
if (e.result.reason === ResultReason.RecognizedSpeech) {
|
|
34
|
+
const text = e.result.text.trim();
|
|
35
|
+
// Filter out very short text (likely background noise or false positives)
|
|
36
|
+
// Minimum 3 characters to reduce sensitivity to background sounds
|
|
37
|
+
if (text.length < 3) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Check confidence if available (some SDK versions provide this)
|
|
41
|
+
// Lower confidence results are more likely to be background noise
|
|
42
|
+
// Increased threshold to 0.4 to better filter out low-volume background noise
|
|
43
|
+
const confidence = e.result.confidence;
|
|
44
|
+
if (confidence !== undefined && confidence < 0.4) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
historyRef.current[indexRef.current] = text;
|
|
48
|
+
indexRef.current += 1;
|
|
49
|
+
// return only the latest finalized sentence to the UI
|
|
50
|
+
setSpeechToTextArray([text]);
|
|
51
|
+
if (setPartialTranscript)
|
|
52
|
+
setPartialTranscript('');
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
recognizer.canceled = (_s, e) => {
|
|
56
|
+
console.warn('Speech recognition canceled:', e.reason);
|
|
57
|
+
if (e.reason === CancellationReason.Error) {
|
|
58
|
+
console.error(`Error code: ${e.errorCode}`);
|
|
59
|
+
console.error(`Error details: ${e.errorDetails}`);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
await new Promise((resolve, reject) => {
|
|
63
|
+
recognizer.startContinuousRecognitionAsync(() => resolve(), error => {
|
|
64
|
+
console.error('Failed to start continuous speech recognition:', error);
|
|
65
|
+
reject(error);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
return recognizer;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Stop speech recognition
|
|
72
|
+
*/
|
|
73
|
+
export const stopRecognition = (recognizer) => {
|
|
74
|
+
if (recognizer) {
|
|
75
|
+
recognizer.stopContinuousRecognitionAsync();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
export async function getTokenOrRefresh(apiKey, apiSecret) {
|
|
79
|
+
const nxtlinqApi = createNxtlinqApi(apiKey, apiSecret);
|
|
80
|
+
const cookie = new Cookie();
|
|
81
|
+
const speechToken = cookie.get('speech-token');
|
|
82
|
+
if (speechToken === undefined) {
|
|
83
|
+
try {
|
|
84
|
+
const res = await nxtlinqApi.cognitive.getCognitiveToken();
|
|
85
|
+
if ('error' in res) {
|
|
86
|
+
throw new Error(res.error);
|
|
87
|
+
}
|
|
88
|
+
const token = res.token;
|
|
89
|
+
const region = res.region;
|
|
90
|
+
cookie.set('speech-token', region + ':' + token, { maxAge: 540, path: '/' });
|
|
91
|
+
return { authToken: token, region: region };
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
const err = e;
|
|
95
|
+
return { authToken: null, error: err.response.data };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const idx = speechToken.indexOf(':');
|
|
100
|
+
return { authToken: speechToken.slice(idx + 1), region: speechToken.slice(0, idx) };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
apiSecret: string;
|
|
4
|
+
autoClearTranscript?: boolean;
|
|
5
|
+
}
|
|
6
|
+
type UseSpeechToTextFromMicResult = {
|
|
7
|
+
start: () => Promise<void>;
|
|
8
|
+
stop: () => void;
|
|
9
|
+
clear: () => void;
|
|
10
|
+
isMicEnabled: boolean;
|
|
11
|
+
transcript: string;
|
|
12
|
+
partialTranscript: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function useSpeechToTextFromMic({ apiKey, apiSecret, autoClearTranscript, }: Props): UseSpeechToTextFromMicResult;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/legacy/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,CAsGtC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { startSpeechToTextFromMic, stopRecognition } from './helper';
|
|
3
|
+
export function useSpeechToTextFromMic({ apiKey, apiSecret, autoClearTranscript = true, }) {
|
|
4
|
+
const [isMicEnabled, setIsMicEnabled] = useState(false);
|
|
5
|
+
const [transcriptArray, setTranscriptArray] = useState([]);
|
|
6
|
+
const [partialTranscript, setPartialTranscript] = useState('');
|
|
7
|
+
const [recognizer, setRecognizer] = useState();
|
|
8
|
+
const wakelock = useRef();
|
|
9
|
+
const historyRef = useRef([]);
|
|
10
|
+
const indexRef = useRef(0);
|
|
11
|
+
const lockWakeState = useCallback(async () => {
|
|
12
|
+
if (typeof navigator === 'undefined' || !('wakeLock' in navigator))
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
wakelock.current = await navigator.wakeLock.request('screen');
|
|
16
|
+
wakelock.current.addEventListener('release', () => {
|
|
17
|
+
// Wake lock was released by the system
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
console.error('Wake lock error:', err);
|
|
22
|
+
}
|
|
23
|
+
}, []);
|
|
24
|
+
const clear = useCallback(() => {
|
|
25
|
+
historyRef.current = [];
|
|
26
|
+
indexRef.current = 0;
|
|
27
|
+
setTranscriptArray([]);
|
|
28
|
+
setPartialTranscript('');
|
|
29
|
+
}, [setPartialTranscript, setTranscriptArray]);
|
|
30
|
+
const start = useCallback(async () => {
|
|
31
|
+
clear();
|
|
32
|
+
await lockWakeState();
|
|
33
|
+
try {
|
|
34
|
+
const recognizerInstance = await startSpeechToTextFromMic(setTranscriptArray, { apiKey, apiSecret }, historyRef, indexRef, setPartialTranscript);
|
|
35
|
+
setRecognizer(recognizerInstance);
|
|
36
|
+
setIsMicEnabled(true);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
wakelock.current?.release();
|
|
40
|
+
setIsMicEnabled(false);
|
|
41
|
+
console.error('Failed to start speech recognition:', error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}, [apiKey, apiSecret, clear, lockWakeState, setPartialTranscript, setTranscriptArray]);
|
|
45
|
+
const stop = useCallback(() => {
|
|
46
|
+
wakelock.current?.release();
|
|
47
|
+
stopRecognition(recognizer);
|
|
48
|
+
setIsMicEnabled(false);
|
|
49
|
+
setRecognizer(undefined);
|
|
50
|
+
}, [recognizer, setRecognizer]);
|
|
51
|
+
// Keep UI state in sync if recognizer stops or gets canceled by the SDK/browser
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!recognizer)
|
|
54
|
+
return;
|
|
55
|
+
const prevCanceled = recognizer.canceled;
|
|
56
|
+
const prevSessionStopped = recognizer.sessionStopped;
|
|
57
|
+
recognizer.canceled = (s, e) => {
|
|
58
|
+
setIsMicEnabled(false);
|
|
59
|
+
if (prevCanceled)
|
|
60
|
+
prevCanceled(s, e);
|
|
61
|
+
};
|
|
62
|
+
recognizer.sessionStopped = (s, e) => {
|
|
63
|
+
setIsMicEnabled(false);
|
|
64
|
+
if (prevSessionStopped)
|
|
65
|
+
prevSessionStopped(s, e);
|
|
66
|
+
};
|
|
67
|
+
return () => {
|
|
68
|
+
// No explicit detach API; restoring previous handlers to avoid leaks
|
|
69
|
+
if (recognizer) {
|
|
70
|
+
recognizer.canceled = prevCanceled;
|
|
71
|
+
recognizer.sessionStopped = prevSessionStopped;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}, [recognizer]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (autoClearTranscript && transcriptArray.length > 0) {
|
|
77
|
+
const timer = setTimeout(() => {
|
|
78
|
+
setTranscriptArray([]);
|
|
79
|
+
}, 100);
|
|
80
|
+
return () => clearTimeout(timer);
|
|
81
|
+
}
|
|
82
|
+
}, [transcriptArray, autoClearTranscript]);
|
|
83
|
+
const transcript = transcriptArray.join(' ');
|
|
84
|
+
return {
|
|
85
|
+
start,
|
|
86
|
+
stop,
|
|
87
|
+
clear,
|
|
88
|
+
isMicEnabled,
|
|
89
|
+
transcript,
|
|
90
|
+
partialTranscript,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { AITApi, Message, VoiceTransport } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
3
|
+
import { type VoiceStatus } from '../../api/voice';
|
|
4
|
+
export type UseVoiceModeOptions = {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
apiSecret: string;
|
|
7
|
+
pseudoId: string;
|
|
8
|
+
externalId?: string;
|
|
9
|
+
aitId?: string;
|
|
10
|
+
walletAddress?: string;
|
|
11
|
+
aitToken?: string;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
nxtlinqApi: AITApi;
|
|
14
|
+
getMessages: () => Message[];
|
|
15
|
+
setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
16
|
+
onError?: (error: Error) => void;
|
|
17
|
+
stopRecording: () => void;
|
|
18
|
+
stopTextToSpeech: () => void;
|
|
19
|
+
voiceTransport?: VoiceTransport;
|
|
20
|
+
};
|
|
21
|
+
export declare function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, walletAddress, aitToken, metadata, nxtlinqApi, getMessages, setMessages, onError, stopRecording, stopTextToSpeech, voiceTransport, }: UseVoiceModeOptions): {
|
|
22
|
+
isVoiceMode: boolean;
|
|
23
|
+
voiceStatus: VoiceStatus;
|
|
24
|
+
isVoiceConnecting: boolean;
|
|
25
|
+
isMicMuted: boolean;
|
|
26
|
+
remoteAudioRef: React.MutableRefObject<HTMLAudioElement | null>;
|
|
27
|
+
enterVoiceMode: () => Promise<void>;
|
|
28
|
+
exitVoiceMode: () => Promise<void>;
|
|
29
|
+
toggleMicMute: () => void;
|
|
30
|
+
interruptVoice: () => void;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=useVoiceMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVoiceMode.d.ts","sourceRoot":"","sources":["../../../../src/legacy/core/lib/useVoiceMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AACpG,OAAO,EAKL,KAAK,WAAW,EAEjB,MAAM,iBAAiB,CAAC;AAgBzB,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,OAAO,EAAE,CAAC;IAC7B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,SAAS,EACT,QAAQ,EACR,UAAU,EACV,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,cAA8B,GAC/B,EAAE,mBAAmB;;;;;;;;;;EA0VrB"}
|