@untemps/react-vocal 2.0.0-beta.1 → 2.0.0-beta.11
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 +91 -0
- package/README.md +55 -21
- package/dist/index.cjs +7 -0
- package/dist/index.es.js +330 -1914
- package/package.json +25 -9
- package/.github/workflows/publish.yml +0 -32
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -1
- package/.prettierignore +0 -3
- package/.prettierrc +0 -29
- package/CLAUDE.md +0 -59
- 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/commitlint.config.js +0 -7
- package/dev/index.html +0 -24
- package/dev/package.json +0 -18
- package/dev/public/index.html +0 -24
- package/dev/src/index.jsx +0 -45
- package/dev/vite.config.js +0 -10
- package/dev/yarn.lock +0 -201
- package/dist/index.es.js.map +0 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +0 -1
- package/dist/index.umd.js +0 -9
- package/dist/index.umd.js.map +0 -1
- package/src/components/Icon.jsx +0 -24
- package/src/components/Vocal.jsx +0 -168
- package/src/components/__tests__/Icon.test.jsx +0 -38
- package/src/components/__tests__/Vocal.test.jsx +0 -270
- package/src/components/__tests__/VocalWithMockedUseVocal.test.jsx +0 -38
- package/src/components/__tests__/__snapshots__/Icon.test.jsx.snap +0 -21
- package/src/components/__tests__/__snapshots__/Vocal.test.jsx.snap +0 -28
- 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 -21
- package/src/hooks/useVocal.js +0 -56
- package/src/index.js +0 -7
- package/vite.config.js +0 -35
- package/vitest.setup.js +0 -71
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { renderHook } from '@testing-library/react'
|
|
2
|
-
import { Vocal as SpeechRecognitionWrapper } from '@untemps/vocal'
|
|
3
|
-
|
|
4
|
-
import useVocal from '../useVocal'
|
|
5
|
-
|
|
6
|
-
vi.mock('@untemps/vocal')
|
|
7
|
-
|
|
8
|
-
describe('useVocal', () => {
|
|
9
|
-
const mockStart = vi.fn()
|
|
10
|
-
const mockStop = vi.fn()
|
|
11
|
-
const mockAbort = vi.fn()
|
|
12
|
-
const mockAddEventListener = vi.fn()
|
|
13
|
-
const mockRemoveEventListener = vi.fn()
|
|
14
|
-
const mockCleanup = vi.fn()
|
|
15
|
-
|
|
16
|
-
const mockIsSupported = vi.fn()
|
|
17
|
-
Object.defineProperty(SpeechRecognitionWrapper, 'isSupported', {
|
|
18
|
-
get: mockIsSupported,
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
describe('with no SpeechRecognition support', () => {
|
|
22
|
-
beforeAll(() => {
|
|
23
|
-
mockIsSupported.mockReturnValue(false)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('cannot create SpeechRecognition instance', () => {
|
|
27
|
-
const {
|
|
28
|
-
result: {
|
|
29
|
-
current: [ref],
|
|
30
|
-
},
|
|
31
|
-
} = renderHook(() => useVocal())
|
|
32
|
-
expect(ref.current).toBeNull()
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('not triggers start function', () => {
|
|
36
|
-
const {
|
|
37
|
-
result: {
|
|
38
|
-
current: [, { start }],
|
|
39
|
-
},
|
|
40
|
-
} = renderHook(() => useVocal())
|
|
41
|
-
start()
|
|
42
|
-
expect(mockStart).not.toHaveBeenCalled()
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('not triggers stop function', () => {
|
|
46
|
-
const {
|
|
47
|
-
result: {
|
|
48
|
-
current: [, { stop }],
|
|
49
|
-
},
|
|
50
|
-
} = renderHook(() => useVocal())
|
|
51
|
-
stop()
|
|
52
|
-
expect(mockStop).not.toHaveBeenCalled()
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('not triggers abort function', () => {
|
|
56
|
-
const {
|
|
57
|
-
result: {
|
|
58
|
-
current: [, { abort }],
|
|
59
|
-
},
|
|
60
|
-
} = renderHook(() => useVocal())
|
|
61
|
-
abort()
|
|
62
|
-
expect(mockAbort).not.toHaveBeenCalled()
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('not triggers clean function', () => {
|
|
66
|
-
const {
|
|
67
|
-
result: {
|
|
68
|
-
current: [, { clean }],
|
|
69
|
-
},
|
|
70
|
-
} = renderHook(() => useVocal())
|
|
71
|
-
clean()
|
|
72
|
-
expect(mockCleanup).not.toHaveBeenCalled()
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('not triggers subscribe function', () => {
|
|
76
|
-
const {
|
|
77
|
-
result: {
|
|
78
|
-
current: [, { subscribe }],
|
|
79
|
-
},
|
|
80
|
-
} = renderHook(() => useVocal())
|
|
81
|
-
subscribe('foo', vi.fn())
|
|
82
|
-
expect(mockAddEventListener).not.toHaveBeenCalled()
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('not triggers unsubscribe function', () => {
|
|
86
|
-
const {
|
|
87
|
-
result: {
|
|
88
|
-
current: [, { unsubscribe }],
|
|
89
|
-
},
|
|
90
|
-
} = renderHook(() => useVocal())
|
|
91
|
-
unsubscribe('foo', vi.fn())
|
|
92
|
-
expect(mockRemoveEventListener).not.toHaveBeenCalled()
|
|
93
|
-
})
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
describe('with SpeechRecognition support', () => {
|
|
97
|
-
beforeAll(() => {
|
|
98
|
-
mockIsSupported.mockReturnValue(true)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
beforeEach(() => {
|
|
102
|
-
SpeechRecognitionWrapper.mockImplementation(function () {
|
|
103
|
-
return {
|
|
104
|
-
start: mockStart,
|
|
105
|
-
stop: mockStop,
|
|
106
|
-
abort: mockAbort,
|
|
107
|
-
addEventListener: mockAddEventListener,
|
|
108
|
-
removeEventListener: mockRemoveEventListener,
|
|
109
|
-
cleanup: mockCleanup,
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
afterEach(() => {
|
|
115
|
-
SpeechRecognitionWrapper.mockReset()
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('creates SpeechRecognition instance', () => {
|
|
119
|
-
const {
|
|
120
|
-
result: {
|
|
121
|
-
current: [ref],
|
|
122
|
-
},
|
|
123
|
-
} = renderHook(() => useVocal())
|
|
124
|
-
expect(ref.current).toBeDefined()
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
it('uses custom SpeechRecognition instance', () => {
|
|
128
|
-
const foo = new SpeechRecognitionWrapper()
|
|
129
|
-
const {
|
|
130
|
-
result: {
|
|
131
|
-
current: [ref],
|
|
132
|
-
},
|
|
133
|
-
} = renderHook(() => useVocal(null, null, foo))
|
|
134
|
-
expect(ref.current).toBe(foo)
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
it('triggers start function', () => {
|
|
138
|
-
const {
|
|
139
|
-
result: {
|
|
140
|
-
current: [, { start }],
|
|
141
|
-
},
|
|
142
|
-
} = renderHook(() => useVocal())
|
|
143
|
-
start()
|
|
144
|
-
expect(mockStart).toHaveBeenCalled()
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('triggers stop function', () => {
|
|
148
|
-
const {
|
|
149
|
-
result: {
|
|
150
|
-
current: [, { stop }],
|
|
151
|
-
},
|
|
152
|
-
} = renderHook(() => useVocal())
|
|
153
|
-
stop()
|
|
154
|
-
expect(mockStop).toHaveBeenCalled()
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('triggers abort function', () => {
|
|
158
|
-
const {
|
|
159
|
-
result: {
|
|
160
|
-
current: [, { abort }],
|
|
161
|
-
},
|
|
162
|
-
} = renderHook(() => useVocal())
|
|
163
|
-
abort()
|
|
164
|
-
expect(mockAbort).toHaveBeenCalled()
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('triggers clean function', () => {
|
|
168
|
-
const {
|
|
169
|
-
result: {
|
|
170
|
-
current: [, { clean }],
|
|
171
|
-
},
|
|
172
|
-
} = renderHook(() => useVocal())
|
|
173
|
-
clean()
|
|
174
|
-
expect(mockCleanup).toHaveBeenCalled()
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('triggers subscribe function', () => {
|
|
178
|
-
const {
|
|
179
|
-
result: {
|
|
180
|
-
current: [, { subscribe }],
|
|
181
|
-
},
|
|
182
|
-
} = renderHook(() => useVocal())
|
|
183
|
-
subscribe('foo', vi.fn())
|
|
184
|
-
expect(mockAddEventListener).toHaveBeenCalled()
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('triggers unsubscribe function', () => {
|
|
188
|
-
const {
|
|
189
|
-
result: {
|
|
190
|
-
current: [, { unsubscribe }],
|
|
191
|
-
},
|
|
192
|
-
} = renderHook(() => useVocal())
|
|
193
|
-
unsubscribe('foo', vi.fn())
|
|
194
|
-
expect(mockRemoveEventListener).toHaveBeenCalled()
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
})
|
package/src/hooks/useCommands.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import Fuse from 'fuse.js'
|
|
2
|
-
|
|
3
|
-
const useCommands = (commands, precision = 0.4) => {
|
|
4
|
-
commands = !!commands
|
|
5
|
-
? Object.entries(commands)?.reduce((acc, [key, value]) => ({ [key.toLowerCase()]: value }), {})
|
|
6
|
-
: {}
|
|
7
|
-
|
|
8
|
-
const triggerCommand = (input) => {
|
|
9
|
-
const fuse = new Fuse(Object.keys(commands), { includeScore: true, ignoreLocation: true })
|
|
10
|
-
const result = fuse.search(input).filter((r) => r.score < precision)
|
|
11
|
-
if (!!result?.length) {
|
|
12
|
-
const key = result[0].item.toLowerCase()
|
|
13
|
-
return commands[key]?.(input)
|
|
14
|
-
}
|
|
15
|
-
return null
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return triggerCommand
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default useCommands
|
package/src/hooks/useTimeout.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react'
|
|
2
|
-
|
|
3
|
-
const useTimeout = (handler, timeout = 0) => {
|
|
4
|
-
const ref = useRef(-1)
|
|
5
|
-
|
|
6
|
-
const stop = useCallback(() => {
|
|
7
|
-
clearTimeout(ref.current)
|
|
8
|
-
ref.current = -1
|
|
9
|
-
}, [])
|
|
10
|
-
|
|
11
|
-
const start = useCallback(() => {
|
|
12
|
-
stop()
|
|
13
|
-
ref.current = setTimeout(handler, timeout)
|
|
14
|
-
}, [handler, timeout, stop])
|
|
15
|
-
|
|
16
|
-
useEffect(() => stop, [stop])
|
|
17
|
-
|
|
18
|
-
return [start, stop]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default useTimeout
|
package/src/hooks/useVocal.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react'
|
|
2
|
-
import { Vocal as SpeechRecognitionWrapper } from '@untemps/vocal'
|
|
3
|
-
|
|
4
|
-
const useVocal = (lang = 'en-US', grammars = null, __rsInstance = null) => {
|
|
5
|
-
const ref = useRef(null)
|
|
6
|
-
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (SpeechRecognitionWrapper.isSupported) {
|
|
9
|
-
ref.current = __rsInstance || new SpeechRecognitionWrapper({ lang, grammars })
|
|
10
|
-
return () => {
|
|
11
|
-
ref.current.abort()
|
|
12
|
-
ref.current.cleanup()
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}, [lang, grammars, __rsInstance])
|
|
16
|
-
|
|
17
|
-
const start = useCallback(() => {
|
|
18
|
-
if (ref.current) {
|
|
19
|
-
ref.current.start()
|
|
20
|
-
}
|
|
21
|
-
}, [])
|
|
22
|
-
|
|
23
|
-
const stop = useCallback(() => {
|
|
24
|
-
if (ref.current) {
|
|
25
|
-
ref.current.stop()
|
|
26
|
-
}
|
|
27
|
-
}, [])
|
|
28
|
-
|
|
29
|
-
const abort = useCallback(() => {
|
|
30
|
-
if (ref.current) {
|
|
31
|
-
ref.current.abort()
|
|
32
|
-
}
|
|
33
|
-
}, [])
|
|
34
|
-
|
|
35
|
-
const subscribe = useCallback((eventType, handler) => {
|
|
36
|
-
if (ref.current) {
|
|
37
|
-
ref.current.addEventListener(eventType, handler)
|
|
38
|
-
}
|
|
39
|
-
}, [])
|
|
40
|
-
|
|
41
|
-
const unsubscribe = useCallback((eventType, handler) => {
|
|
42
|
-
if (ref.current) {
|
|
43
|
-
ref.current.removeEventListener(eventType, handler)
|
|
44
|
-
}
|
|
45
|
-
}, [])
|
|
46
|
-
|
|
47
|
-
const clean = useCallback(() => {
|
|
48
|
-
if (ref.current) {
|
|
49
|
-
ref.current.cleanup()
|
|
50
|
-
}
|
|
51
|
-
}, [])
|
|
52
|
-
|
|
53
|
-
return [ref, { start, stop, abort, subscribe, unsubscribe, clean }]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default useVocal
|
package/src/index.js
DELETED
package/vite.config.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vite'
|
|
2
|
-
import react from '@vitejs/plugin-react'
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
plugins: [react()],
|
|
6
|
-
build: {
|
|
7
|
-
lib: {
|
|
8
|
-
entry: 'src/index.js',
|
|
9
|
-
name: 'ReactVocal',
|
|
10
|
-
formats: ['es', 'cjs', 'umd'],
|
|
11
|
-
fileName: (format) => ({ es: 'index.es.js', umd: 'index.umd.js', cjs: 'index.js' })[format],
|
|
12
|
-
},
|
|
13
|
-
rollupOptions: {
|
|
14
|
-
external: ['react', 'react-dom'],
|
|
15
|
-
output: {
|
|
16
|
-
globals: {
|
|
17
|
-
react: 'React',
|
|
18
|
-
'react-dom': 'ReactDOM',
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
sourcemap: true,
|
|
23
|
-
},
|
|
24
|
-
test: {
|
|
25
|
-
globals: true,
|
|
26
|
-
environment: 'jsdom',
|
|
27
|
-
setupFiles: ['./vitest.setup.js'],
|
|
28
|
-
restoreMocks: true,
|
|
29
|
-
coverage: {
|
|
30
|
-
provider: 'v8',
|
|
31
|
-
reporter: ['text', 'lcov'],
|
|
32
|
-
reportsDirectory: './coverage',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
})
|
package/vitest.setup.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest'
|
|
2
|
-
import '@testing-library/jest-dom/vitest'
|
|
3
|
-
|
|
4
|
-
Object.defineProperty(global, 'navigator', {
|
|
5
|
-
value: { userAgent: 'node.js' },
|
|
6
|
-
writable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
})
|
|
9
|
-
global.PermissionStatus = vi.fn(function () {
|
|
10
|
-
return {
|
|
11
|
-
state: 'granted',
|
|
12
|
-
addEventListener: vi.fn(),
|
|
13
|
-
}
|
|
14
|
-
})
|
|
15
|
-
global.Permissions = vi.fn(function () {
|
|
16
|
-
return {
|
|
17
|
-
query: vi.fn().mockResolvedValue(new PermissionStatus()),
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
Object.defineProperty(global.navigator, 'permissions', {
|
|
21
|
-
value: new Permissions(),
|
|
22
|
-
writable: true,
|
|
23
|
-
configurable: true,
|
|
24
|
-
})
|
|
25
|
-
global.MediaDevices = vi.fn(function () {
|
|
26
|
-
return {
|
|
27
|
-
getUserMedia: vi.fn().mockResolvedValue('foo'),
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
Object.defineProperty(global.navigator, 'mediaDevices', {
|
|
31
|
-
value: new MediaDevices(),
|
|
32
|
-
writable: true,
|
|
33
|
-
configurable: true,
|
|
34
|
-
})
|
|
35
|
-
global.SpeechGrammarList = vi.fn(function () {
|
|
36
|
-
return {
|
|
37
|
-
length: 0,
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
global.SpeechRecognition = vi.fn(function () {
|
|
41
|
-
const handlers = {}
|
|
42
|
-
return {
|
|
43
|
-
addEventListener: vi.fn(function (type, callback) {
|
|
44
|
-
handlers[type] = callback
|
|
45
|
-
}),
|
|
46
|
-
removeEventListener: vi.fn(),
|
|
47
|
-
dispatchEvent: vi.fn(),
|
|
48
|
-
start: vi.fn(function () {
|
|
49
|
-
handlers.start?.()
|
|
50
|
-
}),
|
|
51
|
-
stop: vi.fn(function () {
|
|
52
|
-
handlers.end?.()
|
|
53
|
-
}),
|
|
54
|
-
abort: vi.fn(function () {
|
|
55
|
-
handlers.end?.()
|
|
56
|
-
}),
|
|
57
|
-
say: vi.fn(function (sentence) {
|
|
58
|
-
handlers.speechstart?.()
|
|
59
|
-
|
|
60
|
-
const resultEvent = new Event('result')
|
|
61
|
-
resultEvent.resultIndex = 0
|
|
62
|
-
resultEvent.results = [[{ transcript: sentence }]]
|
|
63
|
-
handlers.speechend?.()
|
|
64
|
-
if (sentence) {
|
|
65
|
-
handlers.result?.(resultEvent)
|
|
66
|
-
} else {
|
|
67
|
-
handlers.nomatch?.()
|
|
68
|
-
}
|
|
69
|
-
}),
|
|
70
|
-
}
|
|
71
|
-
})
|