Skip to content

Commit 4c60cae

Browse files
committed
feat(voice-button): refactor speech recognition and transcript insertion logic
1 parent 39ca9cb commit 4c60cae

2 files changed

Lines changed: 26 additions & 15 deletions

File tree

packages/components/src/sender-actions/voice-button/index.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,31 @@ const insertTranscript = (transcript: string) => {
3434
return
3535
}
3636
37-
// 在单次录音会话期间,持续替换当前的语音插入范围
37+
// autoReplace 模式:替换当前范围内的内容
3838
const range = speechRange.value ?? {
3939
from: editorInstance.state.selection.from,
4040
to: editorInstance.state.selection.to,
4141
}
42-
const tr = editorInstance.state.tr.insertText(transcript, range.from, range.to)
43-
editorInstance.view.dispatch(tr)
42+
43+
// 用 insertContentAt 传 range,会替换该范围内的内容
44+
editorInstance.commands.insertContentAt(range, transcript)
45+
46+
// 更新范围,下次调用时覆盖本次插入的内容
4447
speechRange.value = {
4548
from: range.from,
4649
to: range.from + transcript.length,
4750
}
51+
4852
editorInstance.commands.focus('end')
4953
}
5054
51-
// 语音配置 - 使用普通对象而不是 computed,避免每次都创建新对象
5255
const speechOptions = {
5356
...props.speechConfig,
5457
onStart: () => {
5558
resetSpeechRange()
5659
emit('speech-start')
5760
},
5861
onInterim: (transcript: string) => {
59-
if (props.speechConfig?.autoReplace) {
60-
insertTranscript(transcript)
61-
}
6262
emit('speech-interim', transcript)
6363
},
6464
onFinal: (transcript: string) => {

packages/components/src/sender-actions/voice-button/webSpeechHandler.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,25 @@ export class WebSpeechHandler implements SpeechHandler {
5252
callbacks.onEnd()
5353
}
5454
this.recognition.onresult = (event: SpeechRecognitionEvent) => {
55-
const transcript = Array.from(event.results)
56-
.map((result) => result[0].transcript)
57-
.join('')
58-
const current = event.results[event.resultIndex]
59-
if (current?.isFinal) {
60-
callbacks.onFinal(transcript)
61-
} else {
62-
callbacks.onInterim(transcript)
55+
// 标准做法:分离 interim 和 final 结果
56+
let interimTranscript = ''
57+
let finalTranscript = ''
58+
59+
// 只处理 resultIndex 之后的新增 results
60+
for (let i = event.resultIndex; i < event.results.length; i++) {
61+
const transcript = event.results[i][0].transcript
62+
if (event.results[i].isFinal) {
63+
finalTranscript += transcript
64+
} else {
65+
interimTranscript += transcript
66+
}
67+
}
68+
69+
// 如果有 final 结果,优先触发 onFinal
70+
if (finalTranscript) {
71+
callbacks.onFinal(finalTranscript)
72+
} else if (interimTranscript) {
73+
callbacks.onInterim(interimTranscript)
6374
}
6475
}
6576
this.recognition.onerror = (event: SpeechRecognitionErrorEvent) => {

0 commit comments

Comments
 (0)