@untemps/react-vocal 2.0.0-beta.13 → 2.0.0-beta.15

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 CHANGED
@@ -1,3 +1,22 @@
1
+ # [2.0.0-beta.15](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.14...v2.0.0-beta.15) (2026-05-24)
2
+
3
+
4
+ ### chore
5
+
6
+ * Migrate codebase to TypeScript ([#156](https://github.com/untemps/react-vocal/issues/156)) ([86c6e49](https://github.com/untemps/react-vocal/commit/86c6e49a82f599b60356afb91f34b59f63710a44))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * minimum supported Node.js version bumped from `^20.19.0 || >=22.12.0` to `>=22`, aligning with the underlying @untemps/vocal 2.x direct dependency
12
+
13
+ # [2.0.0-beta.14](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.13...v2.0.0-beta.14) (2026-05-24)
14
+
15
+
16
+ ### Performance Improvements
17
+
18
+ * Wrap _onFocus and _onBlur in useCallback ([#155](https://github.com/untemps/react-vocal/issues/155)) ([9a26840](https://github.com/untemps/react-vocal/commit/9a2684091252b784ea79f328536006f00ed9862b))
19
+
1
20
  # [2.0.0-beta.13](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.12...v2.0.0-beta.13) (2026-05-24)
2
21
 
3
22
  # [2.0.0-beta.12](https://github.com/untemps/react-vocal/compare/v2.0.0-beta.11...v2.0.0-beta.12) (2026-05-24)
package/README.md CHANGED
@@ -54,6 +54,21 @@ yarn add fuse.js
54
54
 
55
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
56
 
57
+ ## TypeScript
58
+
59
+ `@untemps/react-vocal` is written in TypeScript and ships full type declarations. The public surface is typed end-to-end:
60
+
61
+ - `Vocal` component props (`VocalProps`, `OnResultCallback`)
62
+ - `useVocal` hook signature, action tuple (`UseVocalActions`, `UseVocalReturn`)
63
+ - `useCommands` shapes (`CommandCallback`, `CommandsMap`, `TriggerCommand`)
64
+ - `isSupported` function (re-exported from `@untemps/vocal`)
65
+
66
+ ```typescript
67
+ import Vocal, { useVocal, isSupported, type VocalProps, type CommandsMap } from '@untemps/react-vocal'
68
+ ```
69
+
70
+ TypeScript is listed as an optional peer dependency (`>=6.0.0`) — install it only if your project uses TS.
71
+
57
72
  ## Usage
58
73
 
59
74
  ### `Vocal` component
@@ -327,7 +342,7 @@ const [ref, { start, stop, abort, subscribe, unsubscribe, clean, isRecording }]
327
342
  | abort | func | Function to abort the recognition |
328
343
  | subscribe | func | Function to subscribe to recognition events |
329
344
  | unsubscribe | func | Function to unsubscribe to recognition events |
330
- | clean | func | Function to clean subscription to recognition events |
345
+ | clean | func | Function to remove all event listeners and clean up the recognition instance |
331
346
  | 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
347
 
333
348
  #### Cancelling a start in flight
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let l=require(`react`);l=c(l,1);var u=()=>!!navigator.permissions,d=()=>!!navigator.mediaDevices,f=async(e,t,{signal:n}={})=>{if(!u()||!d())throw new DOMException(`Navigator API: permissions or Navigator API: mediaDevices not supported`,`NOT_SUPPORTED_ERR`);if(n?.throwIfAborted(),(await navigator.permissions.query({name:e})).state===`denied`)throw new DOMException(`Permission denied`,`NOT_ALLOWED_ERR`);n?.throwIfAborted();let r=navigator.mediaDevices.getUserMedia(t);if(!n)return r;let i,a=new Promise((e,t)=>{i=()=>t(n.reason),n.addEventListener(`abort`,i,{once:!0})});return Promise.race([r,a]).finally(()=>{n.removeEventListener(`abort`,i)})},p={AUDIO_END:`audioend`,AUDIO_START:`audiostart`,END:`end`,ERROR:`error`,NO_MATCH:`nomatch`,RESULT:`result`,SOUND_END:`soundend`,SOUND_START:`soundstart`,SPEECH_END:`speechend`,SPEECH_START:`speechstart`,START:`start`},m=1e3,h=new Set([`not-allowed`,`service-not-allowed`,`audio-capture`]),g={grammars:null,lang:`en-US`,continuous:!1,interimResults:!1,maxAlternatives:1},_=()=>{if(!(typeof window>`u`))return window.SpeechRecognition??window.webkitSpeechRecognition??window.mozSpeechRecognition??window.msSpeechRecognition},v=()=>window.SpeechGrammarList??window.webkitSpeechGrammarList??window.mozSpeechGrammarList??window.msSpeechGrammarList,y=e=>e.reduce((e,t)=>(t.confidence??0)>(e.confidence??0)?t:e),b=e=>{let t=e.slice();return Object.defineProperty(t,`isFinal`,{value:!0}),Object.defineProperty(t,`item`,{value:e=>t[e]}),t},x=e=>{let t=e.slice();return Object.defineProperty(t,`item`,{value:e=>t[e]}),t},S=e=>Object.values(p).includes(e),C=e=>`Unknown event type "${e}". Valid types are: ${Object.values(p).join(`, `)}.`,w=()=>!!_()&&!!u()&&!!d(),T=e=>{let t=_();if(!t)throw new DOMException(`SpeechRecognition not supported`,`NOT_SUPPORTED_ERR`);let n=new t,r={},i=!1,a=!1,o=0,s=null,c=!1,l=[],u={...g,...e??{}};if(n.lang=u.lang,n.continuous=u.continuous,n.interimResults=u.interimResults,n.maxAlternatives=u.maxAlternatives,u.grammars)n.grammars=u.grammars;else{let e=v();n.grammars=e?new e:null}let d=()=>{s!==null&&(clearTimeout(s),s=null),c=!1},w=()=>!!n&&!a&&n.continuous,T=()=>{s=null;try{n.start(),o=Date.now()}catch{c=!1,i=!1}},E=()=>{let e=l;if(l=[],e.length===0||!r[p.RESULT]?.length)return;let t=e.map(e=>y(Array.from(e)).transcript).join(` `).trim(),n=Object.assign(new Event(p.RESULT),{resultIndex:0,results:x(e)});[...r[p.RESULT]].forEach(({callback:e})=>{e(n,t,[t])})},D=[[p.END,e=>{if(w()){let t=Math.max(0,m-(Date.now()-o));c=!0,s=setTimeout(T,t),e.stopImmediatePropagation();return}E(),i=!1}],[p.START,e=>{c&&(e.stopImmediatePropagation(),queueMicrotask(()=>{c=!1}))}],[p.ERROR,e=>{h.has(e.error)&&(a=!0,d(),i=!1)}],[p.RESULT,e=>{if(!u.continuous)return;let t=e,n=t.results?.[t.resultIndex];n?.isFinal&&l.push(b(Array.from(n)))}]];D.forEach(([e,t])=>n.addEventListener(e,t));let O=async({signal:e}={})=>{if(n)try{let t=await f(`microphone`,{audio:!0},{signal:e});if(e?.aborted||!n)return;if(!t)throw Error(`Unable to retrieve the stream from media device`);a=!1,l=[],n.start(),i=!0,o=Date.now()}catch(e){if(e instanceof Error&&e.name===`AbortError`)return;throw e}},k=()=>{n&&(a=!0,d(),n.stop(),i=!1)},A=()=>{n&&(a=!0,d(),l=[],n.abort(),i=!1)},j=(e,t)=>{if(!S(e))throw Error(C(e));if(!n)return;let i=n=>{if(c&&(e===p.END||e===p.START))return;if(e!==p.RESULT){t(n);return}let r=n;if(!(r.results?.length>0)||r.resultIndex>=r.results.length){t(n);return}let i=r.results[r.resultIndex];if(u.continuous&&i.isFinal)return;let a=Array.from(i);t(n,y(a).transcript,a.map(e=>e.transcript))};n.addEventListener(e,i),r[e]||(r[e]=[]),r[e].push({callback:t,handler:i})},M=(e,t)=>{if(!S(e))throw Error(C(e));if(!(!n||!r[e]))if(t!==void 0){let i=r[e].findIndex(e=>e.callback===t);i!==-1&&(n.removeEventListener(e,r[e][i].handler),r[e].splice(i,1),r[e].length===0&&delete r[e])}else r[e].forEach(({handler:t})=>n.removeEventListener(e,t)),delete r[e]};return{get isRecording(){return i},start:O,stop:k,abort:A,on:j,off:M,cleanup:()=>{k(),Object.keys(r).forEach(e=>M(e)),D.forEach(([e,t])=>n?.removeEventListener(e,t)),n=null}}},E=e=>typeof e==`function`,D=(e=`en-US`,t=null,n=1,r=!1,i=null)=>{let a=(0,l.useRef)(null),[o,s]=(0,l.useState)(!1),c=(0,l.useMemo)(()=>w(),[]);return(0,l.useEffect)(()=>{if(c){let o=i||T({lang:e,grammars:t,maxAlternatives:n,continuous:r});a.current=o;let c=()=>s(!0),l=()=>s(!1);return o.on(`start`,c),o.on(`end`,l),o.on(`error`,l),()=>{o.off(`start`,c),o.off(`end`,l),o.off(`error`,l),o.abort(),o.cleanup(),s(!1)}}},[e,t,n,r,i,c]),[a,{start:(0,l.useCallback)(e=>{if(a.current)return s(!0),a.current.start(e)},[]),stop:(0,l.useCallback)(()=>{a.current&&a.current.stop()},[]),abort:(0,l.useCallback)(()=>{a.current&&a.current.abort()},[]),subscribe:(0,l.useCallback)((e,t)=>{a.current&&a.current.on(e,t)},[]),unsubscribe:(0,l.useCallback)((e,t)=>{a.current&&a.current.off(e,t)},[]),clean:(0,l.useCallback)(()=>{a.current&&a.current.cleanup()},[]),isRecording:o}]},O=(e,t=0)=>{let n=(0,l.useRef)(-1),r=(0,l.useCallback)(()=>{clearTimeout(n.current),n.current=-1},[]),i=(0,l.useCallback)(()=>{r(),n.current=setTimeout(e,t)},[e,t,r]);return(0,l.useEffect)(()=>r,[r]),[i,r]},k=(e,t=.4)=>{let n=(0,l.useMemo)(()=>e?Object.entries(e).reduce((e,[t,n])=>({...e,[t.toLowerCase()]:n}),{}):{},[e]),r=(0,l.useMemo)(()=>Object.keys(n),[n]),i=(0,l.useMemo)(()=>r.some(e=>e.includes(` `)),[r]),a=(0,l.useRef)(null);return(0,l.useEffect)(()=>{if(!i){a.current=null;return}let e=!1;return import(`fuse.js`).then(t=>{e||(a.current=new(t.default??t)(r,{includeScore:!0,ignoreLocation:!0}))}).catch(()=>{e||process.env.NODE_ENV!==`production`&&console.warn(`[react-vocal] fuse.js is not installed. Phrase command keys will fall back to exact matching. Install fuse.js to enable fuzzy matching: npm install fuse.js`)}),()=>{e=!0}},[i,r]),e=>{if(!r.length)return null;if(!i){let t=e.trim().split(/\s+/),r=t.length>1?t:[e.trim()];for(let e of r){let t=e.toLowerCase();if(t in n)return n[t]?.(e,t)}return null}let o=a.current;if(o){let r=o.search(e).filter(e=>e.score<t);if(r?.length){let t=r[0].item.toLowerCase();return n[t]?.(e,t)}}else{let t=e.toLowerCase(),i=r.find(e=>t.includes(e)||e.includes(t));if(i)return n[i]?.(e,i)}return null}},A=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),j=o((e=>{process.env.NODE_ENV!==`production`&&(function(){function t(e){if(e==null)return null;if(typeof e==`function`)return e.$$typeof===O?null:e.displayName||e.name||null;if(typeof e==`string`)return e;switch(e){case _:return`Fragment`;case y:return`Profiler`;case v:return`StrictMode`;case C:return`Suspense`;case w:return`SuspenseList`;case D:return`Activity`}if(typeof e==`object`)switch(typeof e.tag==`number`&&console.error(`Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue.`),e.$$typeof){case g:return`Portal`;case x:return e.displayName||`Context`;case b:return(e._context.displayName||`Context`)+`.Consumer`;case S:var n=e.render;return e=e.displayName,e||=(e=n.displayName||n.name||``,e===``?`ForwardRef`:`ForwardRef(`+e+`)`),e;case T:return n=e.displayName||null,n===null?t(e.type)||`Memo`:n;case E:n=e._payload,e=e._init;try{return t(e(n))}catch{}}return null}function n(e){return``+e}function r(e){try{n(e);var t=!1}catch{t=!0}if(t){t=console;var r=t.error,i=typeof Symbol==`function`&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||`Object`;return r.call(t,`The provided key is an unsupported type %s. This value must be coerced to a string before using it here.`,i),n(e)}}function i(e){if(e===_)return`<>`;if(typeof e==`object`&&e&&e.$$typeof===E)return`<...>`;try{var n=t(e);return n?`<`+n+`>`:`<...>`}catch{return`<...>`}}function a(){var e=k.A;return e===null?null:e.getOwner()}function o(){return Error(`react-stack-top-frame`)}function s(e){if(A.call(e,`key`)){var t=Object.getOwnPropertyDescriptor(e,`key`).get;if(t&&t.isReactWarning)return!1}return e.key!==void 0}function c(e,t){function n(){N||(N=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}n.isReactWarning=!0,Object.defineProperty(e,`key`,{get:n,configurable:!0})}function l(){var e=t(this.type);return P[e]||(P[e]=!0,console.error(`Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.`)),e=this.props.ref,e===void 0?null:e}function u(e,t,n,r,i,a){var o=n.ref;return e={$$typeof:h,type:e,key:t,props:n,_owner:r},(o===void 0?null:o)===null?Object.defineProperty(e,`ref`,{enumerable:!1,value:null}):Object.defineProperty(e,`ref`,{enumerable:!1,get:l}),e._store={},Object.defineProperty(e._store,`validated`,{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,`_debugInfo`,{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,`_debugStack`,{configurable:!1,enumerable:!1,writable:!0,value:i}),Object.defineProperty(e,`_debugTask`,{configurable:!1,enumerable:!1,writable:!0,value:a}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function d(e,n,i,o,l,d){var p=n.children;if(p!==void 0)if(o)if(j(p)){for(o=0;o<p.length;o++)f(p[o]);Object.freeze&&Object.freeze(p)}else console.error(`React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.`);else f(p);if(A.call(n,`key`)){p=t(e);var m=Object.keys(n).filter(function(e){return e!==`key`});o=0<m.length?`{key: someKey, `+m.join(`: ..., `)+`: ...}`:`{key: someKey}`,L[p+o]||(m=0<m.length?`{`+m.join(`: ..., `)+`: ...}`:`{}`,console.error(`A props object containing a "key" prop is being spread into JSX:
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);let t=require(`react`);var n=()=>!!navigator.permissions,r=()=>!!navigator.mediaDevices,i=async(e,t,{signal:i}={})=>{if(!n()||!r())throw new DOMException(`Navigator API: permissions or Navigator API: mediaDevices not supported`,`NOT_SUPPORTED_ERR`);if(i?.throwIfAborted(),(await navigator.permissions.query({name:e})).state===`denied`)throw new DOMException(`Permission denied`,`NOT_ALLOWED_ERR`);i?.throwIfAborted();let a=navigator.mediaDevices.getUserMedia(t);if(!i)return a;let o,s=new Promise((e,t)=>{o=()=>t(i.reason),i.addEventListener(`abort`,o,{once:!0})});return Promise.race([a,s]).finally(()=>{i.removeEventListener(`abort`,o)})},a={AUDIO_END:`audioend`,AUDIO_START:`audiostart`,END:`end`,ERROR:`error`,NO_MATCH:`nomatch`,RESULT:`result`,SOUND_END:`soundend`,SOUND_START:`soundstart`,SPEECH_END:`speechend`,SPEECH_START:`speechstart`,START:`start`},o=1e3,s=new Set([`not-allowed`,`service-not-allowed`,`audio-capture`]),c={grammars:null,lang:`en-US`,continuous:!1,interimResults:!1,maxAlternatives:1},l=()=>{if(!(typeof window>`u`))return window.SpeechRecognition??window.webkitSpeechRecognition??window.mozSpeechRecognition??window.msSpeechRecognition},u=()=>window.SpeechGrammarList??window.webkitSpeechGrammarList??window.mozSpeechGrammarList??window.msSpeechGrammarList,d=e=>e.reduce((e,t)=>(t.confidence??0)>(e.confidence??0)?t:e),f=e=>{let t=e.slice();return Object.defineProperty(t,`isFinal`,{value:!0}),Object.defineProperty(t,`item`,{value:e=>t[e]}),t},p=e=>{let t=e.slice();return Object.defineProperty(t,`item`,{value:e=>t[e]}),t},m=e=>Object.values(a).includes(e),h=e=>`Unknown event type "${e}". Valid types are: ${Object.values(a).join(`, `)}.`,g=()=>!!l()&&!!n()&&!!r(),_=e=>{let t=l();if(!t)throw new DOMException(`SpeechRecognition not supported`,`NOT_SUPPORTED_ERR`);let n=new t,r={},g=!1,_=!1,v=0,y=null,b=!1,x=[],S={...c,...e??{}};if(n.lang=S.lang,n.continuous=S.continuous,n.interimResults=S.interimResults,n.maxAlternatives=S.maxAlternatives,S.grammars)n.grammars=S.grammars;else{let e=u();n.grammars=e?new e:null}let C=()=>{y!==null&&(clearTimeout(y),y=null),b=!1},w=()=>!!n&&!_&&n.continuous,T=()=>{y=null;try{n.start(),v=Date.now()}catch{b=!1,g=!1}},E=()=>{let e=x;if(x=[],e.length===0||!r[a.RESULT]?.length)return;let t=e.map(e=>d(Array.from(e)).transcript).join(` `).trim(),n=Object.assign(new Event(a.RESULT),{resultIndex:0,results:p(e)});[...r[a.RESULT]].forEach(({callback:e})=>{e(n,t,[t])})},D=[[a.END,e=>{if(w()){let t=Math.max(0,o-(Date.now()-v));b=!0,y=setTimeout(T,t),e.stopImmediatePropagation();return}E(),g=!1}],[a.START,e=>{b&&(e.stopImmediatePropagation(),queueMicrotask(()=>{b=!1}))}],[a.ERROR,e=>{s.has(e.error)&&(_=!0,C(),g=!1)}],[a.RESULT,e=>{if(!S.continuous)return;let t=e,n=t.results?.[t.resultIndex];n?.isFinal&&x.push(f(Array.from(n)))}]];D.forEach(([e,t])=>n.addEventListener(e,t));let O=async({signal:e}={})=>{if(n)try{let t=await i(`microphone`,{audio:!0},{signal:e});if(e?.aborted||!n)return;if(!t)throw Error(`Unable to retrieve the stream from media device`);_=!1,x=[],n.start(),g=!0,v=Date.now()}catch(e){if(e instanceof Error&&e.name===`AbortError`)return;throw e}},k=()=>{n&&(_=!0,C(),n.stop(),g=!1)},A=()=>{n&&(_=!0,C(),x=[],n.abort(),g=!1)},j=(e,t)=>{if(!m(e))throw Error(h(e));if(!n)return;let i=n=>{if(b&&(e===a.END||e===a.START))return;if(e!==a.RESULT){t(n);return}let r=n;if(!(r.results?.length>0)||r.resultIndex>=r.results.length){t(n);return}let i=r.results[r.resultIndex];if(S.continuous&&i.isFinal)return;let o=Array.from(i);t(n,d(o).transcript,o.map(e=>e.transcript))};n.addEventListener(e,i),r[e]||(r[e]=[]),r[e].push({callback:t,handler:i})},M=(e,t)=>{if(!m(e))throw Error(h(e));if(!(!n||!r[e]))if(t!==void 0){let i=r[e].findIndex(e=>e.callback===t);i!==-1&&(n.removeEventListener(e,r[e][i].handler),r[e].splice(i,1),r[e].length===0&&delete r[e])}else r[e].forEach(({handler:t})=>n.removeEventListener(e,t)),delete r[e]};return{get isRecording(){return g},start:O,stop:k,abort:A,on:j,off:M,cleanup:()=>{k(),Object.keys(r).forEach(e=>M(e)),D.forEach(([e,t])=>n?.removeEventListener(e,t)),n=null}}},v=e=>typeof e==`function`,y=(e=`en-US`,n=null,r=1,i=!1,a=null)=>{let o=(0,t.useRef)(null),[s,c]=(0,t.useState)(!1),l=(0,t.useMemo)(()=>g(),[]);return(0,t.useEffect)(()=>{if(l){let t=a||_({lang:e,grammars:n,maxAlternatives:r,continuous:i});o.current=t;let s=()=>c(!0),l=()=>c(!1);return t.on(`start`,s),t.on(`end`,l),t.on(`error`,l),()=>{t.off(`start`,s),t.off(`end`,l),t.off(`error`,l),t.abort(),t.cleanup(),c(!1)}}},[e,n,r,i,a,l]),[o,{start:(0,t.useCallback)(e=>{if(o.current)return c(!0),o.current.start(e)},[]),stop:(0,t.useCallback)(()=>{o.current&&o.current.stop()},[]),abort:(0,t.useCallback)(()=>{o.current&&o.current.abort()},[]),subscribe:(0,t.useCallback)((e,t)=>{o.current&&o.current.on(e,t)},[]),unsubscribe:(0,t.useCallback)((e,t)=>{o.current&&o.current.off(e,t)},[]),clean:(0,t.useCallback)(()=>{o.current&&o.current.cleanup()},[]),isRecording:s}]},b=(e,n=0)=>{let r=(0,t.useRef)(-1),i=(0,t.useCallback)(()=>{clearTimeout(r.current),r.current=-1},[]),a=(0,t.useCallback)(()=>{i(),r.current=setTimeout(e,n)},[e,n,i]);return(0,t.useEffect)(()=>i,[i]),[a,i]},x=(e,n=.4)=>{let r=(0,t.useMemo)(()=>e?Object.entries(e).reduce((e,[t,n])=>({...e,[t.toLowerCase()]:n}),{}):{},[e]),i=(0,t.useMemo)(()=>Object.keys(r),[r]),a=(0,t.useMemo)(()=>i.some(e=>e.includes(` `)),[i]),o=(0,t.useRef)(null);return(0,t.useEffect)(()=>{if(!a){o.current=null;return}let e=!1;return import(`fuse.js`).then(t=>{e||(o.current=new(t.default??t)(i,{includeScore:!0,ignoreLocation:!0}))}).catch(()=>{e||process.env.NODE_ENV!==`production`&&console.warn(`[react-vocal] fuse.js is not installed. Phrase command keys will fall back to exact matching. Install fuse.js to enable fuzzy matching: npm install fuse.js`)}),()=>{e=!0}},[a,i]),e=>{if(!i.length)return null;if(!a){let t=e.trim().split(/\s+/),n=t.length>1?t:[e.trim()];for(let e of n){let t=e.toLowerCase();if(t in r)return r[t]?.(e,t)}return null}let t=o.current;if(t){let i=t.search(e).filter(e=>(e.score??1)<n);if(i?.length){let t=i[0].item.toLowerCase();return r[t]?.(e,t)}}else{let t=e.toLowerCase(),n=i.find(e=>t.includes(e)||e.includes(t));if(n)return r[n]?.(e,n)}return null}},S=e((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),C=e((e=>{process.env.NODE_ENV!==`production`&&(function(){function t(e){if(e==null)return null;if(typeof e==`function`)return e.$$typeof===O?null:e.displayName||e.name||null;if(typeof e==`string`)return e;switch(e){case _:return`Fragment`;case y:return`Profiler`;case v:return`StrictMode`;case C:return`Suspense`;case w:return`SuspenseList`;case D:return`Activity`}if(typeof e==`object`)switch(typeof e.tag==`number`&&console.error(`Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue.`),e.$$typeof){case g:return`Portal`;case x:return e.displayName||`Context`;case b:return(e._context.displayName||`Context`)+`.Consumer`;case S:var n=e.render;return e=e.displayName,e||=(e=n.displayName||n.name||``,e===``?`ForwardRef`:`ForwardRef(`+e+`)`),e;case T:return n=e.displayName||null,n===null?t(e.type)||`Memo`:n;case E:n=e._payload,e=e._init;try{return t(e(n))}catch{}}return null}function n(e){return``+e}function r(e){try{n(e);var t=!1}catch{t=!0}if(t){t=console;var r=t.error,i=typeof Symbol==`function`&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||`Object`;return r.call(t,`The provided key is an unsupported type %s. This value must be coerced to a string before using it here.`,i),n(e)}}function i(e){if(e===_)return`<>`;if(typeof e==`object`&&e&&e.$$typeof===E)return`<...>`;try{var n=t(e);return n?`<`+n+`>`:`<...>`}catch{return`<...>`}}function a(){var e=k.A;return e===null?null:e.getOwner()}function o(){return Error(`react-stack-top-frame`)}function s(e){if(A.call(e,`key`)){var t=Object.getOwnPropertyDescriptor(e,`key`).get;if(t&&t.isReactWarning)return!1}return e.key!==void 0}function c(e,t){function n(){N||(N=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}n.isReactWarning=!0,Object.defineProperty(e,`key`,{get:n,configurable:!0})}function l(){var e=t(this.type);return P[e]||(P[e]=!0,console.error(`Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.`)),e=this.props.ref,e===void 0?null:e}function u(e,t,n,r,i,a){var o=n.ref;return e={$$typeof:h,type:e,key:t,props:n,_owner:r},(o===void 0?null:o)===null?Object.defineProperty(e,`ref`,{enumerable:!1,value:null}):Object.defineProperty(e,`ref`,{enumerable:!1,get:l}),e._store={},Object.defineProperty(e._store,`validated`,{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,`_debugInfo`,{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,`_debugStack`,{configurable:!1,enumerable:!1,writable:!0,value:i}),Object.defineProperty(e,`_debugTask`,{configurable:!1,enumerable:!1,writable:!0,value:a}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function d(e,n,i,o,l,d){var p=n.children;if(p!==void 0)if(o)if(j(p)){for(o=0;o<p.length;o++)f(p[o]);Object.freeze&&Object.freeze(p)}else console.error(`React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.`);else f(p);if(A.call(n,`key`)){p=t(e);var m=Object.keys(n).filter(function(e){return e!==`key`});o=0<m.length?`{key: someKey, `+m.join(`: ..., `)+`: ...}`:`{key: someKey}`,L[p+o]||(m=0<m.length?`{`+m.join(`: ..., `)+`: ...}`:`{}`,console.error(`A props object containing a "key" prop is being spread into JSX:
2
2
  let props = %s;
3
3
  <%s {...props} />
4
4
  React keys must be passed directly to JSX without using spread:
5
5
  let props = %s;
6
- <%s key={someKey} {...props} />`,o,p,m,p),L[p+o]=!0)}if(p=null,i!==void 0&&(r(i),p=``+i),s(n)&&(r(n.key),p=``+n.key),`key`in n)for(var h in i={},n)h!==`key`&&(i[h]=n[h]);else i=n;return p&&c(i,typeof e==`function`?e.displayName||e.name||`Unknown`:e),u(e,p,i,a(),l,d)}function f(e){p(e)?e._store&&(e._store.validated=1):typeof e==`object`&&e&&e.$$typeof===E&&(e._payload.status===`fulfilled`?p(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function p(e){return typeof e==`object`&&!!e&&e.$$typeof===h}var m=require(`react`),h=Symbol.for(`react.transitional.element`),g=Symbol.for(`react.portal`),_=Symbol.for(`react.fragment`),v=Symbol.for(`react.strict_mode`),y=Symbol.for(`react.profiler`),b=Symbol.for(`react.consumer`),x=Symbol.for(`react.context`),S=Symbol.for(`react.forward_ref`),C=Symbol.for(`react.suspense`),w=Symbol.for(`react.suspense_list`),T=Symbol.for(`react.memo`),E=Symbol.for(`react.lazy`),D=Symbol.for(`react.activity`),O=Symbol.for(`react.client.reference`),k=m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,A=Object.prototype.hasOwnProperty,j=Array.isArray,M=console.createTask?console.createTask:function(){return null};m={react_stack_bottom_frame:function(e){return e()}};var N,P={},F=m.react_stack_bottom_frame.bind(m,o)(),I=M(i(o)),L={};e.Fragment=_,e.jsx=function(e,t,n){var r=1e4>k.recentlyCreatedOwnerStacks++;return d(e,t,n,!1,r?Error(`react-stack-top-frame`):F,r?M(i(e)):I)},e.jsxs=function(e,t,n){var r=1e4>k.recentlyCreatedOwnerStacks++;return d(e,t,n,!0,r?Error(`react-stack-top-frame`):F,r?M(i(e)):I)}})()})),M=o(((e,t)=>{process.env.NODE_ENV===`production`?t.exports=A():t.exports=j()}))(),N=({color:e=`black`,activeColor:t=`red`,isActive:n=!1})=>(0,M.jsx)(`svg`,{"data-testid":`__icon-root__`,xmlns:`http://www.w3.org/2000/svg`,width:`100%`,height:`100%`,viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,M.jsxs)(`g`,{children:[(0,M.jsx)(`path`,{"data-testid":`__icon-path__`,fill:e,d:`M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z`}),n&&(0,M.jsx)(`circle`,{"data-testid":`__icon-active__`,fill:t,cx:`16`,cy:`4`,r:`4`})]})}),P=(e,t)=>{for(let n of e)for(let e of n)if(t(e.transcript??``)!==null)return},F=({children:e,commands:t=null,lang:n=`en-US`,grammars:r=null,timeout:i=3e3,silenceTimeout:a=null,precision:o=.4,maxAlternatives:s=1,continuous:c=!1,ariaLabel:u=`start recognition`,style:d=null,className:f=null,outlineStyle:p=`2px solid`,onStart:m=null,onEnd:h=null,onSpeechStart:g=null,onSpeechEnd:_=null,onResult:v=null,onError:y=null,onNoMatch:b=null,signal:x=null,__rsInstance:S})=>{let C=(0,l.useRef)(null),T=(0,l.useMemo)(()=>w(),[]),[,{start:A,stop:j,subscribe:F,unsubscribe:I,isRecording:L}]=D(n,r,s,c,S),R=k(t,o),z=(0,l.useRef)({});z.current={onStart:m,onEnd:h,onSpeechStart:g,onSpeechEnd:_,onResult:v,onError:y,onNoMatch:b};let B=(0,l.useRef)(c);B.current=c;let V=(0,l.useRef)(R);V.current=R;let H=(0,l.useRef)(null),U=(0,l.useRef)(null),W=(0,l.useRef)(!1),G=(0,l.useRef)(a);G.current=a;let ee=(0,l.useCallback)(()=>U.current?.(),[]),[K,q]=O(ee,i),[te,J]=O(ee,a??0),Y=(0,l.useCallback)(()=>{try{j()}catch(e){z.current.onError?.(e),H.current?.()}},[j]),ne=(0,l.useCallback)(e=>{K(),z.current.onStart?.(e)},[K]),re=(0,l.useCallback)(e=>{q(),z.current.onSpeechStart?.(e)},[q]),ie=(0,l.useCallback)(e=>{K(),B.current&&G.current>0&&te(),z.current.onSpeechEnd?.(e)},[K,te]),ae=(0,l.useCallback)((e,t)=>{q(),B.current||(P(e?.results??[],V.current),Y()),z.current.onResult?.(t,e)},[q,Y]),X=(0,l.useCallback)(e=>{Y(),z.current.onError?.(e)},[Y]),oe=(0,l.useCallback)(e=>{q(),Y(),z.current.onNoMatch?.(e)},[q,Y]),Z=(0,l.useCallback)(e=>{if(!W.current){W.current=!0,q(),J();try{Y(),H.current?.()}finally{W.current=!1,z.current.onEnd?.(e)}}},[q,J,Y]);U.current=Z;let Q=(0,l.useMemo)(()=>({start:ne,end:Z,speechstart:re,speechend:ie,result:ae,error:X,nomatch:oe}),[ne,Z,re,ie,ae,X,oe]);H.current=()=>Object.entries(Q).forEach(([e,t])=>I?.(e,t));let $=(0,l.useCallback)(()=>{try{J(),Object.entries(Q).forEach(([e,t])=>F(e,t)),A({signal:x})?.catch?.(X)}catch(e){X(e)}},[Q,F,A,J,X,x]),se=()=>{!f&&p&&(C.current.style.outline=p)},ce=()=>{!f&&p&&(C.current.style.outline=`none`)};return T?E(e)?e($,Y,L):(0,l.isValidElement)(e)?(0,l.cloneElement)(e,{...!L&&{onClick:$}}):(0,M.jsx)(`button`,{"data-testid":`__vocal-root__`,ref:C,"aria-label":u,"aria-pressed":L,style:f?null:{width:24,height:24,backgroundColor:`transparent`,border:`none`,padding:0,cursor:!c&&L?`default`:`pointer`,...d},className:f,onFocus:se,onBlur:ce,onClick:L?Y:$,children:(0,M.jsx)(N,{isActive:L,color:`#aaa`})}):null};exports.default=F,exports.isSupported=w,exports.useVocal=D;
6
+ <%s key={someKey} {...props} />`,o,p,m,p),L[p+o]=!0)}if(p=null,i!==void 0&&(r(i),p=``+i),s(n)&&(r(n.key),p=``+n.key),`key`in n)for(var h in i={},n)h!==`key`&&(i[h]=n[h]);else i=n;return p&&c(i,typeof e==`function`?e.displayName||e.name||`Unknown`:e),u(e,p,i,a(),l,d)}function f(e){p(e)?e._store&&(e._store.validated=1):typeof e==`object`&&e&&e.$$typeof===E&&(e._payload.status===`fulfilled`?p(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function p(e){return typeof e==`object`&&!!e&&e.$$typeof===h}var m=require(`react`),h=Symbol.for(`react.transitional.element`),g=Symbol.for(`react.portal`),_=Symbol.for(`react.fragment`),v=Symbol.for(`react.strict_mode`),y=Symbol.for(`react.profiler`),b=Symbol.for(`react.consumer`),x=Symbol.for(`react.context`),S=Symbol.for(`react.forward_ref`),C=Symbol.for(`react.suspense`),w=Symbol.for(`react.suspense_list`),T=Symbol.for(`react.memo`),E=Symbol.for(`react.lazy`),D=Symbol.for(`react.activity`),O=Symbol.for(`react.client.reference`),k=m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,A=Object.prototype.hasOwnProperty,j=Array.isArray,M=console.createTask?console.createTask:function(){return null};m={react_stack_bottom_frame:function(e){return e()}};var N,P={},F=m.react_stack_bottom_frame.bind(m,o)(),I=M(i(o)),L={};e.Fragment=_,e.jsx=function(e,t,n){var r=1e4>k.recentlyCreatedOwnerStacks++;return d(e,t,n,!1,r?Error(`react-stack-top-frame`):F,r?M(i(e)):I)},e.jsxs=function(e,t,n){var r=1e4>k.recentlyCreatedOwnerStacks++;return d(e,t,n,!0,r?Error(`react-stack-top-frame`):F,r?M(i(e)):I)}})()})),w=e(((e,t)=>{process.env.NODE_ENV===`production`?t.exports=S():t.exports=C()}))(),T=({color:e=`black`,activeColor:t=`red`,isActive:n=!1})=>(0,w.jsx)(`svg`,{"data-testid":`__icon-root__`,xmlns:`http://www.w3.org/2000/svg`,width:`100%`,height:`100%`,viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,w.jsxs)(`g`,{children:[(0,w.jsx)(`path`,{"data-testid":`__icon-path__`,fill:e,d:`M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z`}),n&&(0,w.jsx)(`circle`,{"data-testid":`__icon-active__`,fill:t,cx:`16`,cy:`4`,r:`4`})]})}),E=(e,t)=>{for(let n of e)for(let e of n)if(t(e.transcript??``)!==null)return},D=({children:e,commands:n=null,lang:r=`en-US`,grammars:i=null,timeout:a=3e3,silenceTimeout:o=null,precision:s=.4,maxAlternatives:c=1,continuous:l=!1,ariaLabel:u=`start recognition`,style:d=null,className:f=null,outlineStyle:p=`2px solid`,onStart:m=null,onEnd:h=null,onSpeechStart:_=null,onSpeechEnd:S=null,onResult:C=null,onError:D=null,onNoMatch:O=null,signal:k=null,__rsInstance:A=null})=>{let j=(0,t.useRef)(null),M=(0,t.useMemo)(()=>g(),[]),[,{start:N,stop:P,subscribe:F,unsubscribe:I,isRecording:L}]=y(r,i,c,l,A),ee=x(n,s),R=(0,t.useRef)({onStart:null,onEnd:null,onSpeechStart:null,onSpeechEnd:null,onResult:null,onError:null,onNoMatch:null});R.current={onStart:m,onEnd:h,onSpeechStart:_,onSpeechEnd:S,onResult:C,onError:D,onNoMatch:O};let z=(0,t.useRef)(l);z.current=l;let B=(0,t.useRef)(ee);B.current=ee;let V=(0,t.useRef)(null),H=(0,t.useRef)(null),U=(0,t.useRef)(!1),W=(0,t.useRef)(o);W.current=o;let G=(0,t.useCallback)(()=>H.current?.(),[]),[K,q]=b(G,a),[te,J]=b(G,o??0),Y=(0,t.useCallback)(()=>{try{P()}catch(e){R.current.onError?.(e),V.current?.()}},[P]),ne=(0,t.useCallback)(e=>{K(),R.current.onStart?.(e)},[K]),re=(0,t.useCallback)(e=>{q(),R.current.onSpeechStart?.(e)},[q]),ie=(0,t.useCallback)(e=>{K(),z.current&&(W.current??0)>0&&te(),R.current.onSpeechEnd?.(e)},[K,te]),ae=(0,t.useCallback)((e,t)=>{q(),z.current||(E(e.results===void 0?[]:Array.from(e.results,e=>Array.from(e)),B.current),Y()),R.current.onResult?.(t,e)},[q,Y]),X=(0,t.useCallback)(e=>{Y(),R.current.onError?.(e)},[Y]),oe=(0,t.useCallback)(e=>{q(),Y(),R.current.onNoMatch?.(e)},[q,Y]),Z=(0,t.useCallback)(e=>{if(!U.current){U.current=!0,q(),J();try{Y(),V.current?.()}finally{U.current=!1,R.current.onEnd?.(e)}}},[q,J,Y]);H.current=Z;let Q=(0,t.useMemo)(()=>({start:ne,end:Z,speechstart:re,speechend:ie,result:ae,error:X,nomatch:oe}),[ne,Z,re,ie,ae,X,oe]);V.current=()=>Object.entries(Q).forEach(([e,t])=>I?.(e,t));let $=(0,t.useCallback)(()=>{try{J(),Object.entries(Q).forEach(([e,t])=>F(e,t)),N({signal:k??void 0})?.catch?.(X)}catch(e){X(e)}},[Q,F,N,J,X,k]),se=(0,t.useCallback)(()=>{!f&&p&&j.current&&(j.current.style.outline=p)},[f,p]),ce=(0,t.useCallback)(()=>{!f&&p&&j.current&&(j.current.style.outline=`none`)},[f,p]);return M?v(e)?e($,Y,L):(0,t.isValidElement)(e)?(0,t.cloneElement)(e,{...!L&&{onClick:$}}):(0,w.jsx)(`button`,{"data-testid":`__vocal-root__`,ref:j,"aria-label":u,"aria-pressed":L,style:f?void 0:{width:24,height:24,backgroundColor:`transparent`,border:`none`,padding:0,cursor:!l&&L?`default`:`pointer`,...d??{}},className:f??void 0,onFocus:se,onBlur:ce,onClick:L?Y:$,children:(0,w.jsx)(T,{isActive:L,color:`#aaa`})}):null};exports.default=D,exports.isSupported=g,exports.useVocal=y;
7
7
  //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,76 @@
1
+ import { CSSProperties } from 'react';
2
+ import { EventHandlerFor } from '@untemps/vocal';
3
+ import { EventType } from '@untemps/vocal';
4
+ import { GenericEventHandler } from '@untemps/vocal';
5
+ import { isSupported } from '@untemps/vocal';
6
+ import { JSX } from 'react/jsx-runtime';
7
+ import { ReactElement } from 'react';
8
+ import { ReactNode } from 'react';
9
+ import { RefObject } from 'react';
10
+ import { VocalInstance } from '@untemps/vocal';
11
+
12
+ export declare type CommandCallback = (rawInput: string, commandKey: string) => unknown;
13
+
14
+ export declare type CommandsMap = Record<string, CommandCallback>;
15
+
16
+ export { isSupported }
17
+
18
+ export declare type OnResultCallback = (bestAlternative: string, event: SpeechRecognitionEvent | Event) => void;
19
+
20
+ export declare type TriggerCommand = (rawInput: string) => unknown;
21
+
22
+ export declare const useVocal: (lang?: string, grammars?: SpeechGrammarList | null, maxAlternatives?: number, continuous?: boolean, __rsInstance?: VocalInstance | null) => UseVocalReturn;
23
+
24
+ export declare interface UseVocalActions {
25
+ start: (options?: {
26
+ signal?: AbortSignal;
27
+ }) => Promise<void> | undefined;
28
+ stop: () => void;
29
+ abort: () => void;
30
+ subscribe: {
31
+ <T extends EventType>(eventType: T, handler: EventHandlerFor<T>): void;
32
+ (eventType: string, handler: GenericEventHandler): void;
33
+ };
34
+ unsubscribe: {
35
+ <T extends EventType>(eventType: T, handler?: EventHandlerFor<T>): void;
36
+ (eventType: string, handler?: GenericEventHandler): void;
37
+ };
38
+ clean: () => void;
39
+ isRecording: boolean;
40
+ }
41
+
42
+ export declare type UseVocalReturn = [RefObject<VocalInstance | null>, UseVocalActions];
43
+
44
+ declare const Vocal: ({ children, commands, lang, grammars, timeout, silenceTimeout, precision, maxAlternatives, continuous, ariaLabel, style, className, outlineStyle, onStart, onEnd, onSpeechStart, onSpeechEnd, onResult, onError, onNoMatch, signal, __rsInstance, }: VocalProps) => JSX.Element | null;
45
+ export default Vocal;
46
+
47
+ export declare interface VocalProps {
48
+ children?: ReactNode | ((start: () => void, stop: () => void, isStarted: boolean) => ReactElement | null);
49
+ commands?: CommandsMap | null;
50
+ lang?: string;
51
+ grammars?: SpeechGrammarList | null;
52
+ timeout?: number;
53
+ silenceTimeout?: number | null;
54
+ precision?: number;
55
+ maxAlternatives?: number;
56
+ continuous?: boolean;
57
+ ariaLabel?: string;
58
+ style?: CSSProperties | null;
59
+ className?: string | null;
60
+ outlineStyle?: string | null;
61
+ onStart?: ((event: Event) => void) | null;
62
+ onEnd?: ((event?: Event) => void) | null;
63
+ onSpeechStart?: ((event: Event) => void) | null;
64
+ onSpeechEnd?: ((event: Event) => void) | null;
65
+ onResult?: OnResultCallback | null;
66
+ onError?: ((error: unknown) => void) | null;
67
+ onNoMatch?: ((event: Event) => void) | null;
68
+ signal?: AbortSignal | null;
69
+ /**
70
+ * Internal/testing escape hatch. Injects a custom vocal instance. Not part of the
71
+ * stable public API — see issue #136 for the redesign of this surface.
72
+ */
73
+ __rsInstance?: VocalInstance | null;
74
+ }
75
+
76
+ export { }
package/dist/index.es.js CHANGED
@@ -245,7 +245,7 @@ var s = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t
245
245
  }
246
246
  let r = c.current;
247
247
  if (r) {
248
- let i = r.search(e).filter((e) => e.score < t);
248
+ let i = r.search(e).filter((e) => (e.score ?? 1) < t);
249
249
  if (i?.length) {
250
250
  let t = i[0].item.toLowerCase();
251
251
  return n[t]?.(e, t);
@@ -451,8 +451,16 @@ var s = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t
451
451
  })] })
452
452
  }), N = (e, t) => {
453
453
  for (let n of e) for (let e of n) if (t(e.transcript ?? "") !== null) return;
454
- }, P = ({ children: r, commands: o = null, lang: s = "en-US", grammars: c = null, timeout: l = 3e3, silenceTimeout: u = null, precision: d = .4, maxAlternatives: f = 1, continuous: p = !1, ariaLabel: m = "start recognition", style: h = null, className: g = null, outlineStyle: _ = "2px solid", onStart: v = null, onEnd: y = null, onSpeechStart: b = null, onSpeechEnd: x = null, onResult: S = null, onError: w = null, onNoMatch: k = null, signal: A = null, __rsInstance: P }) => {
455
- let F = a(null), I = i(() => C(), []), [, { start: L, stop: R, subscribe: ee, unsubscribe: te, isRecording: z }] = E(s, c, f, p, P), B = O(o, d), V = a({});
454
+ }, P = ({ children: r, commands: o = null, lang: s = "en-US", grammars: c = null, timeout: l = 3e3, silenceTimeout: u = null, precision: d = .4, maxAlternatives: f = 1, continuous: p = !1, ariaLabel: m = "start recognition", style: h = null, className: g = null, outlineStyle: _ = "2px solid", onStart: v = null, onEnd: y = null, onSpeechStart: b = null, onSpeechEnd: x = null, onResult: S = null, onError: w = null, onNoMatch: k = null, signal: A = null, __rsInstance: P = null }) => {
455
+ let F = a(null), I = i(() => C(), []), [, { start: L, stop: R, subscribe: ee, unsubscribe: te, isRecording: z }] = E(s, c, f, p, P), B = O(o, d), V = a({
456
+ onStart: null,
457
+ onEnd: null,
458
+ onSpeechStart: null,
459
+ onSpeechEnd: null,
460
+ onResult: null,
461
+ onError: null,
462
+ onNoMatch: null
463
+ });
456
464
  V.current = {
457
465
  onStart: v,
458
466
  onEnd: y,
@@ -479,9 +487,9 @@ var s = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t
479
487
  }, [K]), se = n((e) => {
480
488
  q(), V.current.onSpeechStart?.(e);
481
489
  }, [q]), ce = n((e) => {
482
- K(), H.current && re.current > 0 && ae(), V.current.onSpeechEnd?.(e);
490
+ K(), H.current && (re.current ?? 0) > 0 && ae(), V.current.onSpeechEnd?.(e);
483
491
  }, [K, ae]), le = n((e, t) => {
484
- q(), H.current || (N(e?.results ?? [], U.current), Y()), V.current.onResult?.(t, e);
492
+ q(), H.current || (N(e.results === void 0 ? [] : Array.from(e.results, (e) => Array.from(e)), U.current), Y()), V.current.onResult?.(t, e);
485
493
  }, [q, Y]), X = n((e) => {
486
494
  Y(), V.current.onError?.(e);
487
495
  }, [Y]), ue = n((e) => {
@@ -521,7 +529,7 @@ var s = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t
521
529
  W.current = () => Object.entries(Q).forEach(([e, t]) => te?.(e, t));
522
530
  let $ = n(() => {
523
531
  try {
524
- J(), Object.entries(Q).forEach(([e, t]) => ee(e, t)), L({ signal: A })?.catch?.(X);
532
+ J(), Object.entries(Q).forEach(([e, t]) => ee(e, t)), L({ signal: A ?? void 0 })?.catch?.(X);
525
533
  } catch (e) {
526
534
  X(e);
527
535
  }
@@ -532,26 +540,26 @@ var s = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t
532
540
  J,
533
541
  X,
534
542
  A
535
- ]), de = () => {
536
- !g && _ && (F.current.style.outline = _);
537
- }, fe = () => {
538
- !g && _ && (F.current.style.outline = "none");
539
- };
543
+ ]), de = n(() => {
544
+ !g && _ && F.current && (F.current.style.outline = _);
545
+ }, [g, _]), fe = n(() => {
546
+ !g && _ && F.current && (F.current.style.outline = "none");
547
+ }, [g, _]);
540
548
  return I ? T(r) ? r($, Y, z) : t(r) ? e(r, { ...!z && { onClick: $ } }) : /* @__PURE__ */ (0, j.jsx)("button", {
541
549
  "data-testid": "__vocal-root__",
542
550
  ref: F,
543
551
  "aria-label": m,
544
552
  "aria-pressed": z,
545
- style: g ? null : {
553
+ style: g ? void 0 : {
546
554
  width: 24,
547
555
  height: 24,
548
556
  backgroundColor: "transparent",
549
557
  border: "none",
550
558
  padding: 0,
551
559
  cursor: !p && z ? "default" : "pointer",
552
- ...h
560
+ ...h ?? {}
553
561
  },
554
- className: g,
562
+ className: g ?? void 0,
555
563
  onFocus: de,
556
564
  onBlur: fe,
557
565
  onClick: z ? Y : $,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@untemps/react-vocal",
3
- "version": "2.0.0-beta.13",
3
+ "version": "2.0.0-beta.15",
4
4
  "author": "Vincent Le Badezet <v.lebadezet@untemps.net>",
5
5
  "repository": "git@github.com:untemps/react-vocal.git",
6
6
  "license": "MIT",
@@ -21,54 +21,71 @@
21
21
  "access": "public"
22
22
  },
23
23
  "files": [
24
- "dist/index.cjs",
25
- "dist/index.es.js",
24
+ "dist/*.cjs",
25
+ "dist/*.es.js",
26
+ "dist/*.d.ts",
26
27
  "CHANGELOG.md"
27
28
  ],
28
29
  "type": "module",
29
30
  "main": "dist/index.cjs",
30
31
  "module": "dist/index.es.js",
32
+ "types": "dist/index.d.ts",
31
33
  "sideEffects": false,
32
34
  "exports": {
33
35
  ".": {
36
+ "types": "./dist/index.d.ts",
34
37
  "import": "./dist/index.es.js",
35
38
  "require": "./dist/index.cjs",
36
39
  "default": "./dist/index.es.js"
37
40
  }
38
41
  },
39
42
  "engines": {
40
- "node": "^20.19.0 || >=22.12.0"
43
+ "node": ">=22"
41
44
  },
42
45
  "devDependencies": {
43
46
  "@commitlint/cli": "^20.5.3",
44
- "fuse.js": "^7.3.0",
45
47
  "@commitlint/config-conventional": "^20.5.3",
48
+ "@eslint/js": "^10.0.1",
49
+ "@microsoft/api-extractor": "^7.58.7",
46
50
  "@semantic-release/changelog": "^6.0.3",
47
51
  "@semantic-release/git": "^10.0.1",
48
52
  "@semantic-release/github": "^12.0.6",
49
53
  "@testing-library/dom": "^10.4.1",
50
54
  "@testing-library/jest-dom": "^6.9.1",
51
55
  "@testing-library/react": "^16.3.2",
56
+ "@types/react": "^19.2.15",
57
+ "@types/react-dom": "^19.2.3",
52
58
  "@untemps/utils": "^3.2.0",
53
59
  "@vitejs/plugin-react": "^6.0.1",
54
60
  "@vitest/coverage-v8": "^4.1.5",
61
+ "eslint": "^10.4.0",
62
+ "eslint-config-prettier": "^10.1.8",
63
+ "fuse.js": "^7.3.0",
64
+ "globals": "^17.6.0",
55
65
  "husky": "^9.1.7",
56
66
  "jsdom": "^29.1.1",
57
67
  "prettier": "^3.8.3",
58
68
  "react": "^19.2.5",
59
69
  "react-dom": "^19.2.5",
60
70
  "semantic-release": "^25.0.3",
71
+ "typescript": "^6.0.3",
72
+ "typescript-eslint": "^8.59.4",
61
73
  "vite": "^8.0.10",
74
+ "vite-plugin-dts": "^5.0.1",
62
75
  "vitest": "^4.1.5"
63
76
  },
64
77
  "peerDependencies": {
65
78
  "fuse.js": ">=6.0.0",
66
79
  "react": ">=16.13.1",
67
- "react-dom": ">=16.13.1"
80
+ "react-dom": ">=16.13.1",
81
+ "typescript": ">=6.0.0"
68
82
  },
69
83
  "peerDependenciesMeta": {
70
84
  "fuse.js": {
71
85
  "optional": true
86
+ },
87
+ "typescript": {
88
+ "optional": true
72
89
  }
73
90
  },
74
91
  "dependencies": {
@@ -109,6 +126,10 @@
109
126
  {
110
127
  "path": "dist/index.es.js",
111
128
  "label": "ES distribution"
129
+ },
130
+ {
131
+ "path": "dist/index.d.ts",
132
+ "label": "TypeScript declarations"
112
133
  }
113
134
  ]
114
135
  }
@@ -119,8 +140,10 @@
119
140
  "dev": "cd dev && yarn && yarn dev",
120
141
  "test": "vitest",
121
142
  "test:ci": "vitest run --coverage",
143
+ "typecheck": "tsc --noEmit",
144
+ "lint": "eslint src vitest.setup.ts",
122
145
  "build": "vite build",
123
146
  "prepare": "husky",
124
- "prettier": "prettier \"*/**/*.js\" --ignore-path ./.prettierignore --write && git add -u && git status"
147
+ "prettier": "prettier \"*/**/*.{js,ts,tsx}\" --ignore-path ./.prettierignore --write && git add -u && git status"
125
148
  }
126
149
  }