@untemps/react-vocal 1.7.37 → 2.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -2
- package/README.md +72 -31
- package/dist/index.es.js +567 -2
- package/dist/index.js +7 -2
- package/package.json +39 -65
- package/.github/workflows/publish.yml +0 -30
- package/.prettierignore +0 -2
- package/.prettierrc +0 -29
- package/CLAUDE.md +0 -55
- package/assets/icon-idle.png +0 -0
- package/assets/icon-listening.png +0 -0
- package/assets/microphone.png +0 -0
- package/assets/react-vocal.png +0 -0
- package/babel.config.js +0 -12
- package/commitlint.config.js +0 -7
- package/dev/babel.config.js +0 -4
- package/dev/package.json +0 -14
- package/dev/public/index.html +0 -24
- package/dev/rollup.config.js +0 -29
- package/dev/src/index.js +0 -46
- package/dev/yarn.lock +0 -201
- package/dist/index.umd.js +0 -2
- package/jest/jest.setup.js +0 -72
- package/rollup.config.js +0 -42
- package/src/components/Icon.js +0 -37
- package/src/components/Vocal.js +0 -235
- package/src/components/__tests__/Icon.test.js +0 -42
- package/src/components/__tests__/Vocal.test.js +0 -273
- package/src/components/__tests__/VocalWithMockedUseVocal.test.js +0 -40
- package/src/hooks/__tests__/useCommands.test.js +0 -64
- package/src/hooks/__tests__/useTimeout.test.js +0 -69
- package/src/hooks/__tests__/useVocal.test.js +0 -197
- package/src/hooks/useCommands.js +0 -21
- package/src/hooks/useTimeout.js +0 -23
- package/src/hooks/useVocal.js +0 -58
- package/src/index.js +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,102 @@
|
|
|
1
|
-
|
|
1
|
+
# [2.0.0-beta.10](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.9...v2.0.0-beta.10) (2026-05-24)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Add aria-hidden to decorative SVG in Icon component ([#148](https://github.com/untemps/react-vocal/issues/148)) ([5e00e34](https://github.com/untemps/react-vocal/commit/5e00e34e991b1a5c1a29a393ac885a76864c601c))
|
|
7
|
+
|
|
8
|
+
# [2.0.0-beta.9](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.8...v2.0.0-beta.9) (2026-05-24)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* Prevent stale Fuse instance in useCommands after async fuse.js import ([#147](https://github.com/untemps/react-vocal/issues/147)) ([a83f06d](https://github.com/untemps/react-vocal/commit/a83f06d73644d1029e735b38dc06c8614470e19e))
|
|
14
|
+
|
|
15
|
+
# [2.0.0-beta.8](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.7...v2.0.0-beta.8) (2026-05-24)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* Migrate to @untemps/vocal 2.x functional API ([#146](https://github.com/untemps/react-vocal/issues/146)) ([582575f](https://github.com/untemps/react-vocal/commit/582575f391472e0e95b0c793b57c7c3edca71939))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
* `isSupported` is now a function instead of a boolean snapshot. Consumers must call it as `isSupported()`.
|
|
26
|
+
Migration:
|
|
27
|
+
// before
|
|
28
|
+
import { isSupported } from '@untemps/react-vocal'
|
|
29
|
+
if (isSupported) { ... }
|
|
30
|
+
// after
|
|
31
|
+
import { isSupported } from '@untemps/react-vocal'
|
|
32
|
+
if (isSupported()) { ... }
|
|
33
|
+
The function form is SSR-safe — it returns `false` when `window` is undefined, so consumers no longer need a manual `typeof window` guard before checking support.
|
|
34
|
+
* UMD bundle is no longer published.
|
|
35
|
+
`dist/index.umd.js` is removed from the release artifacts and from the npm tarball. Consumers loading react-vocal via `<script src="…/dist/index.umd.js">` or an AMD loader must migrate to the ES bundle (`dist/index.es.js`) via a module-aware loader (or use a bundler). The CJS bundle (`dist/index.js`) remains available for Node-style require().
|
|
36
|
+
|
|
37
|
+
# [2.0.0-beta.7](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.6...v2.0.0-beta.7) (2026-05-13)
|
|
38
|
+
|
|
39
|
+
# [2.0.0-beta.6](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.5...v2.0.0-beta.6) (2026-05-12)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Features
|
|
43
|
+
|
|
44
|
+
* Add continuous session support ([#118](https://github.com/untemps/react-vocal/issues/118)) ([690ba61](https://github.com/untemps/react-vocal/commit/690ba617746aa28499d7ea2d48751453a652ff5e))
|
|
45
|
+
|
|
46
|
+
# [2.0.0-beta.5](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.4...v2.0.0-beta.5) (2026-05-11)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Features
|
|
50
|
+
|
|
51
|
+
* Make fuse.js an optional peer dependency ([#117](https://github.com/untemps/react-vocal/issues/117)) ([a1c2a33](https://github.com/untemps/react-vocal/commit/a1c2a337f8d4ba4729c81f0a9b8664bcf914755f))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### BREAKING CHANGES
|
|
55
|
+
|
|
56
|
+
* fuse.js must now be installed separately to enable fuzzy matching for phrase commands.
|
|
57
|
+
|
|
58
|
+
# [2.0.0-beta.4](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.3...v2.0.0-beta.4) (2026-05-10)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
### Features
|
|
62
|
+
|
|
63
|
+
* Per-segment command matching, maxAlternatives, and exact lookup ([39b6370](https://github.com/untemps/react-vocal/commit/39b63701b2a15c058b5ae510be8ad4b4437d6763))
|
|
64
|
+
|
|
65
|
+
# [2.0.0-beta.3](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2026-05-07)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### Features
|
|
69
|
+
|
|
70
|
+
* Aggregate all result segments picking highest-confidence alternative ([#113](https://github.com/untemps/react-vocal/issues/113)) ([600600d](https://github.com/untemps/react-vocal/commit/600600dfde457df6fe93974d76d0b48599846082))
|
|
71
|
+
|
|
72
|
+
# [2.0.0-beta.2](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2026-05-07)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### Bug Fixes
|
|
76
|
+
|
|
77
|
+
* Stabilize event handlers and fix stale prop closures ([#112](https://github.com/untemps/react-vocal/issues/112)) ([69c5c00](https://github.com/untemps/react-vocal/commit/69c5c00ed73e792b6dd91dd6b294163e1c3d00dc))
|
|
78
|
+
|
|
79
|
+
# [2.0.0-beta.1](https://github.com/untemps/react-vocal/compare/v1.7.35...v2.0.0-beta.1) (2026-05-05)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
### Bug Fixes
|
|
83
|
+
|
|
84
|
+
* Address review feedback on migration PR ([0dba863](https://github.com/untemps/react-vocal/commit/0dba86374736f8a2a3763ef20073cdb5bc3a4ac8))
|
|
85
|
+
* Commit Vitest snapshots and remove __snapshots__ from gitignore ([e120fda](https://github.com/untemps/react-vocal/commit/e120fda321c5ed553d02262399ead46653b4e002))
|
|
86
|
+
* Fix comment accuracy and trailing blank line in Vocal ([0ffbbc9](https://github.com/untemps/react-vocal/commit/0ffbbc97dcda8c4c1008b5d9259cbc70753d6551))
|
|
87
|
+
* Restore Icon colors broken by React 19 defaultProps removal ([3d50a38](https://github.com/untemps/react-vocal/commit/3d50a38996e1cc2589ac238e30c1b164b3677252))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
### Features
|
|
91
|
+
|
|
92
|
+
* Migrate to Vite, Vitest and React 19 ([#109](https://github.com/untemps/react-vocal/issues/109)) ([29ecf60](https://github.com/untemps/react-vocal/commit/29ecf607d7e4a0eb2ac802b31e6003a225d7fecc))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### BREAKING CHANGES
|
|
96
|
+
|
|
97
|
+
* Runtime prop validation removed. `Vocal.propTypes` and `Icon.propTypes` have been deleted — React 19 deprecated static propTypes on function components. Consumers relying on development-time prop warnings must migrate to TypeScript or an equivalent static type checker.
|
|
98
|
+
* Node >=20.19.0 required. Vite 8 and jsdom 29 enforce this minimum. Node 18 and 19 are no longer supported for development or CI.
|
|
99
|
+
* `background: none` replaced by `backgroundColor: transparent` on the default button. The shorthand previously reset all background sub-properties (image, gradient…); the longhand only resets the color. Impact is minimal for most users but visible if a custom style prop relied on the implicit background-image reset.
|
|
4
100
|
|
|
5
101
|
## [1.7.35](https://github.com/untemps/react-vocal/compare/v1.7.34...v1.7.35) (2026-05-03)
|
|
6
102
|
|
package/README.md
CHANGED
|
@@ -33,7 +33,12 @@ timeout elapses.
|
|
|
33
33
|
Some browsers supports the `SpeechRecognition` API but not all the related APIs.
|
|
34
34
|
For example, browsers on iOS 14.5, the `SpeechGrammar` and `SpeechGrammarList` and `Permissions` APIs are not supported.
|
|
35
35
|
|
|
36
|
-
Although the lack of `SpeechGrammar` and `SpeechGrammarList` is handled by the
|
|
36
|
+
Although the lack of `SpeechGrammar` and `SpeechGrammarList` is handled by the underlying `@untemps/vocal` library, you need to deal with `Permissions` by yourself.
|
|
37
|
+
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
- React >= 16.13.1
|
|
41
|
+
- Node >= 20.19.0
|
|
37
42
|
|
|
38
43
|
## Installation
|
|
39
44
|
|
|
@@ -41,6 +46,14 @@ Although the lack of `SpeechGrammar` and `SpeechGrammarList` is handled by the u
|
|
|
41
46
|
yarn add @untemps/react-vocal
|
|
42
47
|
```
|
|
43
48
|
|
|
49
|
+
Fuzzy matching for phrase commands requires [fuse.js](https://fusejs.io/) as an optional peer dependency:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
yarn add fuse.js
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Without fuse.js, phrase commands fall back to case-insensitive exact matching. Single-word commands always use exact matching and never require fuse.js.
|
|
56
|
+
|
|
44
57
|
## Usage
|
|
45
58
|
|
|
46
59
|
### `Vocal` component
|
|
@@ -187,35 +200,44 @@ const commands = {
|
|
|
187
200
|
|
|
188
201
|
The component utilizes a special hook called `useCommands` to respond to the commands.
|
|
189
202
|
The hook performs a fuzzy search to match approximate commands if needed. This allows to fix accidental typos or approximate recognition results.
|
|
190
|
-
To do so the hook uses [fuse.js](https://fusejs.io/) which implements an algorithm to find strings that are approximately equal to a given input. The score precision that distinguishes acceptable command-to-callback mapping from negative matching can be customized in the hook
|
|
203
|
+
To do so the hook uses [fuse.js](https://fusejs.io/) which implements an algorithm to find strings that are approximately equal to a given input. The score precision that distinguishes acceptable command-to-callback mapping from negative matching can be customized in the hook instantiation.
|
|
191
204
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
fuse.js is an optional peer dependency — install it separately to enable fuzzy matching (see [Installation](#installation)). Without it, phrase commands fall back to case-insensitive exact matching.
|
|
206
|
+
|
|
207
|
+
**Single-word command keys** (e.g. `rouge`, `submit`) use exact case-insensitive lookup. When the recognition returns a multi-word transcript, each word is tried individually so a command fires even when embedded in a phrase (e.g. _"je veux du rouge"_ triggers `rouge`).
|
|
195
208
|
|
|
196
|
-
|
|
209
|
+
**Phrase command keys** (e.g. `'Change the background color'`) use [fuse.js](https://fusejs.io/) fuzzy matching. The `precision` prop controls the Fuse.js score threshold (default `0.4` — lower is stricter).
|
|
197
210
|
|
|
198
|
-
|
|
211
|
+
**Homophone tolerance** is achieved via `maxAlternatives`: by setting it to 3–5, the speech engine returns several transcription candidates per segment. The correct word (e.g. _vert_) may appear as a secondary alternative when the primary is a homophone (e.g. _verre_), and will still trigger the command.
|
|
212
|
+
|
|
213
|
+
**At most one command fires per utterance.** Alternatives and segments are scanned in order and matching stops at the first hit, so a single recognition event can trigger at most one command callback.
|
|
199
214
|
|
|
200
215
|
---
|
|
201
216
|
|
|
202
217
|
#### `Vocal` component API
|
|
203
218
|
|
|
204
|
-
| Props | Type | Default
|
|
205
|
-
| ------------- | ----------------- |
|
|
206
|
-
| commands
|
|
207
|
-
| lang
|
|
208
|
-
| grammars
|
|
209
|
-
| timeout
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
+
| Props | Type | Default | Description |
|
|
220
|
+
| ------------- | ----------------- | -------------------- | ----------------------------------------------------------------------------------------------- |
|
|
221
|
+
| commands | object | null | Callbacks to be triggered when specified commands are detected by the recognition |
|
|
222
|
+
| lang | string | 'en-US' | Language understood by the recognition [BCP 47 language tag](https://tools.ietf.org/html/bcp47) |
|
|
223
|
+
| grammars | SpeechGrammarList | null | Grammars understood by the recognition [JSpeech Grammar Format](https://www.w3.org/TR/jsgf/) |
|
|
224
|
+
| timeout | number | 3000 | Time in ms to wait before discarding the recognition |
|
|
225
|
+
| precision | number | 0.4 | Fuse.js score threshold for **phrase** command keys only (lower = stricter). Single-word commands always use exact lookup. |
|
|
226
|
+
| maxAlternatives | number | 1 | Maximum number of recognition alternatives per segment. Setting this to 3–5 lets the engine surface the correct word as a secondary transcript, which is useful for handling homophones (e.g. _vert_ / _verre_ in French). |
|
|
227
|
+
| continuous | boolean | false | Keep the recognition session open after each result. The session accumulates transcript across segments and stops when the button is clicked again or `silenceTimeout` expires. Commands are not evaluated in continuous mode. |
|
|
228
|
+
| silenceTimeout | number | null | When `continuous` is true, automatically stop the session after this many ms of inactivity following the last recognized result. `null` or `0` disables auto-stop (button click required). |
|
|
229
|
+
| style | object | null | Styles of the root element if className is not specified |
|
|
230
|
+
| className | string | null | Class of the root element |
|
|
231
|
+
| ariaLabel | string | 'start recognition' | Accessible label for the default button |
|
|
232
|
+
| outlineStyle | string | '2px solid' | Focus outline style applied to the default button |
|
|
233
|
+
| onStart | func | null | Handler called when the recognition starts |
|
|
234
|
+
| onEnd | func | null | Handler called when the recognition ends |
|
|
235
|
+
| onSpeechStart | func | null | Handler called when the speech starts |
|
|
236
|
+
| onSpeechEnd | func | null | Handler called when the speech ends |
|
|
237
|
+
| onResult | func | null | Handler called when a result is recognized |
|
|
238
|
+
| onError | func | null | Handler called when an error occurs |
|
|
239
|
+
| onNoMatch | func | null | Handler called when no result can be recognized |
|
|
240
|
+
| signal | AbortSignal | null | Optional `AbortSignal` propagated to the underlying `start()` call. Aborting it cancels the in-flight start (e.g. while waiting for microphone permission). |
|
|
219
241
|
|
|
220
242
|
### `useVocal` hook
|
|
221
243
|
|
|
@@ -279,41 +301,60 @@ const App = () => {
|
|
|
279
301
|
#### Signature
|
|
280
302
|
|
|
281
303
|
```
|
|
282
|
-
useVocal(lang, grammars)
|
|
304
|
+
useVocal(lang, grammars, maxAlternatives, continuous)
|
|
283
305
|
```
|
|
284
306
|
|
|
285
|
-
| Args
|
|
286
|
-
|
|
|
287
|
-
| lang
|
|
288
|
-
| grammars
|
|
307
|
+
| Args | Type | Default | Description |
|
|
308
|
+
| --------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
309
|
+
| lang | string | 'en-US' | Language understood by the recognition [BCP 47 language tag](https://tools.ietf.org/html/bcp47) |
|
|
310
|
+
| grammars | SpeechGrammarList | null | Grammars understood by the recognition [JSpeech Grammar Format](https://www.w3.org/TR/jsgf/) |
|
|
311
|
+
| maxAlternatives | number | 1 | Maximum number of recognition alternatives per segment |
|
|
312
|
+
| continuous | boolean | false | Keep the recognition session open after each result |
|
|
289
313
|
|
|
290
314
|
---
|
|
291
315
|
|
|
292
316
|
#### Return value
|
|
293
317
|
|
|
294
318
|
```
|
|
295
|
-
const [ref, { start, stop, abort, subscribe, unsubscribe, clean }]
|
|
319
|
+
const [ref, { start, stop, abort, subscribe, unsubscribe, clean, isRecording }]
|
|
296
320
|
```
|
|
297
321
|
|
|
298
322
|
| Args | Type | Description |
|
|
299
323
|
| ----------- | ---- | ---------------------------------------------------- |
|
|
300
|
-
| ref | Ref | React ref to the
|
|
301
|
-
| start | func | Function to start the recognition
|
|
324
|
+
| ref | Ref | React ref to the underlying `@untemps/vocal` instance |
|
|
325
|
+
| start | func | Function to start the recognition. Accepts an optional `{ signal }` argument — an `AbortSignal` propagated to the underlying `start()` call. Returns the underlying `vocal.start()` promise (resolves once the session starts, rejects on microphone/permission errors). |
|
|
302
326
|
| stop | func | Function to stop the recognition |
|
|
303
327
|
| abort | func | Function to abort the recognition |
|
|
304
328
|
| subscribe | func | Function to subscribe to recognition events |
|
|
305
329
|
| unsubscribe | func | Function to unsubscribe to recognition events |
|
|
306
330
|
| clean | func | Function to clean subscription to recognition events |
|
|
331
|
+
| isRecording | bool | Reactive flag mirroring whether a session is active. `true` between `start()` and the next `end`/`error` event. Updated optimistically on `start()` so the UI re-renders at click time. |
|
|
332
|
+
|
|
333
|
+
#### Cancelling a start in flight
|
|
334
|
+
|
|
335
|
+
Both `<Vocal signal={...}>` and `useVocal().start({ signal })` accept an `AbortSignal`. Aborting the controller while the browser is still resolving microphone permission cancels the start cleanly — no `start` event is dispatched.
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
const controller = new AbortController()
|
|
339
|
+
|
|
340
|
+
// Cancel pending recognition after 2s of waiting for permission
|
|
341
|
+
setTimeout(() => controller.abort(), 2000)
|
|
342
|
+
|
|
343
|
+
const [, { start }] = useVocal('en-US')
|
|
344
|
+
start({ signal: controller.signal })
|
|
345
|
+
```
|
|
307
346
|
|
|
308
347
|
### Browser support flag
|
|
309
348
|
|
|
310
349
|
#### Basic usage
|
|
311
350
|
|
|
351
|
+
`isSupported` is a function that returns `true` when the browser supports the Web Speech API (along with the Permissions and MediaDevices APIs that `@untemps/vocal` relies on). It is safe to call during server-side rendering — it returns `false` when `window` is undefined.
|
|
352
|
+
|
|
312
353
|
```javascript
|
|
313
354
|
import Vocal, { isSupported } from '@untemps/react-vocal'
|
|
314
355
|
|
|
315
356
|
const App = () => {
|
|
316
|
-
return isSupported ? <Vocal /> : <p>Your browser does not support Web Speech API</p>
|
|
357
|
+
return isSupported() ? <Vocal /> : <p>Your browser does not support Web Speech API</p>
|
|
317
358
|
}
|
|
318
359
|
```
|
|
319
360
|
|