diff --git a/src/components/chat-input/chat-input.scss b/src/components/chat-input/chat-input.scss index db0f756113a40f1bdadbe9a9abd96b3b115f0ab1..49316916820825458b79d5b16be74f5d88208c86 100644 --- a/src/components/chat-input/chat-input.scss +++ b/src/components/chat-input/chat-input.scss @@ -1,3 +1,4 @@ +/* stylelint-disable declaration-block-no-redundant-longhand-properties */ @include b(chat-input) { display: flex; align-items: center; @@ -9,6 +10,7 @@ max-height: 6em; overflow-x: hidden !important; overflow-y: auto; + font-size: 16px; resize: none; background-color: #{getCssVar('ai-chat', 'background-color')}; border: none; @@ -78,4 +80,4 @@ animation: loading-change .8s infinite; } } -} +} \ No newline at end of file diff --git a/src/components/chat-input/chat-input.tsx b/src/components/chat-input/chat-input.tsx index 0093379a0c87d2a2d10ba1f7b6a0530dedd4b64f..4bfb430a3f177fc3d528ace0282aac4459910b5d 100644 --- a/src/components/chat-input/chat-input.tsx +++ b/src/components/chat-input/chat-input.tsx @@ -1,8 +1,9 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { TargetedEvent, useCallback, useEffect, useRef } from 'preact/compat'; import { useComputed, useSignal } from '@preact/signals'; import autosize from 'autosize'; import { Namespace } from '../../utils'; -import { SendSvg } from '../../icons'; +import { AudioSvg, RecordingSvg, SendSvg } from '../../icons'; import { AiChatController } from '../../controller'; import './chat-input.scss'; @@ -19,6 +20,10 @@ export interface ChatInputProps { const ns = new Namespace('chat-input'); +// 语音识别构造器 +const SpeechRecognition = + (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition; + export const ChatInput = (props: ChatInputProps) => { const textareaRef = useRef(null); @@ -26,6 +31,38 @@ export const ChatInput = (props: ChatInputProps) => { const isLoading = useSignal(false); + // 是否正在语音输入 + const recording = useSignal(false); + + // 语音识别实例 + const recognition = useRef(); + + if (SpeechRecognition && !recognition.current) { + recognition.current = new SpeechRecognition(); + + recognition.current.onstart = () => { + recording.value = true; + }; + + recognition.current.onend = () => { + recording.value = false; + }; + + recognition.current.onresult = (e: any) => { + const transcript = e.results?.[0]?.[0]?.transcript; + if (transcript) { + input.value = `${input.value}${transcript}`; + } + }; + } + + // 处理语音输入按钮点击事件 + const handleRecordButtonClick = () => { + if (recognition.current && !recording.value) { + recognition.current.start(); + } + }; + const onInput = useCallback( (e: TargetedEvent) => { input.value = (e.target as HTMLTextAreaElement).value; @@ -86,7 +123,14 @@ export const ChatInput = (props: ChatInputProps) => { />
+ {recording.value ? : } +
+
{ + return ( + + ); +}; diff --git a/src/icons/index.ts b/src/icons/index.ts index 98286684b56466407f51225b85aba7eaec3f1705..228d4abdcd63733ffc08c63e90f74d7d7a72db97 100644 --- a/src/icons/index.ts +++ b/src/icons/index.ts @@ -8,3 +8,5 @@ export { FullScreenSvg } from './full-screen-svg'; export { CloseFullScreenSvg } from './close-full-screen-svg'; export { MinimizeSvg } from './minimize-svg'; export { AISvg } from './ai-svg'; +export { AudioSvg } from './audio-svg'; +export { RecordingSvg } from './recording-svg'; diff --git a/src/icons/recording-svg.tsx b/src/icons/recording-svg.tsx new file mode 100644 index 0000000000000000000000000000000000000000..38ee7a435b1b3e6db40c385c5de501b12cf85147 --- /dev/null +++ b/src/icons/recording-svg.tsx @@ -0,0 +1,55 @@ +// 语音输入loading图标 +export const RecordingSvg = () => { + const SIZE = 1000; + const COUNT = 4; + const RECT_WIDTH = 140; + const RECT_RADIUS = RECT_WIDTH / 2; + const RECT_HEIGHT_MIN = 250; + const RECT_HEIGHT_MAX = 500; + const DURATION = 0.8; + return ( + + {Array.from({ length: COUNT }).map((_, index) => { + const dest = (SIZE - RECT_WIDTH * COUNT) / (COUNT - 1); + const x = index * (dest + RECT_WIDTH); + const yMin = SIZE / 2 - RECT_HEIGHT_MIN / 2; + const yMax = SIZE / 2 - RECT_HEIGHT_MAX / 2; + + return ( + + + + + ); + })} + + ); +};