Skip to content

Commit 8bd2d97

Browse files
docs: Add JSDoc examples to core utilities and expand README with SSR, TypeScript, advanced examples, and troubleshooting.
1 parent e9eee47 commit 8bd2d97

4 files changed

Lines changed: 226 additions & 9 deletions

File tree

README.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,180 @@ insertTextAtCursor(inputRef, 'hello', currentValue, setValue)
187187

188188
> **Note:** The Web Speech API requires HTTPS in production (except localhost).
189189
190+
---
191+
192+
## SSR / Next.js
193+
194+
This package is SSR-safe. The Web Speech API is only accessed on the client.
195+
196+
### Next.js App Router
197+
198+
```tsx
199+
'use client'
200+
201+
import { useSpeechInput } from '@syntropy-labs/react-web-speech'
202+
203+
export function VoiceButton() {
204+
const { isListening, toggle, isSupported } = useSpeechInput()
205+
206+
if (!isSupported) return null
207+
208+
return (
209+
<button onClick={toggle}>
210+
{isListening ? 'Stop' : 'Speak'}
211+
</button>
212+
)
213+
}
214+
```
215+
216+
### Next.js Pages Router
217+
218+
```tsx
219+
import dynamic from 'next/dynamic'
220+
221+
const VoiceInput = dynamic(
222+
() => import('../components/VoiceInput'),
223+
{ ssr: false }
224+
)
225+
```
226+
227+
---
228+
229+
## TypeScript
230+
231+
All types are exported:
232+
233+
```tsx
234+
import type {
235+
UseSpeechInputOptions,
236+
UseSpeechInputReturn,
237+
UseSpeechInputWithCursorOptions,
238+
UseSpeechInputWithCursorReturn,
239+
SpeechError,
240+
SpeechErrorType,
241+
MicPermissionState,
242+
CursorPosition,
243+
BrowserCapabilities,
244+
} from '@syntropy-labs/react-web-speech'
245+
```
246+
247+
---
248+
249+
## Advanced Examples
250+
251+
### Voice-controlled Form
252+
253+
```tsx
254+
import { useState, useRef } from 'react'
255+
import { useSpeechInputWithCursor } from '@syntropy-labs/react-web-speech'
256+
257+
function VoiceForm() {
258+
const [formData, setFormData] = useState({ name: '', email: '' })
259+
const [activeField, setActiveField] = useState<'name' | 'email'>('name')
260+
const inputRefs = {
261+
name: useRef<HTMLInputElement>(null),
262+
email: useRef<HTMLInputElement>(null),
263+
}
264+
265+
const { toggle, isListening } = useSpeechInputWithCursor({
266+
inputRef: inputRefs[activeField],
267+
value: formData[activeField],
268+
onChange: (value) => setFormData({ ...formData, [activeField]: value }),
269+
})
270+
271+
return (
272+
<form>
273+
<input
274+
ref={inputRefs.name}
275+
value={formData.name}
276+
onFocus={() => setActiveField('name')}
277+
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
278+
placeholder="Name"
279+
/>
280+
<input
281+
ref={inputRefs.email}
282+
value={formData.email}
283+
onFocus={() => setActiveField('email')}
284+
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
285+
placeholder="Email"
286+
/>
287+
<button type="button" onClick={toggle}>
288+
{isListening ? '🔴 Stop' : '🎙️ Speak'}
289+
</button>
290+
</form>
291+
)
292+
}
293+
```
294+
295+
### Real-time Transcript Display
296+
297+
```tsx
298+
import { useSpeechInput } from '@syntropy-labs/react-web-speech'
299+
300+
function LiveTranscript() {
301+
const { transcript, interimTranscript, isListening, toggle } = useSpeechInput({
302+
interimResults: true,
303+
continuous: true,
304+
})
305+
306+
return (
307+
<div>
308+
<button onClick={toggle}>{isListening ? 'Stop' : 'Start'}</button>
309+
<p>
310+
{transcript}
311+
<span style={{ opacity: 0.5 }}>{interimTranscript}</span>
312+
</p>
313+
</div>
314+
)
315+
}
316+
```
317+
318+
---
319+
320+
## Troubleshooting
321+
322+
### "Permission denied" error
323+
324+
The user has denied microphone access. They need to:
325+
1. Click the 🔒 icon in the browser address bar
326+
2. Reset microphone permissions
327+
3. Refresh the page
328+
329+
### "Network error"
330+
331+
The Web Speech API requires an internet connection. Chrome sends audio to Google's servers for processing.
332+
333+
### Recognition stops immediately
334+
335+
Some browsers stop recognition after detecting silence. Solutions:
336+
- Use `continuous: true` for longer sessions
337+
- Increase `silenceTimeout` (or set to `0` to disable)
338+
339+
### Works in development but not production
340+
341+
The Web Speech API requires HTTPS. Make sure your production site uses SSL.
342+
343+
### Duplicate React error with yarn link
344+
345+
When testing locally with `yarn link`, add React aliases to your Vite config:
346+
347+
```ts
348+
// vite.config.ts
349+
import { defineConfig } from 'vite'
350+
import path from 'path'
351+
352+
export default defineConfig({
353+
resolve: {
354+
alias: {
355+
react: path.resolve('./node_modules/react'),
356+
'react-dom': path.resolve('./node_modules/react-dom'),
357+
},
358+
},
359+
})
360+
```
361+
362+
---
363+
190364
## Why This Package?
191365

