@@ -785,148 +785,7 @@ export function useSpeechInputWithCursor(
785785
786786---
787787
788- ## Phase 4: Optional UI Components
789-
790- ** Goal:** Provide composable, unstyled (headless) UI components for common use cases.
791-
792- ### 4.1 SpeechButton Component
793-
794- Following headless UI best practices (like Radix UI, Headless UI):
795-
796- ``` tsx
797- // src/components/SpeechButton.tsx
798-
799- import { forwardRef , type ReactNode , type ButtonHTMLAttributes } from ' react' ;
800- import { useSpeechInput , type UseSpeechInputOptions } from ' ../hooks/useSpeechInput' ;
801-
802- export interface SpeechButtonRenderProps {
803- isListening: boolean ;
804- isSupported: boolean ;
805- permissionState: ' prompt' | ' granted' | ' denied' | ' unsupported' ;
806- transcript: string ;
807- error: { type: string ; message: string } | null ;
808- }
809-
810- export interface SpeechButtonProps
811- extends ButtonHTMLAttributes <HTMLButtonElement >,
812- UseSpeechInputOptions {
813- asChild? : boolean ; // Radix-style composition
814- }
815-
816- export const SpeechButton = forwardRef <HTMLButtonElement , SpeechButtonProps >(
817- function SpeechButton(
818- { asChild , onClick , ... props },
819- ref
820- ) {
821- const { isListening, toggle } = useSpeechInput (props );
822- const Comp = asChild ? Slot : ' button' ;
823-
824- return (
825- <Comp
826- ref = { ref }
827- onClick = { (e ) => {
828- onClick ?.(e );
829- toggle ();
830- }}
831- aria-pressed = { isListening }
832- { ... props }
833- />
834- );
835- }
836- );
837- ```
838-
839- ### 4.2 SpeechInput Component
840-
841- Controlled input with speech integration:
842-
843- ``` tsx
844- // src/components/SpeechInput.tsx
845-
846- import {
847- forwardRef ,
848- useState ,
849- useRef ,
850- useImperativeHandle ,
851- type InputHTMLAttributes ,
852- type ReactNode
853- } from ' react' ;
854- import { useSpeechInputWithCursor } from ' ../hooks/useSpeechInputWithCursor' ;
855- import type { UseSpeechInputOptions } from ' ../types' ;
856-
857- export interface SpeechInputRenderProps {
858- isListening: boolean ;
859- isSupported: boolean ;
860- permissionState: ' prompt' | ' granted' | ' denied' | ' unsupported' ;
861- error: { type: string ; message: string } | null ;
862- toggle: () => Promise <void >;
863- }
864-
865- export interface SpeechInputProps
866- extends Omit <InputHTMLAttributes <HTMLInputElement >, ' onChange' >,
867- UseSpeechInputOptions {
868- /** Controlled value */
869- value: string ;
870- /** Change handler */
871- onChange: (value : string ) => void ;
872- /** Render prop for mic button */
873- renderButton? : (props : SpeechInputRenderProps ) => ReactNode ;
874- /** Container className */
875- containerClassName? : string ;
876- }
877-
878- export const SpeechInput = forwardRef <HTMLInputElement , SpeechInputProps >(
879- function SpeechInput(
880- {
881- value ,
882- onChange ,
883- renderButton ,
884- containerClassName ,
885- ... props
886- },
887- ref
888- ) {
889- const inputRef = useRef <HTMLInputElement >(null );
890-
891- useImperativeHandle (ref , () => inputRef .current ! , []);
892-
893- const speech = useSpeechInputWithCursor ({
894- inputRef ,
895- value ,
896- onChange ,
897- ... props ,
898- });
899-
900- return (
901- <div className = { containerClassName } >
902- <input
903- ref = { inputRef }
904- value = { value }
905- onChange = { (e ) => onChange (e .target .value )}
906- { ... props }
907- />
908- { renderButton ?.({
909- isListening: speech .isListening ,
910- isSupported: speech .isSupported ,
911- permissionState: speech .permissionState ,
912- error: speech .error ,
913- toggle: speech .toggle ,
914- })}
915- </div >
916- );
917- }
918- );
919- ```
920-
921- ### Deliverables
922- - [ ] ` src/components/SpeechButton.tsx ` — Headless speech toggle button
923- - [ ] ` src/components/SpeechInput.tsx ` — Input with speech integration
924- - [ ] Storybook stories for components
925- - [ ] Accessibility tests (a11y)
926-
927- ---
928-
929- ## Phase 5: Documentation & Examples
788+ ## Phase 4: Documentation & Examples
930789
931790** Goal:** Create comprehensive documentation and example applications.
932791
0 commit comments