@speechos/react 1.0.0 → 1.0.3

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/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$1, dictate, stopDictation, cancel } = useSpeechOSContext();
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$1.recordingState === "recording";
219
- const isProcessing = state$1.recordingState === "processing";
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$1, edit, stopEdit, cancel } = useSpeechOSContext();
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$1.recordingState === "recording" && state$1.activeAction === "edit";
300
- const isProcessing = state$1.recordingState === "processing";
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/useTranscription.ts
337
+ //#region src/hooks/useCommand.ts
342
338
  /**
343
- * Low-level hook for granular transcription control
339
+ * Simplified hook for voice command workflows
344
340
  *
345
- * Use this when you need fine-grained control over the LiveKit
346
- * connection lifecycle. For most use cases, prefer useDictation()
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
- * function CustomVoiceUI() {
352
- * const {
353
- * connect,
354
- * waitUntilReady,
355
- * enableMicrophone,
356
- * getTranscript,
357
- * disconnect,
358
- * isConnected,
359
- * isMicEnabled,
360
- * recordingState,
361
- * } = useTranscription();
362
- *
363
- * const handleRecord = async () => {
364
- * // Step 1: Connect to LiveKit
365
- * await connect();
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
- * // Step 2: Wait for agent to be ready
368
- * await waitUntilReady();
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
- * // Step 3: Enable microphone
371
- * await enableMicrophone();
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
- * // ... user speaks ...
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
- * // Step 4: Get transcript
376
- * const text = await getTranscript();
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
- * // Step 5: Cleanup
380
- * await disconnect();
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
- * <p>Connected: {isConnected ? 'Yes' : 'No'}</p>
386
- * <p>Mic: {isMicEnabled ? 'On' : 'Off'}</p>
387
- * <p>State: {recordingState}</p>
388
- * <button onClick={handleRecord}>Record</button>
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 useTranscription() {
397
- const { state: state$1, connect: contextConnect, waitUntilReady: contextWaitUntilReady, enableMicrophone: contextEnableMicrophone, stopAndGetTranscript, stopAndEdit, disconnect: contextDisconnect, cancel } = useSpeechOSContext();
398
- const connect = (0, react.useCallback)(async () => {
399
- await contextConnect();
400
- }, [contextConnect]);
401
- const waitUntilReady = (0, react.useCallback)(async () => {
402
- await contextWaitUntilReady();
403
- }, [contextWaitUntilReady]);
404
- const enableMicrophone = (0, react.useCallback)(async () => {
405
- await contextEnableMicrophone();
406
- }, [contextEnableMicrophone]);
407
- const getTranscript = (0, react.useCallback)(async () => {
408
- return await stopAndGetTranscript();
409
- }, [stopAndGetTranscript]);
410
- const getEdit = (0, react.useCallback)(async (originalText) => {
411
- return await stopAndEdit(originalText);
412
- }, [stopAndEdit]);
413
- const disconnect = (0, react.useCallback)(async () => {
414
- await contextDisconnect();
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
- connect,
418
- waitUntilReady,
419
- enableMicrophone,
420
- getTranscript,
421
- getEdit,
422
- disconnect,
423
- cancel,
424
- isConnected: state$1.isConnected,
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, position = "bottom-center", host, zIndex, debug, onTranscription, onEdit, onError, onShow, onHide, className }) {
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
- }, [position]);
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.useTranscription = useTranscription;
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, useTranscription, type UseDictationResult, type UseEditResult, type UseTranscriptionResult, } from "./hooks/index.js";
57
- export { SpeechOSWidget, type SpeechOSWidgetProps } from "./components/index.js";
58
- export type { SpeechOSConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, RecordingState, UnsubscribeFn, } from "@speechos/core";
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, useTranscription, type UseDictationResult, type UseEditResult, type UseTranscriptionResult, } from "./hooks/index.js";
57
- export { SpeechOSWidget, type SpeechOSWidgetProps } from "./components/index.js";
58
- export type { SpeechOSConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, RecordingState, UnsubscribeFn, } from "@speechos/core";
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/useTranscription.ts
314
+ //#region src/hooks/useCommand.ts
319
315
  /**
320
- * Low-level hook for granular transcription control
316
+ * Simplified hook for voice command workflows
321
317
  *
322
- * Use this when you need fine-grained control over the LiveKit
323
- * connection lifecycle. For most use cases, prefer useDictation()
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
- * function CustomVoiceUI() {
329
- * const {
330
- * connect,
331
- * waitUntilReady,
332
- * enableMicrophone,
333
- * getTranscript,
334
- * disconnect,
335
- * isConnected,
336
- * isMicEnabled,
337
- * recordingState,
338
- * } = useTranscription();
339
- *
340
- * const handleRecord = async () => {
341
- * // Step 1: Connect to LiveKit
342
- * await connect();
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
- * // Step 2: Wait for agent to be ready
345
- * await waitUntilReady();
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
- * // Step 3: Enable microphone
348
- * await enableMicrophone();
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
- * // ... user speaks ...
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
- * // Step 4: Get transcript
353
- * const text = await getTranscript();
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
- * // Step 5: Cleanup
357
- * await disconnect();
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
- * <p>Connected: {isConnected ? 'Yes' : 'No'}</p>
363
- * <p>Mic: {isMicEnabled ? 'On' : 'Off'}</p>
364
- * <p>State: {recordingState}</p>
365
- * <button onClick={handleRecord}>Record</button>
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 useTranscription() {
374
- const { state: state$1, connect: contextConnect, waitUntilReady: contextWaitUntilReady, enableMicrophone: contextEnableMicrophone, stopAndGetTranscript, stopAndEdit, disconnect: contextDisconnect, cancel } = useSpeechOSContext();
375
- const connect = useCallback(async () => {
376
- await contextConnect();
377
- }, [contextConnect]);
378
- const waitUntilReady = useCallback(async () => {
379
- await contextWaitUntilReady();
380
- }, [contextWaitUntilReady]);
381
- const enableMicrophone = useCallback(async () => {
382
- await contextEnableMicrophone();
383
- }, [contextEnableMicrophone]);
384
- const getTranscript = useCallback(async () => {
385
- return await stopAndGetTranscript();
386
- }, [stopAndGetTranscript]);
387
- const getEdit = useCallback(async (originalText) => {
388
- return await stopAndEdit(originalText);
389
- }, [stopAndEdit]);
390
- const disconnect = useCallback(async () => {
391
- await contextDisconnect();
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
- connect,
395
- waitUntilReady,
396
- enableMicrophone,
397
- getTranscript,
398
- getEdit,
399
- disconnect,
400
- cancel,
401
- isConnected: state$1.isConnected,
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, position = "bottom-center", host, zIndex, debug, onTranscription, onEdit, onError, onShow, onHide, className }) {
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
- }, [position]);
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, useTranscription };
568
+ export { SpeechOSContext, SpeechOSProvider, SpeechOSWidget, VERSION, useCommand, useDictation, useEdit, useSpeechOS, useSpeechOSContext, useSpeechOSEvents, useSpeechOSState, useSpeechOSWidget };