192366
Existing React speech-to-text packages lack critical production-ready features:
@@ -226,3 +400,4 @@ yarn build
226400

227401
MIT © [SyntropyLabs](https://github.com/SyntropyLabs)
228402

403+

src/core/browser.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,17 @@ import type { BrowserCapabilities, SpeechRecognitionInstance } from '../types'
66
let cachedCapabilities: BrowserCapabilities | null = null
77

88
/**
9-
* Detect browser capabilities for Web Speech API
10-
* Results are cached for performance
9+
* Detect browser capabilities for Web Speech API.
10+
* Results are cached for performance.
1111
*
1212
* @returns Browser capabilities object
13+
* @example
14+
* ```ts
15+
* const caps = detectBrowserCapabilities()
16+
* if (!caps.isSupported) {
17+
* console.log('Speech API not supported')
18+
* }
19+
* ```
1320
*/
1421
export function detectBrowserCapabilities(): BrowserCapabilities {
1522
// Return cached result if available
@@ -78,8 +85,16 @@ export function clearCapabilitiesCache(): void {
7885
}
7986

8087
/**
81-
* Check if the current browser has known issues with Speech API
88+
* Check if the current browser has known issues with Speech API.
89+
*
8290
* @returns Warning message if issues exist, null otherwise
91+
* @example
92+
* ```ts
93+
* const warning = getBrowserCompatibilityWarning()
94+
* if (warning) {
95+
* console.warn(warning)
96+
* }
97+
* ```
8398
*/
8499
export function getBrowserCompatibilityWarning(): string | null {
85100
const { browserName, isSupported } = detectBrowserCapabilities()

src/core/permissions.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@ import type { MicPermissionState } from '../types'
22
import { detectBrowserCapabilities } from './browser'
33

44
/**
5-
* Get the current microphone permission state
6-
* Uses Permissions API when available, falls back to 'prompt'
5+
* Get the current microphone permission state.
6+
* Uses Permissions API when available, falls back to 'prompt'.
77
*
88
* @returns Promise resolving to permission state
9+
* @example
10+
* ```ts
11+
* const state = await getMicPermissionState()
12+
* if (state === 'denied') {
13+
* alert('Please enable microphone access')
14+
* }
15+
* ```
916
*/
1017
export async function getMicPermissionState(): Promise<MicPermissionState> {
1118
const { isSupported, supportsPermissionsAPI } = detectBrowserCapabilities()
@@ -73,10 +80,17 @@ export function subscribeToPermissionChanges(
7380
}
7481

7582
/**
76-
* Request microphone permission by attempting to access the device
77-
* This triggers the browser's permission prompt
83+
* Request microphone permission by attempting to access the device.
84+
* This triggers the browser's permission prompt.
7885
*
7986
* @returns Promise resolving to the new permission state
87+
* @example
88+
* ```ts
89+
* const state = await requestMicPermission()
90+
* if (state === 'granted') {
91+
* console.log('Microphone access granted!')
92+
* }
93+
* ```
8094
*/
8195
export async function requestMicPermission(): Promise<MicPermissionState> {
8296
const { isSupported } = detectBrowserCapabilities()

src/core/recognition.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,24 @@ const ERROR_MESSAGES: Record<SpeechErrorType, string> = {
3737
}
3838

3939
/**
40-
* Create a configured SpeechRecognition instance
40+
* Create a configured SpeechRecognition instance.
4141
*
4242
* @param options - Recognition configuration options
43-
* @param callbacks - Event callbacks
43+
* @param callbacks - Event callbacks for recognition events
4444
* @returns Configured recognition instance, or null if not supported
45+
* @example
46+
* ```ts
47+
* const recognition = createRecognitionInstance(
48+
* { lang: 'en-US', continuous: false },
49+
* {
50+
* onResult: (text, isFinal) => console.log(text),
51+
* onError: (err) => console.error(err),
52+
* onStart: () => console.log('Started'),
53+
* onEnd: () => console.log('Ended'),
54+
* }
55+
* )
56+
* recognition?.start()
57+
* ```
4558
*/
4659
export function createRecognitionInstance(
4760
options: RecognitionOptions,

0 commit comments

Comments
 (0)