mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
👋 feat: remove Edge TTS (#6885)
* feat: remove Edge TTS * remove the remaining edge code * chore: cleanup * chore: cleanup package-lock
This commit is contained in:
parent
c49f883e1a
commit
5d56f48879
13 changed files with 63 additions and 547 deletions
|
@ -73,7 +73,6 @@
|
|||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.394.0",
|
||||
"match-sorter": "^6.3.4",
|
||||
"msedge-tts": "^2.0.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"rc-input-number": "^7.4.2",
|
||||
"react": "^18.2.0",
|
||||
|
|
|
@ -29,7 +29,6 @@ export enum STTEndpoints {
|
|||
|
||||
export enum TTSEndpoints {
|
||||
browser = 'browser',
|
||||
edge = 'edge',
|
||||
external = 'external',
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import type { TMessageAudio } from '~/common';
|
||||
import { useLocalize, useTTSBrowser, useTTSEdge, useTTSExternal } from '~/hooks';
|
||||
import { VolumeIcon, VolumeMuteIcon, Spinner } from '~/components/svg';
|
||||
import { useToastContext } from '~/Providers/ToastContext';
|
||||
import { useLocalize, useTTSBrowser, useTTSExternal } from '~/hooks';
|
||||
import { VolumeIcon, VolumeMuteIcon, Spinner } from '~/components';
|
||||
import { logger } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
|
@ -85,97 +84,6 @@ export function BrowserTTS({ isLast, index, messageId, content, className }: TMe
|
|||
);
|
||||
}
|
||||
|
||||
export function EdgeTTS({ isLast, index, messageId, content, className }: TMessageAudio) {
|
||||
const localize = useLocalize();
|
||||
const playbackRate = useRecoilValue(store.playbackRate);
|
||||
const isBrowserSupported = useMemo(
|
||||
() => typeof MediaSource !== 'undefined' && MediaSource.isTypeSupported('audio/mpeg'),
|
||||
[],
|
||||
);
|
||||
|
||||
const { showToast } = useToastContext();
|
||||
const { toggleSpeech, isSpeaking, isLoading, audioRef } = useTTSEdge({
|
||||
isLast,
|
||||
index,
|
||||
messageId,
|
||||
content,
|
||||
});
|
||||
|
||||
const renderIcon = (size: string) => {
|
||||
if (isLoading === true) {
|
||||
return <Spinner size={size} />;
|
||||
}
|
||||
|
||||
if (isSpeaking === true) {
|
||||
return <VolumeMuteIcon size={size} />;
|
||||
}
|
||||
|
||||
return <VolumeIcon size={size} />;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const messageAudio = document.getElementById(`audio-${messageId}`) as HTMLAudioElement | null;
|
||||
if (!messageAudio) {
|
||||
return;
|
||||
}
|
||||
if (playbackRate != null && playbackRate > 0 && messageAudio.playbackRate !== playbackRate) {
|
||||
messageAudio.playbackRate = playbackRate;
|
||||
}
|
||||
}, [audioRef, isSpeaking, playbackRate, messageId]);
|
||||
|
||||
logger.log(
|
||||
'MessageAudio: audioRef.current?.src, audioRef.current',
|
||||
audioRef.current?.src,
|
||||
audioRef.current,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className={className}
|
||||
onClickCapture={() => {
|
||||
if (!isBrowserSupported) {
|
||||
showToast({
|
||||
message: localize('com_nav_tts_unsupported_error'),
|
||||
status: 'error',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (audioRef.current) {
|
||||
audioRef.current.muted = false;
|
||||
}
|
||||
toggleSpeech();
|
||||
}}
|
||||
type="button"
|
||||
title={isSpeaking === true ? localize('com_ui_stop') : localize('com_ui_read_aloud')}
|
||||
>
|
||||
{renderIcon('19')}
|
||||
</button>
|
||||
{isBrowserSupported ? (
|
||||
<audio
|
||||
ref={audioRef}
|
||||
controls
|
||||
preload="none"
|
||||
controlsList="nodownload nofullscreen noremoteplayback"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
display: 'none',
|
||||
height: '0px',
|
||||
width: '0px',
|
||||
}}
|
||||
src={audioRef.current?.src}
|
||||
onError={(error) => {
|
||||
logger.error('Error fetching audio:', error);
|
||||
}}
|
||||
id={`audio-${messageId}`}
|
||||
autoPlay
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ExternalTTS({ isLast, index, messageId, content, className }: TMessageAudio) {
|
||||
const localize = useLocalize();
|
||||
const playbackRate = useRecoilValue(store.playbackRate);
|
||||
|
|
|
@ -1,39 +1,11 @@
|
|||
import React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import type { Option } from '~/common';
|
||||
import { useLocalize, useTTSBrowser, useTTSEdge, useTTSExternal } from '~/hooks';
|
||||
import { useLocalize, useTTSBrowser, useTTSExternal } from '~/hooks';
|
||||
import { Dropdown } from '~/components/ui';
|
||||
import { logger } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
export function EdgeVoiceDropdown() {
|
||||
const localize = useLocalize();
|
||||
const { voices = [] } = useTTSEdge();
|
||||
const [voice, setVoice] = useRecoilState(store.voice);
|
||||
|
||||
const handleVoiceChange = (newValue?: string | Option) => {
|
||||
logger.log('Edge Voice changed:', newValue);
|
||||
const newVoice = typeof newValue === 'string' ? newValue : newValue?.value;
|
||||
if (newVoice != null) {
|
||||
return setVoice(newVoice.toString());
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div>{localize('com_nav_voice_select')}</div>
|
||||
<Dropdown
|
||||
key={`edge-voice-dropdown-${voices.length}`}
|
||||
value={voice ?? ''}
|
||||
options={voices}
|
||||
onChange={handleVoiceChange}
|
||||
sizeClasses="min-w-[200px] !max-w-[400px] [--anchor-max-width:400px]"
|
||||
testId="EdgeVoiceDropdown"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function BrowserVoiceDropdown() {
|
||||
const localize = useLocalize();
|
||||
const { voices = [] } = useTTSBrowser();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { memo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import type { TMessageAudio } from '~/common';
|
||||
import { BrowserTTS, EdgeTTS, ExternalTTS } from '~/components/Audio/TTS';
|
||||
import { BrowserTTS, ExternalTTS } from '~/components/Audio/TTS';
|
||||
import { TTSEndpoints } from '~/common';
|
||||
import store from '~/store';
|
||||
|
||||
|
@ -9,7 +9,6 @@ function MessageAudio(props: TMessageAudio) {
|
|||
const engineTTS = useRecoilValue<string>(store.engineTTS);
|
||||
|
||||
const TTSComponents = {
|
||||
[TTSEndpoints.edge]: EdgeTTS,
|
||||
[TTSEndpoints.browser]: BrowserTTS,
|
||||
[TTSEndpoints.external]: ExternalTTS,
|
||||
};
|
||||
|
|
|
@ -15,13 +15,9 @@ const EngineTTSDropdown: React.FC<EngineTTSDropdownProps> = ({ external }) => {
|
|||
const endpointOptions = external
|
||||
? [
|
||||
{ value: 'browser', label: localize('com_nav_browser') },
|
||||
{ value: 'edge', label: localize('com_nav_edge') },
|
||||
{ value: 'external', label: localize('com_nav_external') },
|
||||
]
|
||||
: [
|
||||
{ value: 'browser', label: localize('com_nav_browser') },
|
||||
{ value: 'edge', label: localize('com_nav_edge') },
|
||||
];
|
||||
: [{ value: 'browser', label: localize('com_nav_browser') }];
|
||||
|
||||
const handleSelect = (value: string) => {
|
||||
setEngineTTS(value);
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import {
|
||||
EdgeVoiceDropdown,
|
||||
BrowserVoiceDropdown,
|
||||
ExternalVoiceDropdown,
|
||||
} from '~/components/Audio/Voices';
|
||||
import { BrowserVoiceDropdown, ExternalVoiceDropdown } from '~/components/Audio/Voices';
|
||||
import store from '~/store';
|
||||
import { TTSEndpoints } from '~/common';
|
||||
|
||||
const voiceDropdownComponentsMap = {
|
||||
[TTSEndpoints.edge]: EdgeVoiceDropdown,
|
||||
[TTSEndpoints.browser]: BrowserVoiceDropdown,
|
||||
[TTSEndpoints.external]: ExternalVoiceDropdown,
|
||||
};
|
||||
|
|
|
@ -3,4 +3,3 @@ export { default as useCustomAudioRef } from './useCustomAudioRef';
|
|||
export { default as usePauseGlobalAudio } from './usePauseGlobalAudio';
|
||||
export { default as useTTSExternal } from './useTTSExternal';
|
||||
export { default as useTTSBrowser } from './useTTSBrowser';
|
||||
export { default as useTTSEdge } from './useTTSEdge';
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
// client/src/hooks/Audio/useTTSEdge.ts
|
||||
import { useRef, useEffect, useState } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { parseTextParts } from 'librechat-data-provider';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import usePauseGlobalAudio from '~/hooks/Audio/usePauseGlobalAudio';
|
||||
import useTextToSpeechEdge from '~/hooks/Input/useTextToSpeechEdge';
|
||||
import useAudioRef from '~/hooks/Audio/useAudioRef';
|
||||
import { logger } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
type TUseTextToSpeech = {
|
||||
messageId?: string;
|
||||
content?: TMessageContentParts[] | string;
|
||||
isLast?: boolean;
|
||||
index?: number;
|
||||
};
|
||||
|
||||
const useTTSEdge = (props?: TUseTextToSpeech) => {
|
||||
const { content, isLast = false, index = 0 } = props ?? {};
|
||||
|
||||
const isMouseDownRef = useRef(false);
|
||||
const timerRef = useRef<number | undefined>(undefined);
|
||||
const [isSpeakingState, setIsSpeaking] = useState(false);
|
||||
const { audioRef } = useAudioRef({ setIsPlaying: setIsSpeaking });
|
||||
|
||||
const { pauseGlobalAudio } = usePauseGlobalAudio(index);
|
||||
const [voice, setVoice] = useRecoilState(store.voice);
|
||||
const globalIsPlaying = useRecoilValue(store.globalAudioPlayingFamily(index));
|
||||
|
||||
const isSpeaking = isSpeakingState || (isLast && globalIsPlaying);
|
||||
|
||||
const {
|
||||
generateSpeechEdge: generateSpeech,
|
||||
cancelSpeechEdge: cancelSpeech,
|
||||
voices,
|
||||
} = useTextToSpeechEdge({ setIsSpeaking });
|
||||
|
||||
useEffect(() => {
|
||||
const firstVoice = voices[0];
|
||||
if (voices.length && typeof firstVoice === 'object') {
|
||||
const lastSelectedVoice = voices.find((v) =>
|
||||
typeof v === 'object' ? v.value === voice : v === voice,
|
||||
);
|
||||
if (lastSelectedVoice != null) {
|
||||
const currentVoice =
|
||||
typeof lastSelectedVoice === 'object' ? lastSelectedVoice.value : lastSelectedVoice;
|
||||
logger.log('useTextToSpeech.ts - Effect:', { voices, voice: currentVoice });
|
||||
setVoice(currentVoice);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log('useTextToSpeech.ts - Effect:', { voices, voice: firstVoice.value });
|
||||
setVoice(firstVoice.value);
|
||||
}
|
||||
}, [setVoice, voice, voices]);
|
||||
|
||||
const handleMouseDown = () => {
|
||||
isMouseDownRef.current = true;
|
||||
timerRef.current = window.setTimeout(() => {
|
||||
if (isMouseDownRef.current) {
|
||||
const messageContent = content ?? '';
|
||||
const parsedMessage =
|
||||
typeof messageContent === 'string' ? messageContent : parseTextParts(messageContent);
|
||||
generateSpeech(parsedMessage);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
isMouseDownRef.current = false;
|
||||
if (timerRef.current != null) {
|
||||
window.clearTimeout(timerRef.current);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSpeech = () => {
|
||||
if (isSpeaking === true) {
|
||||
cancelSpeech();
|
||||
pauseGlobalAudio();
|
||||
} else {
|
||||
const messageContent = content ?? '';
|
||||
const parsedMessage =
|
||||
typeof messageContent === 'string' ? messageContent : parseTextParts(messageContent);
|
||||
generateSpeech(parsedMessage);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
handleMouseDown,
|
||||
handleMouseUp,
|
||||
toggleSpeech,
|
||||
isSpeaking,
|
||||
isLoading: false,
|
||||
audioRef,
|
||||
voices,
|
||||
};
|
||||
};
|
||||
|
||||
export default useTTSEdge;
|
|
@ -6,7 +6,6 @@ import type { Option } from '~/common';
|
|||
import useTextToSpeechExternal from '~/hooks/Input/useTextToSpeechExternal';
|
||||
import useTextToSpeechBrowser from '~/hooks/Input/useTextToSpeechBrowser';
|
||||
import useGetAudioSettings from '~/hooks/Input/useGetAudioSettings';
|
||||
import useTextToSpeechEdge from '~/hooks/Input/useTextToSpeechEdge';
|
||||
import useAudioRef from '~/hooks/Audio/useAudioRef';
|
||||
import { usePauseGlobalAudio } from '../Audio';
|
||||
import { logger } from '~/utils';
|
||||
|
@ -40,12 +39,6 @@ const useTextToSpeech = (props?: TUseTextToSpeech) => {
|
|||
voices: voicesLocal,
|
||||
} = useTextToSpeechBrowser({ setIsSpeaking });
|
||||
|
||||
const {
|
||||
generateSpeechEdge,
|
||||
cancelSpeechEdge,
|
||||
voices: voicesEdge,
|
||||
} = useTextToSpeechEdge({ setIsSpeaking });
|
||||
|
||||
const {
|
||||
generateSpeechExternal,
|
||||
cancelSpeech: cancelSpeechExternal,
|
||||
|
@ -61,26 +54,23 @@ const useTextToSpeech = (props?: TUseTextToSpeech) => {
|
|||
|
||||
const generateSpeech = useMemo(() => {
|
||||
const map = {
|
||||
edge: generateSpeechEdge,
|
||||
browser: generateSpeechLocal,
|
||||
external: generateSpeechExternal,
|
||||
};
|
||||
|
||||
return map[textToSpeechEndpoint];
|
||||
}, [generateSpeechEdge, generateSpeechExternal, generateSpeechLocal, textToSpeechEndpoint]);
|
||||
}, [generateSpeechExternal, generateSpeechLocal, textToSpeechEndpoint]);
|
||||
|
||||
const cancelSpeech = useMemo(() => {
|
||||
const map = {
|
||||
edge: cancelSpeechEdge,
|
||||
browser: cancelSpeechLocal,
|
||||
external: cancelSpeechExternal,
|
||||
};
|
||||
return map[textToSpeechEndpoint];
|
||||
}, [cancelSpeechEdge, cancelSpeechExternal, cancelSpeechLocal, textToSpeechEndpoint]);
|
||||
}, [cancelSpeechExternal, cancelSpeechLocal, textToSpeechEndpoint]);
|
||||
|
||||
const isLoading = useMemo(() => {
|
||||
const map = {
|
||||
edge: false,
|
||||
browser: false,
|
||||
external: isLoadingExternal,
|
||||
};
|
||||
|
@ -89,13 +79,12 @@ const useTextToSpeech = (props?: TUseTextToSpeech) => {
|
|||
|
||||
const voices: Option[] | string[] = useMemo(() => {
|
||||
const voiceMap = {
|
||||
edge: voicesEdge,
|
||||
browser: voicesLocal,
|
||||
external: voicesExternal,
|
||||
};
|
||||
|
||||
return voiceMap[textToSpeechEndpoint];
|
||||
}, [textToSpeechEndpoint, voicesEdge, voicesExternal, voicesLocal]);
|
||||
}, [textToSpeechEndpoint, voicesExternal, voicesLocal]);
|
||||
|
||||
useEffect(() => {
|
||||
const firstVoice = voices[0];
|
||||
|
|
|
@ -1,249 +0,0 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import { MsEdgeTTS, OUTPUT_FORMAT } from 'msedge-tts';
|
||||
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
||||
import type { VoiceOption } from '~/common';
|
||||
import { useToastContext } from '~/Providers/ToastContext';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import store from '~/store';
|
||||
|
||||
interface UseTextToSpeechEdgeReturn {
|
||||
generateSpeechEdge: (text: string) => void;
|
||||
cancelSpeechEdge: () => void;
|
||||
voices: VoiceOption[];
|
||||
}
|
||||
|
||||
function useTextToSpeechEdge({
|
||||
setIsSpeaking,
|
||||
}: {
|
||||
setIsSpeaking: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}): UseTextToSpeechEdgeReturn {
|
||||
const localize = useLocalize();
|
||||
const [voices, setVoices] = useState<VoiceOption[]>([]);
|
||||
const voiceName = useRecoilValue(store.voice);
|
||||
const ttsRef = useRef<MsEdgeTTS | null>(null);
|
||||
const audioElementRef = useRef<HTMLAudioElement | null>(null);
|
||||
const mediaSourceRef = useRef<MediaSource | null>(null);
|
||||
const sourceBufferRef = useRef<SourceBuffer | null>(null);
|
||||
const pendingBuffers = useRef<Uint8Array[]>([]);
|
||||
const { showToast } = useToastContext();
|
||||
const initAttempts = useRef(0);
|
||||
|
||||
const isBrowserSupported = useMemo(
|
||||
() => typeof MediaSource !== 'undefined' && MediaSource.isTypeSupported('audio/mpeg'),
|
||||
[],
|
||||
);
|
||||
|
||||
const fetchVoices = useCallback(() => {
|
||||
if (!ttsRef.current) {
|
||||
ttsRef.current = new MsEdgeTTS();
|
||||
}
|
||||
ttsRef.current
|
||||
.getVoices()
|
||||
.then((voicesList) => {
|
||||
setVoices(
|
||||
voicesList.map((v) => ({
|
||||
value: v.ShortName,
|
||||
label: v.FriendlyName,
|
||||
})),
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching voices:', error);
|
||||
showToast({
|
||||
message: localize('com_nav_voices_fetch_error'),
|
||||
status: 'error',
|
||||
});
|
||||
});
|
||||
}, [showToast, localize]);
|
||||
|
||||
const initializeTTS = useCallback(() => {
|
||||
if (!ttsRef.current) {
|
||||
ttsRef.current = new MsEdgeTTS({
|
||||
enableLogger: true,
|
||||
});
|
||||
}
|
||||
const availableVoice: VoiceOption | undefined = voices.find((v) => v.value === voiceName);
|
||||
|
||||
if (availableVoice) {
|
||||
if (initAttempts.current > 3) {
|
||||
return;
|
||||
}
|
||||
ttsRef.current
|
||||
.setMetadata(availableVoice.value, OUTPUT_FORMAT.AUDIO_24KHZ_48KBITRATE_MONO_MP3, {})
|
||||
.catch((error) => {
|
||||
initAttempts.current += 1;
|
||||
console.error('Error initializing TTS:', error);
|
||||
showToast({
|
||||
message: localize('com_nav_tts_init_error', { 0: (error as Error).message }),
|
||||
status: 'error',
|
||||
});
|
||||
});
|
||||
} else if (voices.length > 0) {
|
||||
ttsRef.current
|
||||
.setMetadata(voices[0].value, OUTPUT_FORMAT.AUDIO_24KHZ_48KBITRATE_MONO_MP3, {})
|
||||
.catch((error) => {
|
||||
initAttempts.current += 1;
|
||||
console.error('Error initializing TTS:', error);
|
||||
showToast({
|
||||
message: localize('com_nav_tts_init_error', { 0: (error as Error).message }),
|
||||
status: 'error',
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [voiceName, showToast, localize, voices]);
|
||||
|
||||
const appendNextBuffer = useCallback(() => {
|
||||
if (
|
||||
sourceBufferRef.current &&
|
||||
!sourceBufferRef.current.updating &&
|
||||
pendingBuffers.current.length > 0
|
||||
) {
|
||||
const nextBuffer = pendingBuffers.current.shift();
|
||||
if (nextBuffer) {
|
||||
try {
|
||||
sourceBufferRef.current.appendBuffer(nextBuffer);
|
||||
} catch (error) {
|
||||
console.error('Error appending buffer:', error);
|
||||
showToast({
|
||||
message: localize('com_nav_buffer_append_error'),
|
||||
status: 'error',
|
||||
});
|
||||
pendingBuffers.current.unshift(nextBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [showToast, localize]);
|
||||
|
||||
const onSourceOpen = useCallback(() => {
|
||||
if (!sourceBufferRef.current && mediaSourceRef.current) {
|
||||
try {
|
||||
sourceBufferRef.current = mediaSourceRef.current.addSourceBuffer('audio/mpeg');
|
||||
sourceBufferRef.current.addEventListener('updateend', appendNextBuffer);
|
||||
} catch (error) {
|
||||
console.error('Error adding source buffer:', error);
|
||||
showToast({
|
||||
message: localize('com_nav_source_buffer_error'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [showToast, localize, appendNextBuffer]);
|
||||
|
||||
const initializeMediaSource = useCallback(() => {
|
||||
if (!mediaSourceRef.current) {
|
||||
mediaSourceRef.current = new MediaSource();
|
||||
audioElementRef.current = new Audio();
|
||||
audioElementRef.current.src = URL.createObjectURL(mediaSourceRef.current);
|
||||
}
|
||||
|
||||
const mediaSource = mediaSourceRef.current;
|
||||
if (mediaSource.readyState === 'open') {
|
||||
onSourceOpen();
|
||||
} else {
|
||||
mediaSource.addEventListener('sourceopen', onSourceOpen);
|
||||
}
|
||||
}, [onSourceOpen]);
|
||||
|
||||
const generateSpeechEdge = useCallback(
|
||||
(text: string) => {
|
||||
const generate = async () => {
|
||||
try {
|
||||
if (!ttsRef.current || !audioElementRef.current) {
|
||||
throw new Error('TTS or Audio element not initialized');
|
||||
}
|
||||
|
||||
setIsSpeaking(true);
|
||||
pendingBuffers.current = [];
|
||||
|
||||
const result = await ttsRef.current.toStream(text);
|
||||
const readable = result.audioStream;
|
||||
|
||||
readable.on('data', (chunk: Buffer) => {
|
||||
pendingBuffers.current.push(new Uint8Array(chunk));
|
||||
appendNextBuffer();
|
||||
});
|
||||
|
||||
readable.on('end', () => {
|
||||
if (mediaSourceRef.current && mediaSourceRef.current.readyState === 'open') {
|
||||
mediaSourceRef.current.endOfStream();
|
||||
}
|
||||
});
|
||||
|
||||
audioElementRef.current.onended = () => {
|
||||
setIsSpeaking(false);
|
||||
};
|
||||
|
||||
await audioElementRef.current.play();
|
||||
} catch (error) {
|
||||
console.error('Error generating speech:', error);
|
||||
showToast({
|
||||
message: localize('com_nav_audio_play_error', { 0: (error as Error).message }),
|
||||
status: 'error',
|
||||
});
|
||||
setIsSpeaking(false);
|
||||
}
|
||||
};
|
||||
|
||||
generate();
|
||||
},
|
||||
[setIsSpeaking, appendNextBuffer, showToast, localize],
|
||||
);
|
||||
|
||||
const cancelSpeechEdge = useCallback(() => {
|
||||
try {
|
||||
if (audioElementRef.current) {
|
||||
audioElementRef.current.pause();
|
||||
audioElementRef.current.currentTime = 0;
|
||||
}
|
||||
if (mediaSourceRef.current && mediaSourceRef.current.readyState === 'open') {
|
||||
mediaSourceRef.current.endOfStream();
|
||||
}
|
||||
pendingBuffers.current = [];
|
||||
setIsSpeaking(false);
|
||||
} catch (error) {
|
||||
console.error('Error cancelling speech:', error);
|
||||
showToast({
|
||||
message: localize('com_nav_speech_cancel_error'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [setIsSpeaking, showToast, localize]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowserSupported) {
|
||||
return;
|
||||
}
|
||||
fetchVoices();
|
||||
}, [fetchVoices, isBrowserSupported]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowserSupported) {
|
||||
return;
|
||||
}
|
||||
initializeTTS();
|
||||
}, [voiceName, initializeTTS, isBrowserSupported]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowserSupported) {
|
||||
return;
|
||||
}
|
||||
initializeMediaSource();
|
||||
return () => {
|
||||
if (mediaSourceRef.current) {
|
||||
URL.revokeObjectURL(audioElementRef.current?.src ?? '');
|
||||
}
|
||||
};
|
||||
}, [initializeMediaSource, isBrowserSupported]);
|
||||
|
||||
if (!isBrowserSupported) {
|
||||
return {
|
||||
generateSpeechEdge: () => ({}),
|
||||
cancelSpeechEdge: () => ({}),
|
||||
voices: [],
|
||||
};
|
||||
}
|
||||
|
||||
return { generateSpeechEdge, cancelSpeechEdge, voices };
|
||||
}
|
||||
|
||||
export default useTextToSpeechEdge;
|
|
@ -298,7 +298,6 @@
|
|||
"com_nav_automatic_playback": "Autoplay Latest Message",
|
||||
"com_nav_balance": "Balance",
|
||||
"com_nav_browser": "Browser",
|
||||
"com_nav_buffer_append_error": "Problem with audio streaming. The playback may be interrupted.",
|
||||
"com_nav_center_chat_input": "Center Chat Input on Welcome Screen",
|
||||
"com_nav_change_picture": "Change picture",
|
||||
"com_nav_chat_commands": "Chat Commands",
|
||||
|
@ -321,7 +320,6 @@
|
|||
"com_nav_delete_cache_storage": "Delete TTS cache storage",
|
||||
"com_nav_delete_data_info": "All your data will be deleted.",
|
||||
"com_nav_delete_warning": "WARNING: This will permanently delete your account.",
|
||||
"com_nav_edge": "Edge",
|
||||
"com_nav_edit_chat_badges": "Edit Chat Badges",
|
||||
"com_nav_enable_cache_tts": "Enable cache TTS",
|
||||
"com_nav_enable_cloud_browser_voice": "Use cloud-based voices",
|
||||
|
@ -421,8 +419,6 @@
|
|||
"com_nav_show_thinking": "Open Thinking Dropdowns by Default",
|
||||
"com_nav_slash_command": "/-Command",
|
||||
"com_nav_slash_command_description": "Toggle command \"/\" for selecting a prompt via keyboard",
|
||||
"com_nav_source_buffer_error": "Error setting up audio playback. Please refresh the page.",
|
||||
"com_nav_speech_cancel_error": "Unable to stop audio playback. You may need to refresh the page.",
|
||||
"com_nav_speech_to_text": "Speech to Text",
|
||||
"com_nav_stop_generating": "Stop generating",
|
||||
"com_nav_text_to_speech": "Text to Speech",
|
||||
|
@ -435,13 +431,10 @@
|
|||
"com_nav_tool_dialog_description": "Assistant must be saved to persist tool selections.",
|
||||
"com_nav_tool_remove": "Remove",
|
||||
"com_nav_tool_search": "Search tools",
|
||||
"com_nav_tts_init_error": "Failed to initialize text-to-speech: {{0}}",
|
||||
"com_nav_tts_unsupported_error": "Text-to-speech for the selected engine is not supported in this browser.",
|
||||
"com_nav_user": "USER",
|
||||
"com_nav_user_msg_markdown": "Render user messages as markdown",
|
||||
"com_nav_user_name_display": "Display username in messages",
|
||||
"com_nav_voice_select": "Voice",
|
||||
"com_nav_voices_fetch_error": "Could not retrieve voice options. Please check your internet connection.",
|
||||
"com_show_agent_settings": "Show Agent Settings",
|
||||
"com_show_completion_settings": "Show Completion Settings",
|
||||
"com_show_examples": "Show Examples",
|
||||
|
|
92
package-lock.json
generated
92
package-lock.json
generated
|
@ -1234,7 +1234,6 @@
|
|||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.394.0",
|
||||
"match-sorter": "^6.3.4",
|
||||
"msedge-tts": "^2.0.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"rc-input-number": "^7.4.2",
|
||||
"react": "^18.2.0",
|
||||
|
@ -3233,25 +3232,6 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"client/node_modules/msedge-tts": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/msedge-tts/-/msedge-tts-2.0.0.tgz",
|
||||
"integrity": "sha512-9qmAh80/rvEFCWDlfqHvrZzf9zioEqksiwpNKSy8MuBud27D6FNPVTHNDc1c37dX0u6w7iYe++Dg/V0a9fAFSw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"axios": "^1.5.0",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
"process": "^0.11.10",
|
||||
"randombytes": "^2.1.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"ws": "^8.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/node-releases": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
|
@ -24042,6 +24022,7 @@
|
|||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
|
||||
"integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
|
@ -24051,7 +24032,8 @@
|
|||
"node_modules/asn1.js/node_modules/bn.js": {
|
||||
"version": "4.12.1",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
|
||||
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg=="
|
||||
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/assert": {
|
||||
"version": "2.1.0",
|
||||
|
@ -24485,7 +24467,8 @@
|
|||
"node_modules/bn.js": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
|
||||
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
|
||||
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
|
@ -24552,7 +24535,8 @@
|
|||
"node_modules/brorand": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
|
||||
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/browser-resolve": {
|
||||
"version": "2.0.0",
|
||||
|
@ -24567,6 +24551,7 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-xor": "^1.0.3",
|
||||
"cipher-base": "^1.0.0",
|
||||
|
@ -24580,6 +24565,7 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
|
||||
"integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"browserify-aes": "^1.0.4",
|
||||
"browserify-des": "^1.0.0",
|
||||
|
@ -24590,6 +24576,7 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
|
||||
"integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"des.js": "^1.0.0",
|
||||
|
@ -24601,6 +24588,7 @@
|
|||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
|
||||
"integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^5.0.0",
|
||||
"randombytes": "^2.0.1"
|
||||
|
@ -24610,6 +24598,7 @@
|
|||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz",
|
||||
"integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^5.2.1",
|
||||
"browserify-rsa": "^4.1.0",
|
||||
|
@ -24630,6 +24619,7 @@
|
|||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz",
|
||||
"integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"safe-buffer": "^5.2.1"
|
||||
|
@ -24745,7 +24735,8 @@
|
|||
"node_modules/buffer-xor": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="
|
||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/builtin-modules": {
|
||||
"version": "3.3.0",
|
||||
|
@ -25031,6 +25022,7 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
|
@ -25612,6 +25604,7 @@
|
|||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
|
||||
"integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.1.0",
|
||||
"elliptic": "^6.5.3"
|
||||
|
@ -25620,12 +25613,14 @@
|
|||
"node_modules/create-ecdh/node_modules/bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
|
@ -25638,6 +25633,7 @@
|
|||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.3",
|
||||
"create-hash": "^1.1.0",
|
||||
|
@ -25721,6 +25717,7 @@
|
|||
"version": "3.12.1",
|
||||
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz",
|
||||
"integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"browserify-cipher": "^1.0.1",
|
||||
"browserify-sign": "^4.2.3",
|
||||
|
@ -25746,6 +25743,7 @@
|
|||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz",
|
||||
"integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"safe-buffer": "^5.2.1"
|
||||
|
@ -26200,6 +26198,7 @@
|
|||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz",
|
||||
"integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
|
@ -26293,6 +26292,7 @@
|
|||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.1.0",
|
||||
"miller-rabin": "^4.0.0",
|
||||
|
@ -26302,7 +26302,8 @@
|
|||
"node_modules/diffie-hellman/node_modules/bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "3.0.1",
|
||||
|
@ -26457,6 +26458,7 @@
|
|||
"version": "6.6.1",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
|
||||
"integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bn.js": "^4.11.9",
|
||||
|
@ -26472,6 +26474,7 @@
|
|||
"version": "4.12.1",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
|
||||
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emittery": {
|
||||
|
@ -27612,6 +27615,7 @@
|
|||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"md5.js": "^1.3.4",
|
||||
"safe-buffer": "^5.1.1"
|
||||
|
@ -29036,6 +29040,7 @@
|
|||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.6.0",
|
||||
|
@ -29049,6 +29054,7 @@
|
|||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
@ -29062,6 +29068,7 @@
|
|||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
|
@ -29306,6 +29313,7 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
"integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hash.js": "^1.0.3",
|
||||
|
@ -30484,15 +30492,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/isomorphic-ws": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
|
||||
"integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ws": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
|
@ -32711,6 +32710,7 @@
|
|||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
|
@ -33988,6 +33988,7 @@
|
|||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
||||
"integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.0.0",
|
||||
"brorand": "^1.0.1"
|
||||
|
@ -33999,7 +34000,8 @@
|
|||
"node_modules/miller-rabin/node_modules/bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "3.0.0",
|
||||
|
@ -34065,12 +34067,14 @@
|
|||
"node_modules/minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/minimalistic-crypto-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||
"integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
|
@ -35031,6 +35035,7 @@
|
|||
"version": "5.1.7",
|
||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz",
|
||||
"integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"asn1.js": "^4.10.1",
|
||||
"browserify-aes": "^1.2.0",
|
||||
|
@ -35047,6 +35052,7 @@
|
|||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz",
|
||||
"integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"safe-buffer": "^5.2.1"
|
||||
|
@ -35324,6 +35330,7 @@
|
|||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
|
||||
"integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"create-hash": "^1.1.2",
|
||||
"create-hmac": "^1.1.4",
|
||||
|
@ -36818,6 +36825,7 @@
|
|||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
|
@ -36925,6 +36933,7 @@
|
|||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
|
||||
"integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.1.0",
|
||||
"browserify-rsa": "^4.0.0",
|
||||
|
@ -36937,7 +36946,8 @@
|
|||
"node_modules/public-encrypt/node_modules/bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
|
@ -37036,6 +37046,7 @@
|
|||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
|
@ -37044,6 +37055,7 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
|
||||
"integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"randombytes": "^2.0.5",
|
||||
"safe-buffer": "^5.1.0"
|
||||
|
@ -38692,6 +38704,7 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
|
@ -39168,6 +39181,7 @@
|
|||
"version": "2.4.11",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
|
@ -39575,6 +39589,7 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
|
||||
"integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "~2.0.4",
|
||||
"readable-stream": "^3.5.0"
|
||||
|
@ -39584,6 +39599,7 @@
|
|||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue