74 lines
1.9 KiB
Markdown
74 lines
1.9 KiB
Markdown
## Speech recognition timeout cleanup
|
|
|
|
Stop stray restart timers from keeping recognition alive
|
|
|
|
---
|
|
|
|
### Summary
|
|
|
|
`packages/app/src/utils/speech.ts` schedules 150ms `setTimeout` restarts in `recognition.onerror` and `recognition.onend` when `shouldContinue` is true. These timers are not tracked or cleared, so they can fire after `stop()`/cleanup and call `recognition.start()`, keeping recognition + closures alive unexpectedly.
|
|
|
|
This spec tracks restart timers explicitly and clears them on stop/cleanup.
|
|
|
|
---
|
|
|
|
### Scoped files (parallel-safe)
|
|
|
|
- `packages/app/src/utils/speech.ts`
|
|
|
|
---
|
|
|
|
### Goals
|
|
|
|
- Ensure no restart timers remain scheduled after `stop()` or `onCleanup`
|
|
- Prevent `recognition.start()` from being called after cleanup
|
|
- Keep behavior identical in the normal recording flow
|
|
|
|
---
|
|
|
|
### Non-goals
|
|
|
|
- Changing the recognition UX/state machine beyond timer tracking
|
|
|
|
---
|
|
|
|
### Proposed approach
|
|
|
|
- Add `let restartTimer: number | undefined`.
|
|
- Add helpers:
|
|
- `clearRestart()`
|
|
- `scheduleRestart()` (guards `shouldContinue` + `recognition`)
|
|
- Replace both raw `setTimeout(..., 150)` uses with `window.setTimeout` stored in `restartTimer`.
|
|
- Call `clearRestart()` in:
|
|
- `start()`
|
|
- `stop()`
|
|
- `onCleanup(...)`
|
|
- `recognition.onstart` (reset state)
|
|
- any path that exits recording due to error
|
|
|
|
---
|
|
|
|
### Implementation steps
|
|
|
|
1. Introduce `restartTimer` and helpers
|
|
|
|
2. Replace `setTimeout(() => recognition?.start(), 150)` occurrences
|
|
|
|
3. Clear the timer in all stop/cleanup paths
|
|
|
|
---
|
|
|
|
### Acceptance criteria
|
|
|
|
- After calling `stop()` or disposing the creator, there are no delayed restarts
|
|
- No unexpected `recognition.start()` calls occur after recording is stopped
|
|
|
|
---
|
|
|
|
### Validation plan
|
|
|
|
- Manual:
|
|
- Start/stop recording repeatedly
|
|
- Trigger a `no-speech` error and confirm restarts only happen while recording is active
|
|
- Navigate away/unmount the component using `createSpeechRecognition` and confirm no restarts happen afterward
|