@speechos/react 1.0.0 → 1.0.2
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/dist/components/SpeechOSWidget.d.cts +4 -7
- package/dist/components/SpeechOSWidget.d.ts +4 -7
- package/dist/context.d.cts +30 -9
- package/dist/context.d.ts +30 -9
- package/dist/hooks/index.d.cts +2 -1
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/useCommand.d.cts +73 -0
- package/dist/hooks/useCommand.d.ts +73 -0
- package/dist/hooks/useSpeechOSWidget.d.cts +74 -0
- package/dist/hooks/useSpeechOSWidget.d.ts +74 -0
- package/dist/index.cjs +139 -87
- package/dist/index.d.cts +5 -20
- package/dist/index.d.ts +5 -20
- package/dist/index.js +132 -81
- package/package.json +8 -7
- package/dist/hooks/useTranscription.d.cts +0 -87
- package/dist/hooks/useTranscription.d.ts +0 -87
package/dist/index.cjs
CHANGED
|
@@ -56,13 +56,9 @@ function SpeechOSProvider({ config, children }) {
|
|
|
56
56
|
stopDictation: () => __speechos_core.speechOS.stopDictation(),
|
|
57
57
|
edit: (text) => __speechos_core.speechOS.edit(text),
|
|
58
58
|
stopEdit: () => __speechos_core.speechOS.stopEdit(),
|
|
59
|
+
command: (commands) => __speechos_core.speechOS.command(commands),
|
|
60
|
+
stopCommand: () => __speechos_core.speechOS.stopCommand(),
|
|
59
61
|
cancel: () => __speechos_core.speechOS.cancel(),
|
|
60
|
-
connect: () => __speechos_core.speechOS.connect(),
|
|
61
|
-
disconnect: () => __speechos_core.speechOS.disconnect(),
|
|
62
|
-
enableMicrophone: () => __speechos_core.speechOS.enableMicrophone(),
|
|
63
|
-
waitUntilReady: () => __speechos_core.speechOS.waitUntilReady(),
|
|
64
|
-
stopAndGetTranscript: () => __speechos_core.speechOS.stopAndGetTranscript(),
|
|
65
|
-
stopAndEdit: (originalText) => __speechos_core.speechOS.stopAndEdit(originalText),
|
|
66
62
|
on: (event, callback) => __speechos_core.events.on(event, callback),
|
|
67
63
|
off: (event, callback) => {
|
|
68
64
|
console.warn("SpeechOS: Use the unsubscribe function returned by on() instead of off()");
|
|
@@ -212,11 +208,11 @@ function useSpeechOSEvents(event, callback) {
|
|
|
212
208
|
* @returns Dictation controls and state
|
|
213
209
|
*/
|
|
214
210
|
function useDictation() {
|
|
215
|
-
const { state: state$
|
|
211
|
+
const { state: state$2, dictate, stopDictation, cancel } = useSpeechOSContext();
|
|
216
212
|
const [transcript, setTranscript] = (0, react.useState)(null);
|
|
217
213
|
const [error, setError] = (0, react.useState)(null);
|
|
218
|
-
const isRecording = state$
|
|
219
|
-
const isProcessing = state$
|
|
214
|
+
const isRecording = state$2.recordingState === "recording";
|
|
215
|
+
const isProcessing = state$2.recordingState === "processing";
|
|
220
216
|
const start = (0, react.useCallback)(async () => {
|
|
221
217
|
setError(null);
|
|
222
218
|
try {
|
|
@@ -292,12 +288,12 @@ function useDictation() {
|
|
|
292
288
|
* @returns Edit controls and state
|
|
293
289
|
*/
|
|
294
290
|
function useEdit() {
|
|
295
|
-
const { state: state$
|
|
291
|
+
const { state: state$2, edit, stopEdit, cancel } = useSpeechOSContext();
|
|
296
292
|
const [originalText, setOriginalText] = (0, react.useState)(null);
|
|
297
293
|
const [result, setResult] = (0, react.useState)(null);
|
|
298
294
|
const [error, setError] = (0, react.useState)(null);
|
|
299
|
-
const isEditing = state$
|
|
300
|
-
const isProcessing = state$
|
|
295
|
+
const isEditing = state$2.recordingState === "recording" && state$2.activeAction === "edit";
|
|
296
|
+
const isProcessing = state$2.recordingState === "processing";
|
|
301
297
|
const start = (0, react.useCallback)(async (textToEdit) => {
|
|
302
298
|
setError(null);
|
|
303
299
|
setOriginalText(textToEdit);
|
|
@@ -338,92 +334,152 @@ function useEdit() {
|
|
|
338
334
|
}
|
|
339
335
|
|
|
340
336
|
//#endregion
|
|
341
|
-
//#region src/hooks/
|
|
337
|
+
//#region src/hooks/useCommand.ts
|
|
342
338
|
/**
|
|
343
|
-
*
|
|
339
|
+
* Simplified hook for voice command workflows
|
|
344
340
|
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
* or useEdit() which provide simpler interfaces.
|
|
341
|
+
* Provides an easy-to-use interface for voice command matching
|
|
342
|
+
* with automatic state management.
|
|
348
343
|
*
|
|
349
344
|
* @example
|
|
350
345
|
* ```tsx
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
* await
|
|
346
|
+
* const commands = [
|
|
347
|
+
* { name: 'scroll_down', description: 'Scroll the page down' },
|
|
348
|
+
* { name: 'open_settings', description: 'Open the settings modal' },
|
|
349
|
+
* {
|
|
350
|
+
* name: 'search',
|
|
351
|
+
* description: 'Search for something',
|
|
352
|
+
* arguments: [{ name: 'query', description: 'The search query' }]
|
|
353
|
+
* },
|
|
354
|
+
* ];
|
|
355
|
+
*
|
|
356
|
+
* function VoiceCommands() {
|
|
357
|
+
* const { start, stop, isListening, isProcessing, result, error } = useCommand();
|
|
358
|
+
*
|
|
359
|
+
* const handleCommand = async () => {
|
|
360
|
+
* await start(commands);
|
|
361
|
+
* };
|
|
366
362
|
*
|
|
367
|
-
*
|
|
368
|
-
* await
|
|
363
|
+
* const handleStop = async () => {
|
|
364
|
+
* const matched = await stop();
|
|
365
|
+
* if (matched) {
|
|
366
|
+
* console.log('Matched command:', matched.name, matched.arguments);
|
|
367
|
+
* }
|
|
368
|
+
* };
|
|
369
369
|
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
370
|
+
* return (
|
|
371
|
+
* <div>
|
|
372
|
+
* <button onClick={isListening ? handleStop : handleCommand} disabled={isProcessing}>
|
|
373
|
+
* {isListening ? 'Execute' : 'Say Command'}
|
|
374
|
+
* </button>
|
|
375
|
+
* {isProcessing && <span>Processing...</span>}
|
|
376
|
+
* {result && <p>Command: {result.name}</p>}
|
|
377
|
+
* {error && <p style={{ color: 'red' }}>{error}</p>}
|
|
378
|
+
* </div>
|
|
379
|
+
* );
|
|
380
|
+
* }
|
|
381
|
+
* ```
|
|
372
382
|
*
|
|
373
|
-
*
|
|
383
|
+
* @returns Command controls and state
|
|
384
|
+
*/
|
|
385
|
+
function useCommand() {
|
|
386
|
+
const { state: state$2, command, stopCommand } = useSpeechOSContext();
|
|
387
|
+
const [result, setResult] = (0, react.useState)(null);
|
|
388
|
+
const [error, setError] = (0, react.useState)(null);
|
|
389
|
+
const isListening = state$2.recordingState === "recording" && state$2.activeAction === "command";
|
|
390
|
+
const isProcessing = state$2.recordingState === "processing";
|
|
391
|
+
const start = (0, react.useCallback)(async (commands) => {
|
|
392
|
+
setError(null);
|
|
393
|
+
try {
|
|
394
|
+
await command(commands);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
const message = err instanceof Error ? err.message : "Failed to start command";
|
|
397
|
+
setError(message);
|
|
398
|
+
}
|
|
399
|
+
}, [command]);
|
|
400
|
+
const stop = (0, react.useCallback)(async () => {
|
|
401
|
+
try {
|
|
402
|
+
const commandResult = await stopCommand();
|
|
403
|
+
setResult(commandResult);
|
|
404
|
+
setError(null);
|
|
405
|
+
return commandResult;
|
|
406
|
+
} catch (err) {
|
|
407
|
+
const message = err instanceof Error ? err.message : "Failed to process command";
|
|
408
|
+
setError(message);
|
|
409
|
+
throw err;
|
|
410
|
+
}
|
|
411
|
+
}, [stopCommand]);
|
|
412
|
+
const clear = (0, react.useCallback)(() => {
|
|
413
|
+
setResult(null);
|
|
414
|
+
setError(null);
|
|
415
|
+
}, []);
|
|
416
|
+
return {
|
|
417
|
+
start,
|
|
418
|
+
stop,
|
|
419
|
+
isListening,
|
|
420
|
+
isProcessing,
|
|
421
|
+
result,
|
|
422
|
+
error,
|
|
423
|
+
clear
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/hooks/useSpeechOSWidget.ts
|
|
429
|
+
/**
|
|
430
|
+
* Hook for programmatic widget control
|
|
374
431
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* console.log('Transcribed:', text);
|
|
432
|
+
* Use this hook when you want to control the widget manually instead of
|
|
433
|
+
* relying on automatic form field detection.
|
|
378
434
|
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
435
|
+
* @example
|
|
436
|
+
* ```tsx
|
|
437
|
+
* function MyComponent() {
|
|
438
|
+
* const { showFor, hide, isVisible } = useSpeechOSWidget();
|
|
439
|
+
* const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
382
440
|
*
|
|
383
441
|
* return (
|
|
384
442
|
* <div>
|
|
385
|
-
* <
|
|
386
|
-
* <
|
|
387
|
-
*
|
|
388
|
-
*
|
|
443
|
+
* <textarea ref={textareaRef} />
|
|
444
|
+
* <button onClick={() => showFor(textareaRef.current!)}>
|
|
445
|
+
* Enable Voice Input
|
|
446
|
+
* </button>
|
|
447
|
+
* {isVisible && (
|
|
448
|
+
* <button onClick={hide}>Hide Widget</button>
|
|
449
|
+
* )}
|
|
389
450
|
* </div>
|
|
390
451
|
* );
|
|
391
452
|
* }
|
|
392
453
|
* ```
|
|
393
|
-
*
|
|
394
|
-
* @returns Low-level transcription controls and state
|
|
395
454
|
*/
|
|
396
|
-
function
|
|
397
|
-
const
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}, [contextDisconnect]);
|
|
455
|
+
function useSpeechOSWidget() {
|
|
456
|
+
const speechOSState = useSpeechOSState();
|
|
457
|
+
const showFor = (0, react.useCallback)((element) => {
|
|
458
|
+
__speechos_core.state.setFocusedElement(element);
|
|
459
|
+
__speechos_core.state.show();
|
|
460
|
+
}, []);
|
|
461
|
+
const attachTo = (0, react.useCallback)((element) => {
|
|
462
|
+
__speechos_core.state.setFocusedElement(element);
|
|
463
|
+
__speechos_core.state.show();
|
|
464
|
+
}, []);
|
|
465
|
+
const detach = (0, react.useCallback)(() => {
|
|
466
|
+
__speechos_core.state.setFocusedElement(null);
|
|
467
|
+
}, []);
|
|
468
|
+
const show = (0, react.useCallback)(() => {
|
|
469
|
+
__speechos_core.state.show();
|
|
470
|
+
}, []);
|
|
471
|
+
const hide = (0, react.useCallback)(() => {
|
|
472
|
+
__speechos_core.state.hide();
|
|
473
|
+
}, []);
|
|
416
474
|
return {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
isMicEnabled: state$1.isMicEnabled,
|
|
426
|
-
recordingState: state$1.recordingState
|
|
475
|
+
showFor,
|
|
476
|
+
attachTo,
|
|
477
|
+
detach,
|
|
478
|
+
show,
|
|
479
|
+
hide,
|
|
480
|
+
isVisible: speechOSState.isVisible,
|
|
481
|
+
isExpanded: speechOSState.isExpanded,
|
|
482
|
+
focusedElement: speechOSState.focusedElement
|
|
427
483
|
};
|
|
428
484
|
}
|
|
429
485
|
|
|
@@ -457,7 +513,7 @@ function useTranscription() {
|
|
|
457
513
|
* }
|
|
458
514
|
* ```
|
|
459
515
|
*/
|
|
460
|
-
function SpeechOSWidget({ apiKey, userId,
|
|
516
|
+
function SpeechOSWidget({ apiKey, userId, host, zIndex, debug, onTranscription, onEdit, onError, onShow, onHide, className }) {
|
|
461
517
|
const containerRef = (0, react.useRef)(null);
|
|
462
518
|
const widgetRef = (0, react.useRef)(null);
|
|
463
519
|
const { init, isInitialized } = useSpeechOSContext();
|
|
@@ -466,8 +522,6 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
466
522
|
apiKey,
|
|
467
523
|
userId,
|
|
468
524
|
host,
|
|
469
|
-
position,
|
|
470
|
-
zIndex,
|
|
471
525
|
debug
|
|
472
526
|
});
|
|
473
527
|
}, [
|
|
@@ -476,8 +530,6 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
476
530
|
apiKey,
|
|
477
531
|
userId,
|
|
478
532
|
host,
|
|
479
|
-
position,
|
|
480
|
-
zIndex,
|
|
481
533
|
debug
|
|
482
534
|
]);
|
|
483
535
|
(0, react.useEffect)(() => {
|
|
@@ -515,7 +567,6 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
515
567
|
return;
|
|
516
568
|
}
|
|
517
569
|
const widget = document.createElement("speechos-widget");
|
|
518
|
-
widget.setAttribute("position", position);
|
|
519
570
|
containerRef.current.appendChild(widget);
|
|
520
571
|
widgetRef.current = widget;
|
|
521
572
|
return () => {
|
|
@@ -524,7 +575,7 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
524
575
|
widgetRef.current = null;
|
|
525
576
|
}
|
|
526
577
|
};
|
|
527
|
-
}, [
|
|
578
|
+
}, []);
|
|
528
579
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
529
580
|
ref: containerRef,
|
|
530
581
|
className,
|
|
@@ -541,10 +592,11 @@ exports.SpeechOSContext = SpeechOSContext;
|
|
|
541
592
|
exports.SpeechOSProvider = SpeechOSProvider;
|
|
542
593
|
exports.SpeechOSWidget = SpeechOSWidget;
|
|
543
594
|
exports.VERSION = VERSION;
|
|
595
|
+
exports.useCommand = useCommand;
|
|
544
596
|
exports.useDictation = useDictation;
|
|
545
597
|
exports.useEdit = useEdit;
|
|
546
598
|
exports.useSpeechOS = useSpeechOS;
|
|
547
599
|
exports.useSpeechOSContext = useSpeechOSContext;
|
|
548
600
|
exports.useSpeechOSEvents = useSpeechOSEvents;
|
|
549
601
|
exports.useSpeechOSState = useSpeechOSState;
|
|
550
|
-
exports.
|
|
602
|
+
exports.useSpeechOSWidget = useSpeechOSWidget;
|
package/dist/index.d.cts
CHANGED
|
@@ -35,25 +35,10 @@
|
|
|
35
35
|
* }
|
|
36
36
|
* ```
|
|
37
37
|
*
|
|
38
|
-
* @example Low-level usage
|
|
39
|
-
* ```tsx
|
|
40
|
-
* import { useSpeechOS } from '@speechos/react';
|
|
41
|
-
*
|
|
42
|
-
* function CustomUI() {
|
|
43
|
-
* const { connect, waitUntilReady, enableMicrophone, stopAndGetTranscript } = useSpeechOS();
|
|
44
|
-
*
|
|
45
|
-
* const handleRecord = async () => {
|
|
46
|
-
* await connect();
|
|
47
|
-
* await waitUntilReady();
|
|
48
|
-
* await enableMicrophone();
|
|
49
|
-
* // ... recording ...
|
|
50
|
-
* const text = await stopAndGetTranscript();
|
|
51
|
-
* };
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
38
|
*/
|
|
55
|
-
export { SpeechOSProvider, SpeechOSContext, useSpeechOSContext, type SpeechOSContextValue, type SpeechOSProviderProps, } from "./context.js";
|
|
56
|
-
export { useSpeechOS, useSpeechOSState, useSpeechOSEvents, useDictation, useEdit,
|
|
57
|
-
export { SpeechOSWidget, type SpeechOSWidgetProps } from "./components/index.js";
|
|
58
|
-
export type {
|
|
39
|
+
export { SpeechOSProvider, SpeechOSContext, useSpeechOSContext, type SpeechOSContextValue, type SpeechOSProviderProps, type SpeechOSReactConfig, } from "./context.js";
|
|
40
|
+
export { useSpeechOS, useSpeechOSState, useSpeechOSEvents, useDictation, useEdit, useCommand, useSpeechOSWidget, type UseDictationResult, type UseEditResult, type UseCommandResult, type UseSpeechOSWidgetResult, } from "./hooks/index.js";
|
|
41
|
+
export { SpeechOSWidget, type SpeechOSWidgetProps, } from "./components/index.js";
|
|
42
|
+
export type { SpeechOSCoreConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, RecordingState, UnsubscribeFn, CommandArgument, CommandDefinition, CommandResult, } from "@speechos/core";
|
|
43
|
+
export type { SpeechOSCoreConfig as SpeechOSConfig } from "@speechos/core";
|
|
59
44
|
export declare const VERSION = "0.1.0";
|
package/dist/index.d.ts
CHANGED
|
@@ -35,25 +35,10 @@
|
|
|
35
35
|
* }
|
|
36
36
|
* ```
|
|
37
37
|
*
|
|
38
|
-
* @example Low-level usage
|
|
39
|
-
* ```tsx
|
|
40
|
-
* import { useSpeechOS } from '@speechos/react';
|
|
41
|
-
*
|
|
42
|
-
* function CustomUI() {
|
|
43
|
-
* const { connect, waitUntilReady, enableMicrophone, stopAndGetTranscript } = useSpeechOS();
|
|
44
|
-
*
|
|
45
|
-
* const handleRecord = async () => {
|
|
46
|
-
* await connect();
|
|
47
|
-
* await waitUntilReady();
|
|
48
|
-
* await enableMicrophone();
|
|
49
|
-
* // ... recording ...
|
|
50
|
-
* const text = await stopAndGetTranscript();
|
|
51
|
-
* };
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
38
|
*/
|
|
55
|
-
export { SpeechOSProvider, SpeechOSContext, useSpeechOSContext, type SpeechOSContextValue, type SpeechOSProviderProps, } from "./context.js";
|
|
56
|
-
export { useSpeechOS, useSpeechOSState, useSpeechOSEvents, useDictation, useEdit,
|
|
57
|
-
export { SpeechOSWidget, type SpeechOSWidgetProps } from "./components/index.js";
|
|
58
|
-
export type {
|
|
39
|
+
export { SpeechOSProvider, SpeechOSContext, useSpeechOSContext, type SpeechOSContextValue, type SpeechOSProviderProps, type SpeechOSReactConfig, } from "./context.js";
|
|
40
|
+
export { useSpeechOS, useSpeechOSState, useSpeechOSEvents, useDictation, useEdit, useCommand, useSpeechOSWidget, type UseDictationResult, type UseEditResult, type UseCommandResult, type UseSpeechOSWidgetResult, } from "./hooks/index.js";
|
|
41
|
+
export { SpeechOSWidget, type SpeechOSWidgetProps, } from "./components/index.js";
|
|
42
|
+
export type { SpeechOSCoreConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, RecordingState, UnsubscribeFn, CommandArgument, CommandDefinition, CommandResult, } from "@speechos/core";
|
|
43
|
+
export type { SpeechOSCoreConfig as SpeechOSConfig } from "@speechos/core";
|
|
59
44
|
export declare const VERSION = "0.1.0";
|
package/dist/index.js
CHANGED
|
@@ -33,13 +33,9 @@ function SpeechOSProvider({ config, children }) {
|
|
|
33
33
|
stopDictation: () => speechOS.stopDictation(),
|
|
34
34
|
edit: (text) => speechOS.edit(text),
|
|
35
35
|
stopEdit: () => speechOS.stopEdit(),
|
|
36
|
+
command: (commands) => speechOS.command(commands),
|
|
37
|
+
stopCommand: () => speechOS.stopCommand(),
|
|
36
38
|
cancel: () => speechOS.cancel(),
|
|
37
|
-
connect: () => speechOS.connect(),
|
|
38
|
-
disconnect: () => speechOS.disconnect(),
|
|
39
|
-
enableMicrophone: () => speechOS.enableMicrophone(),
|
|
40
|
-
waitUntilReady: () => speechOS.waitUntilReady(),
|
|
41
|
-
stopAndGetTranscript: () => speechOS.stopAndGetTranscript(),
|
|
42
|
-
stopAndEdit: (originalText) => speechOS.stopAndEdit(originalText),
|
|
43
39
|
on: (event, callback) => events.on(event, callback),
|
|
44
40
|
off: (event, callback) => {
|
|
45
41
|
console.warn("SpeechOS: Use the unsubscribe function returned by on() instead of off()");
|
|
@@ -315,92 +311,152 @@ function useEdit() {
|
|
|
315
311
|
}
|
|
316
312
|
|
|
317
313
|
//#endregion
|
|
318
|
-
//#region src/hooks/
|
|
314
|
+
//#region src/hooks/useCommand.ts
|
|
319
315
|
/**
|
|
320
|
-
*
|
|
316
|
+
* Simplified hook for voice command workflows
|
|
321
317
|
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
* or useEdit() which provide simpler interfaces.
|
|
318
|
+
* Provides an easy-to-use interface for voice command matching
|
|
319
|
+
* with automatic state management.
|
|
325
320
|
*
|
|
326
321
|
* @example
|
|
327
322
|
* ```tsx
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
* await
|
|
323
|
+
* const commands = [
|
|
324
|
+
* { name: 'scroll_down', description: 'Scroll the page down' },
|
|
325
|
+
* { name: 'open_settings', description: 'Open the settings modal' },
|
|
326
|
+
* {
|
|
327
|
+
* name: 'search',
|
|
328
|
+
* description: 'Search for something',
|
|
329
|
+
* arguments: [{ name: 'query', description: 'The search query' }]
|
|
330
|
+
* },
|
|
331
|
+
* ];
|
|
332
|
+
*
|
|
333
|
+
* function VoiceCommands() {
|
|
334
|
+
* const { start, stop, isListening, isProcessing, result, error } = useCommand();
|
|
335
|
+
*
|
|
336
|
+
* const handleCommand = async () => {
|
|
337
|
+
* await start(commands);
|
|
338
|
+
* };
|
|
343
339
|
*
|
|
344
|
-
*
|
|
345
|
-
* await
|
|
340
|
+
* const handleStop = async () => {
|
|
341
|
+
* const matched = await stop();
|
|
342
|
+
* if (matched) {
|
|
343
|
+
* console.log('Matched command:', matched.name, matched.arguments);
|
|
344
|
+
* }
|
|
345
|
+
* };
|
|
346
346
|
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
347
|
+
* return (
|
|
348
|
+
* <div>
|
|
349
|
+
* <button onClick={isListening ? handleStop : handleCommand} disabled={isProcessing}>
|
|
350
|
+
* {isListening ? 'Execute' : 'Say Command'}
|
|
351
|
+
* </button>
|
|
352
|
+
* {isProcessing && <span>Processing...</span>}
|
|
353
|
+
* {result && <p>Command: {result.name}</p>}
|
|
354
|
+
* {error && <p style={{ color: 'red' }}>{error}</p>}
|
|
355
|
+
* </div>
|
|
356
|
+
* );
|
|
357
|
+
* }
|
|
358
|
+
* ```
|
|
349
359
|
*
|
|
350
|
-
*
|
|
360
|
+
* @returns Command controls and state
|
|
361
|
+
*/
|
|
362
|
+
function useCommand() {
|
|
363
|
+
const { state: state$1, command, stopCommand } = useSpeechOSContext();
|
|
364
|
+
const [result, setResult] = useState(null);
|
|
365
|
+
const [error, setError] = useState(null);
|
|
366
|
+
const isListening = state$1.recordingState === "recording" && state$1.activeAction === "command";
|
|
367
|
+
const isProcessing = state$1.recordingState === "processing";
|
|
368
|
+
const start = useCallback(async (commands) => {
|
|
369
|
+
setError(null);
|
|
370
|
+
try {
|
|
371
|
+
await command(commands);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
const message = err instanceof Error ? err.message : "Failed to start command";
|
|
374
|
+
setError(message);
|
|
375
|
+
}
|
|
376
|
+
}, [command]);
|
|
377
|
+
const stop = useCallback(async () => {
|
|
378
|
+
try {
|
|
379
|
+
const commandResult = await stopCommand();
|
|
380
|
+
setResult(commandResult);
|
|
381
|
+
setError(null);
|
|
382
|
+
return commandResult;
|
|
383
|
+
} catch (err) {
|
|
384
|
+
const message = err instanceof Error ? err.message : "Failed to process command";
|
|
385
|
+
setError(message);
|
|
386
|
+
throw err;
|
|
387
|
+
}
|
|
388
|
+
}, [stopCommand]);
|
|
389
|
+
const clear = useCallback(() => {
|
|
390
|
+
setResult(null);
|
|
391
|
+
setError(null);
|
|
392
|
+
}, []);
|
|
393
|
+
return {
|
|
394
|
+
start,
|
|
395
|
+
stop,
|
|
396
|
+
isListening,
|
|
397
|
+
isProcessing,
|
|
398
|
+
result,
|
|
399
|
+
error,
|
|
400
|
+
clear
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/hooks/useSpeechOSWidget.ts
|
|
406
|
+
/**
|
|
407
|
+
* Hook for programmatic widget control
|
|
351
408
|
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* console.log('Transcribed:', text);
|
|
409
|
+
* Use this hook when you want to control the widget manually instead of
|
|
410
|
+
* relying on automatic form field detection.
|
|
355
411
|
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
412
|
+
* @example
|
|
413
|
+
* ```tsx
|
|
414
|
+
* function MyComponent() {
|
|
415
|
+
* const { showFor, hide, isVisible } = useSpeechOSWidget();
|
|
416
|
+
* const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
359
417
|
*
|
|
360
418
|
* return (
|
|
361
419
|
* <div>
|
|
362
|
-
* <
|
|
363
|
-
* <
|
|
364
|
-
*
|
|
365
|
-
*
|
|
420
|
+
* <textarea ref={textareaRef} />
|
|
421
|
+
* <button onClick={() => showFor(textareaRef.current!)}>
|
|
422
|
+
* Enable Voice Input
|
|
423
|
+
* </button>
|
|
424
|
+
* {isVisible && (
|
|
425
|
+
* <button onClick={hide}>Hide Widget</button>
|
|
426
|
+
* )}
|
|
366
427
|
* </div>
|
|
367
428
|
* );
|
|
368
429
|
* }
|
|
369
430
|
* ```
|
|
370
|
-
*
|
|
371
|
-
* @returns Low-level transcription controls and state
|
|
372
431
|
*/
|
|
373
|
-
function
|
|
374
|
-
const
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}, [contextDisconnect]);
|
|
432
|
+
function useSpeechOSWidget() {
|
|
433
|
+
const speechOSState = useSpeechOSState();
|
|
434
|
+
const showFor = useCallback((element) => {
|
|
435
|
+
state.setFocusedElement(element);
|
|
436
|
+
state.show();
|
|
437
|
+
}, []);
|
|
438
|
+
const attachTo = useCallback((element) => {
|
|
439
|
+
state.setFocusedElement(element);
|
|
440
|
+
state.show();
|
|
441
|
+
}, []);
|
|
442
|
+
const detach = useCallback(() => {
|
|
443
|
+
state.setFocusedElement(null);
|
|
444
|
+
}, []);
|
|
445
|
+
const show = useCallback(() => {
|
|
446
|
+
state.show();
|
|
447
|
+
}, []);
|
|
448
|
+
const hide = useCallback(() => {
|
|
449
|
+
state.hide();
|
|
450
|
+
}, []);
|
|
393
451
|
return {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
isMicEnabled: state$1.isMicEnabled,
|
|
403
|
-
recordingState: state$1.recordingState
|
|
452
|
+
showFor,
|
|
453
|
+
attachTo,
|
|
454
|
+
detach,
|
|
455
|
+
show,
|
|
456
|
+
hide,
|
|
457
|
+
isVisible: speechOSState.isVisible,
|
|
458
|
+
isExpanded: speechOSState.isExpanded,
|
|
459
|
+
focusedElement: speechOSState.focusedElement
|
|
404
460
|
};
|
|
405
461
|
}
|
|
406
462
|
|
|
@@ -434,7 +490,7 @@ function useTranscription() {
|
|
|
434
490
|
* }
|
|
435
491
|
* ```
|
|
436
492
|
*/
|
|
437
|
-
function SpeechOSWidget({ apiKey, userId,
|
|
493
|
+
function SpeechOSWidget({ apiKey, userId, host, zIndex, debug, onTranscription, onEdit, onError, onShow, onHide, className }) {
|
|
438
494
|
const containerRef = useRef(null);
|
|
439
495
|
const widgetRef = useRef(null);
|
|
440
496
|
const { init, isInitialized } = useSpeechOSContext();
|
|
@@ -443,8 +499,6 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
443
499
|
apiKey,
|
|
444
500
|
userId,
|
|
445
501
|
host,
|
|
446
|
-
position,
|
|
447
|
-
zIndex,
|
|
448
502
|
debug
|
|
449
503
|
});
|
|
450
504
|
}, [
|
|
@@ -453,8 +507,6 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
453
507
|
apiKey,
|
|
454
508
|
userId,
|
|
455
509
|
host,
|
|
456
|
-
position,
|
|
457
|
-
zIndex,
|
|
458
510
|
debug
|
|
459
511
|
]);
|
|
460
512
|
useEffect(() => {
|
|
@@ -492,7 +544,6 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
492
544
|
return;
|
|
493
545
|
}
|
|
494
546
|
const widget = document.createElement("speechos-widget");
|
|
495
|
-
widget.setAttribute("position", position);
|
|
496
547
|
containerRef.current.appendChild(widget);
|
|
497
548
|
widgetRef.current = widget;
|
|
498
549
|
return () => {
|
|
@@ -501,7 +552,7 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
501
552
|
widgetRef.current = null;
|
|
502
553
|
}
|
|
503
554
|
};
|
|
504
|
-
}, [
|
|
555
|
+
}, []);
|
|
505
556
|
return /* @__PURE__ */ jsx("div", {
|
|
506
557
|
ref: containerRef,
|
|
507
558
|
className,
|
|
@@ -514,4 +565,4 @@ function SpeechOSWidget({ apiKey, userId, position = "bottom-center", host, zInd
|
|
|
514
565
|
const VERSION = "0.1.0";
|
|
515
566
|
|
|
516
567
|
//#endregion
|
|
517
|
-
export { SpeechOSContext, SpeechOSProvider, SpeechOSWidget, VERSION, useDictation, useEdit, useSpeechOS, useSpeechOSContext, useSpeechOSEvents, useSpeechOSState,
|
|
568
|
+
export { SpeechOSContext, SpeechOSProvider, SpeechOSWidget, VERSION, useCommand, useDictation, useEdit, useSpeechOS, useSpeechOSContext, useSpeechOSEvents, useSpeechOSState, useSpeechOSWidget };
|