Skip to content

Commit 4121827

Browse files
committed
Enhance WebSocket error handling and STT error management
- Update OpenAIWhisperClient to ensure WebSocket event listeners are removed only if initialized. - Add error handling in ChatTextArea to set and clear STT error state during recording. - Integrate error display in STTSetupPopover for improved user feedback on STT setup issues. - Sync optimistic UI state in useSTT hook for immediate feedback on recording actions.
1 parent f1f3c77 commit 4121827

File tree

4 files changed

+44
-7
lines changed

4 files changed

+44
-7
lines changed

src/services/stt/OpenAIWhisperClient.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,27 +197,41 @@ export class OpenAIWhisperClient extends EventEmitter {
197197

198198
const onOpen = () => {
199199
clearTimeout(timeout)
200-
this.ws!.off("open", onOpen)
201-
this.ws!.off("error", onError)
200+
if (this.ws) {
201+
this.ws.off("open", onOpen)
202+
this.ws.off("error", onError)
203+
}
202204
resolve()
203205
}
204206

205207
const onError = (error: Error) => {
206208
clearTimeout(timeout)
207-
this.ws!.off("open", onOpen)
208-
this.ws!.off("error", onError)
209+
if (this.ws) {
210+
this.ws.off("open", onOpen)
211+
this.ws.off("error", onError)
212+
}
209213
reject(new Error(`WebSocket connection failed: ${error.message}`))
210214
}
211215

212-
this.ws!.once("open", onOpen)
213-
this.ws!.once("error", onError)
216+
if (this.ws) {
217+
this.ws.once("open", onOpen)
218+
this.ws.once("error", onError)
219+
} else {
220+
reject(new Error("WebSocket not initialized"))
221+
}
214222
})
215223

216224
this.isConnecting = false
217225
this.reconnectAttempts = 0
218226
this.emit("connected")
219227
} catch (error) {
220228
this.isConnecting = false
229+
try {
230+
this.ws?.removeAllListeners()
231+
if (this.ws?.readyState === WebSocket.OPEN || this.ws?.readyState === WebSocket.CONNECTING) {
232+
this.ws?.close()
233+
}
234+
} catch (_cleanupError) {}
221235
this.ws = null
222236
throw error
223237
}

webview-ui/src/components/chat/ChatTextArea.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
343343
},
344344
onError: (error) => {
345345
console.error("STT error:", error)
346+
setSttError(error)
346347
recordingStartStateRef.current = null
347348
},
348349
})
@@ -382,6 +383,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
382383
afterCursor: inputValue.slice(pos),
383384
position: pos,
384385
}
386+
setSttError(null)
385387
}
386388
}, [isRecording, inputValue])
387389

@@ -475,6 +477,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
475477

476478
// kilocode_change start: Popover state for STT setup help
477479
const [sttSetupPopoverOpen, setSttSetupPopoverOpen] = useState(false)
480+
const [sttError, setSttError] = useState<string | null>(null)
478481
const handleMicrophoneClick = useCallback(() => {
479482
// If STT is unavailable, open setup popover instead of starting recording
480483
if (!speechToTextStatus?.available) {
@@ -485,6 +488,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
485488
if (isRecording) {
486489
stopSTT()
487490
} else {
491+
setSttError(null) // Clear any previous error when starting new recording
488492
startSTT(language || "en") // Pass user's language from extension state
489493
}
490494
}, [isRecording, startSTT, stopSTT, language, speechToTextStatus?.available])
@@ -1760,7 +1764,8 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
17601764
speechToTextStatus={speechToTextStatus}
17611765
open={sttSetupPopoverOpen}
17621766
onOpenChange={setSttSetupPopoverOpen}
1763-
onFfmpegHelpClick={handleFfmpegHelpClick}>
1767+
onFfmpegHelpClick={handleFfmpegHelpClick}
1768+
error={sttError}>
17641769
<MicrophoneButton
17651770
isRecording={isRecording}
17661771
onClick={handleMicrophoneClick}

webview-ui/src/components/chat/STTSetupPopover.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,21 @@ interface STTSetupPopoverProps {
1616
open: boolean
1717
onOpenChange: (open: boolean) => void
1818
onFfmpegHelpClick: () => void
19+
error?: string | null
1920
}
2021

2122
interface STTSetupPopoverContentProps {
2223
reason?: "openaiKeyMissing" | "ffmpegNotInstalled"
2324
onFfmpegHelpClick: () => void
2425
onOpenChange?: (open: boolean) => void
26+
error?: string | null
2527
}
2628

2729
export const STTSetupPopoverContent: React.FC<STTSetupPopoverContentProps> = ({
2830
reason,
2931
onFfmpegHelpClick,
3032
onOpenChange,
33+
error,
3134
}) => {
3235
const { t } = useTranslation()
3336
const docsUrl = buildDocLink("features/experimental/voice-transcription", "stt_setup")
@@ -59,6 +62,18 @@ export const STTSetupPopoverContent: React.FC<STTSetupPopoverContentProps> = ({
5962
/>
6063
</p>
6164

65+
{error && (
66+
<div
67+
className="my-0 mb-3 p-2 rounded text-sm border"
68+
style={{
69+
backgroundColor: "var(--vscode-inputValidation-errorBackground, rgba(255, 0, 0, 0.1))",
70+
borderColor: "var(--vscode-inputValidation-errorBorder, #ff0000)",
71+
color: "var(--vscode-errorForeground, #ff0000)",
72+
}}>
73+
<strong>Error:</strong> {error}
74+
</div>
75+
)}
76+
6277
<ul className="my-0 mb-0 list-disc list-inside text-sm text-vscode-descriptionForeground space-y-1">
6378
{reason === "openaiKeyMissing" || !reason ? (
6479
<li>{t("kilocode:speechToText.setupPopover.openAiReason")}</li>
@@ -94,6 +109,7 @@ export const STTSetupPopover: React.FC<STTSetupPopoverProps> = ({
94109
open,
95110
onOpenChange,
96111
onFfmpegHelpClick,
112+
error,
97113
}) => {
98114
const portalContainer = useRooPortal("roo-portal")
99115

@@ -120,6 +136,7 @@ export const STTSetupPopover: React.FC<STTSetupPopoverProps> = ({
120136
reason={reason}
121137
onFfmpegHelpClick={onFfmpegHelpClick}
122138
onOpenChange={onOpenChange}
139+
error={error}
123140
/>
124141
</PopoverContent>
125142
</Popover>

webview-ui/src/hooks/useSTT.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export function useSTT(options: UseSTTOptions = {}): UseSTTReturn {
9292
if (msg.sessionId !== sessionIdRef.current) return
9393

9494
setRealIsRecording(false)
95+
setOptimisticIsRecording(false) // Immediately sync optimistic state on stop
9596
setVolume(0)
9697

9798
if (msg.reason === "completed") {

0 commit comments

Comments
 (0)