@pipeup/voice 0.9.1 → 1.0.0

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/voice.esm.js CHANGED
@@ -1,8 +1,8 @@
1
- async function z(n,b={}){let w=new Map,R={get:async y=>w.get(y),set:async(y,d)=>void w.set(y,d),lasting:!1};if(b.memory||typeof indexedDB>"u")return R;try{let y=await new Promise((m,u)=>{let l=setTimeout(()=>u(new Error("timed out")),4e3),c=indexedDB.open(`pipeup-addon-${n}`,1);c.onupgradeneeded=()=>c.result.createObjectStore("kv"),c.onsuccess=()=>{clearTimeout(l),m(c.result)},c.onerror=c.onblocked=()=>{clearTimeout(l),u(c.error??new Error("blocked"))}}),d=(m,u)=>new Promise((l,c)=>{let v=u(y.transaction("kv",m).objectStore("kv"));v.onsuccess=()=>l(v.result),v.onerror=()=>c(v.error)});return{get:m=>d("readonly",u=>u.get(m)),set:async(m,u)=>void await d("readwrite",l=>l.put(u,m)),lasting:!0}}catch{return R}}var Ce=2n**256n-2n**32n-977n;var oe=6e4,ie=["en-US","en-GB","es-ES","fr-FR","de-DE","it-IT","pt-BR","nl-NL","ja-JP","ko-KR","zh-CN"],se=["M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z","M5.5 11.5a6.5 6.5 0 0 0 13 0M12 18v3M9 21h6"],ae="Your words are worked out on this device. Nothing is sent.",ce={speech:"Your voice is sent to the browser's speech service to be turned into words. Pipeup doesn't keep it.",webkit:"Safari uses Apple's speech recognition. Depending on the language and device the words are worked out here or your voice is sent to Apple; Pipeup can't tell which, and doesn't keep it."},de="The microphone is blocked. Allow it for this page, then press the microphone again.",le={"not-allowed":{speech:de,webkit:"The microphone or speech recognition was refused for this page. Allow it, reload the page, then press the microphone again."},"service-not-allowed":{speech:"This browser's speech service isn't available for that language, or is turned off in its settings.",webkit:"Safari isn't allowed to use Apple's speech recognition. Turn on Siri or Dictation, allow Safari under System Settings \u203A Privacy & Security \u203A Speech Recognition, then reload."},"no-speech":{speech:"Didn't hear anything. Press the microphone to try again.",webkit:"Didn't hear anything. Press the microphone to try again."},network:{speech:"The browser's speech service couldn't be reached. Check your connection and try again.",webkit:"Apple's speech service couldn't be reached. Check your connection and try again."},"audio-capture":{speech:"No microphone was found.",webkit:"No microphone was found."},"language-not-supported":{speech:"This browser can't dictate in that language.",webkit:"Safari can't dictate in that language."}},pe=`.voice-p{margin:0 0 10px}.voice-p.voice-swap{animation:voice-in .25s ease}
1
+ async function F(n,m={}){let b=m.keep;if(b)return{get:async s=>await b.get(s),set:(s,w)=>b.set(s,w),lasting:!0};let M=new Map,k={get:async s=>M.get(s),set:async(s,w)=>void M.set(s,w),lasting:!1};if(m.memory||typeof indexedDB>"u")return k;try{let s=await new Promise((h,c)=>{let y=setTimeout(()=>c(new Error("timed out")),4e3),p=indexedDB.open(`pipeup-addon-${n}`,1);p.onupgradeneeded=()=>p.result.createObjectStore("kv"),p.onsuccess=()=>{clearTimeout(y),h(p.result)},p.onerror=p.onblocked=()=>{clearTimeout(y),c(p.error??new Error("blocked"))}}),w=(h,c)=>new Promise((y,p)=>{let P=c(s.transaction("kv",h).objectStore("kv"));P.onsuccess=()=>y(P.result),P.onerror=()=>p(P.error)});return{get:h=>w("readonly",c=>c.get(h)),set:async(h,c)=>void await w("readwrite",y=>y.put(c,h)),lasting:!0}}catch{return k}}var Ce=2n**256n-2n**32n-977n;var oe=6e4,ie=["en-US","en-GB","es-ES","fr-FR","de-DE","it-IT","pt-BR","nl-NL","ja-JP","ko-KR","zh-CN"],se=["M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z","M5.5 11.5a6.5 6.5 0 0 0 13 0M12 18v3M9 21h6"],ae="Your words are worked out on this device. Nothing is sent.",ce={speech:"Your voice is sent to the browser's speech service to be turned into words. Pipeup doesn't keep it.",webkit:"Safari uses Apple's speech recognition. Depending on the language and device the words are worked out here or your voice is sent to Apple; Pipeup can't tell which, and doesn't keep it."},de="The microphone is blocked. Allow it for this page, then press the microphone again.",pe={"not-allowed":{speech:de,webkit:"The microphone or speech recognition was refused for this page. Allow it, reload the page, then press the microphone again."},"service-not-allowed":{speech:"This browser's speech service isn't available for that language, or is turned off in its settings.",webkit:"Safari isn't allowed to use Apple's speech recognition. Turn on Siri or Dictation, allow Safari under System Settings \u203A Privacy & Security \u203A Speech Recognition, then reload."},"no-speech":{speech:"Didn't hear anything. Press the microphone to try again.",webkit:"Didn't hear anything. Press the microphone to try again."},network:{speech:"The browser's speech service couldn't be reached. Check your connection and try again.",webkit:"Apple's speech service couldn't be reached. Check your connection and try again."},"audio-capture":{speech:"No microphone was found.",webkit:"No microphone was found."},"language-not-supported":{speech:"This browser can't dictate in that language.",webkit:"Safari can't dictate in that language."}},le=`.voice-p{margin:0 0 10px}.voice-p.voice-swap{animation:voice-in .25s ease}
2
2
  @keyframes voice-in{from{opacity:0}to{opacity:1}}
3
3
  .voice-row{display:flex;gap:8px;align-items:center;margin:0 0 10px}
4
4
  .voice-row select{font:inherit;color:var(--pu-text);background:var(--pu-surface);border:1px solid var(--pu-line);border-radius:8px;padding:4px 6px}
5
5
  .voice-note{color:var(--pu-faint)}.voice-btns{display:flex;gap:8px}
6
6
  .voice-btns button{font:inherit;color:inherit;background:none;cursor:pointer;padding:6px 12px;border:1px solid var(--pu-line);border-radius:8px;transition:background-color .2s var(--pu-ease),opacity .2s var(--pu-ease)}
7
7
  .voice-btns button:hover,.voice-btns button:focus-visible{background:var(--pu-hover);outline:none}
8
- .voice-btns button:disabled{opacity:.5;cursor:default}.voice-btns .voice-start{color:var(--pu-accent)}`,ue=()=>{let n=globalThis;return n.SpeechRecognition||n.webkitSpeechRecognition},J=n=>n.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||navigator.language||"en-US";function F(n){try{return new Intl.DisplayNames(["en"],{type:"language",languageDisplay:"standard"}).of(n)||n}catch{return n}}function fe(){return{id:"voice",api:1,version:"0.4.1",needs:["composer","panel","notify","styles"],network:{when:"after-consent",to:["the browser's speech service","the browser maker's speech pack download"],says:"Only when you press the microphone: your browser may download a speech pack once, and, only if it can't work on this device, your voice goes to the browser maker's speech service."},setup(n){let b=ue();if(!b)return n.off("this browser has no speech engine");let w=globalThis.SpeechRecognition?"speech":"webkit",R=n.addStyles(pe),y,d,m=!1,u=z("voice",{memory:n.ephemeral}).then(async r=>{y=r,d=await r.get("choice").catch(()=>{}),m=!0}),l=new Map,c=async r=>{let p="service";try{if(b.available){let f=await b.available({langs:[r],processLocally:!0});f==="available"?p="device":f==="downloadable"||f==="downloading"?p="download":await b.available({langs:[r],processLocally:!1})==="unavailable"&&(p="none")}}catch{}return l.set(r,p),p},v=()=>d?.lang||J(n.root),W=r=>!!d&&d.engine===w&&(r==="device"||r==="service"&&d.mode==="service"),$,q=(r,p)=>new Promise(f=>{let t=r,o=p,g=!1,s=(e,i,O="")=>{let T=document.createElement(e);return T.className=i,T.textContent=O,T},a=s("p","voice-p"),C=()=>o==="download"?`Your browser will first download a speech pack for ${F(t)}, once. Your words stay on this device.`:o==="none"?`This browser can't dictate in ${F(t)} on this device. Choose another language.`:o==="device"?ae:ce[w],D=e=>{a.textContent=e,a.classList.remove("voice-swap"),a.offsetWidth,a.classList.add("voice-swap")};a.textContent=C();let h=document.createElement("select"),L=()=>{A.disabled=o==="none"};h.setAttribute("aria-label","Language");for(let e of new Set([r,...ie])){let i=s("option","",F(e));i.value=e,h.append(i)}h.value=t;let E=s("div","voice-row"),_=s("label","","Language");E.append(_,h);let A=s("button","voice-start","Start"),x=s("button","","Not now");A.type=x.type="button";let S=s("div","voice-btns");S.append(A,x);let k=s("div","");k.append(a,E),y&&!y.lasting&&k.append(s("p","voice-p voice-note","This browser won't remember this choice.")),k.append(S);let P=e=>{g||(g=!0,f(e),$?.close())};L();let U=0;h.onchange=async()=>{let e=++U;t=h.value,A.disabled=!0;let i=l.get(t)??await c(t);e===U&&(o=i,D(C()),L())},x.onclick=()=>P(null),A.onclick=async()=>{if(o==="download"){A.disabled=x.disabled=h.disabled=!0,D("Downloading the speech pack\u2026");let e=!1;try{e=await b.install?.({langs:[t],processLocally:!0})===!0}catch{}return e?l.set(t,"device"):n.notify("The speech pack couldn't be downloaded. Try again later."),P(e?{lang:t,w:"device"}:null)}P({lang:t,w:o})},$=n.openPanel(k,{label:"Dictate",onClose:()=>P(null)})}),B,N=!1,j=(r,p,f)=>{let t;try{t=new b}catch{return n.notify("Dictation couldn't start in this browser.")}try{t.lang=p,t.interimResults=!0,t.continuous=!0,f&&(t.processLocally=!0)}catch{return n.notify("Dictation couldn't start in this browser.")}let o=r.dictate(),g=!!r.text()&&!/\s$/.test(r.text()),s="",a="",C=!1,D=!1,h=!1,L=!1,E,_=setInterval(()=>{r.connected()||S(!0)},500),A=r.onEnd(()=>S(!0)),x=()=>{clearTimeout(E),E=setTimeout(()=>S(!1),oe)},S=e=>{D=!0,e&&(h=!0,s="",o.cancel());try{e?t.abort():t.stop()}catch{}},k=e=>e.trim()?(g?" ":"")+e.trim():"",P=e=>{let i=e.trim();return w!=="webkit"||!a?i:i===a?"":i.startsWith(`${a} `)?i.slice(a.length).trim():i};t.onstart=()=>{C=!0,x(),n.announce("Listening")},t.onspeechstart=x,t.onresult=e=>{if(h)return;x();let i="";for(let O=e.resultIndex;O<e.results.length;O++){let T=e.results[O];if(T.isFinal){let M=P(T[0].transcript);M&&(a=a?`${a} ${M}`:M,o.commit(k(M)),g=!0)}else i+=T[0].transcript}s=P(i),s&&o.update(k(s))};let U=()=>{L||(L=!0,clearInterval(_),clearTimeout(E),A(),!h&&s.trim()&&o.commit(k(s)),B=void 0,r.setPressed(!1),C&&n.announce("Stopped listening"))};t.onerror=e=>{if(!(e.error==="aborted"&&D)){let i=e.error==="aborted"&&e.message?.trim();n.notify(i?`Dictation stopped: ${i}`:le[e.error]?.[w]||"Dictation stopped.")}U()},t.onend=U,B={stop:()=>S(!1),abort:()=>S(!0)},r.setPressed(!0);try{t.start()}catch{n.notify("Dictation couldn't start. Try again."),U()}},Y=r=>{if(B)return B.stop();if(N)return;let p=v(),f=l.get(p);if(m&&f&&W(f))return j(r,p,f==="device");N=!0,(async()=>{try{await u;let t=v(),o={lang:t,w:l.get(t)??await c(t)};if(!W(o.w)){let g=await q(o.lang,o.w);if(!g)return;o=g,d={engine:w,mode:g.w==="service"?"service":"device",...g.lang!==J(n.root)&&{lang:g.lang}},await y?.set("choice",d).catch(()=>{})}r.connected()&&!B&&j(r,o.lang,o.w==="device")}finally{N=!1}})()},G=!1,V=()=>{G||(G=!0,u.then(()=>c(v())))},X=n.addComposerTool({id:"voice",icon:se,label:"Dictate",available:()=>(V(),!0),press:Y});return()=>{B?.abort(),$?.close(),X(),R()}}}}export{fe as default};
8
+ .voice-btns button:disabled{opacity:.5;cursor:default}.voice-btns .voice-start{color:var(--pu-accent)}`,ue=()=>{let n=globalThis;return n.SpeechRecognition||n.webkitSpeechRecognition},J=n=>n.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||navigator.language||"en-US";function W(n){try{return new Intl.DisplayNames(["en"],{type:"language",languageDisplay:"standard"}).of(n)||n}catch{return n}}function fe(){return{id:"voice",api:1,version:"0.4.1",needs:["composer","panel","notify","styles"],network:{when:"after-consent",to:["the browser's speech service","the browser maker's speech pack download"],says:"Only when you press the microphone: your browser may download a speech pack once, and, only if it can't work on this device, your voice goes to the browser maker's speech service."},setup(n){let m=ue();if(!m)return n.off("this browser has no speech engine");let b=globalThis.SpeechRecognition?"speech":"webkit",M=n.addStyles(le),k,s,w=!1,h=F("voice",{memory:n.ephemeral,keep:n.keep}).then(async r=>{k=r,s=await r.get("choice").catch(()=>{}),w=!0}),c=new Map,y=async r=>{let l="service";try{if(m.available){let u=await m.available({langs:[r],processLocally:!0});u==="available"?l="device":u==="downloadable"||u==="downloading"?l="download":await m.available({langs:[r],processLocally:!1})==="unavailable"&&(l="none")}}catch{}return c.set(r,l),l},p=()=>s?.lang||J(n.root),P=r=>!!s&&s.engine===b&&(r==="device"||r==="service"&&s.mode==="service"),N,q=(r,l)=>new Promise(u=>{let t=r,o=l,f=!1,a=(e,i,R="")=>{let B=document.createElement(e);return B.className=i,B.textContent=R,B},d=a("p","voice-p"),D=()=>o==="download"?`Your browser will first download a speech pack for ${W(t)}, once. Your words stay on this device.`:o==="none"?`This browser can't dictate in ${W(t)} on this device. Choose another language.`:o==="device"?ae:ce[b],L=e=>{d.textContent=e,d.classList.remove("voice-swap"),d.offsetWidth,d.classList.add("voice-swap")};d.textContent=D();let g=document.createElement("select"),O=()=>{v.disabled=o==="none"};g.setAttribute("aria-label","Language");for(let e of new Set([r,...ie])){let i=a("option","",W(e));i.value=e,g.append(i)}g.value=t;let U=a("div","voice-row"),K=a("label","","Language");U.append(K,g);let v=a("button","voice-start","Start"),A=a("button","","Not now");v.type=A.type="button";let x=a("div","voice-btns");x.append(v,A);let S=a("div","");S.append(d,U),k&&!k.lasting&&S.append(a("p","voice-p voice-note","This browser won't remember this choice.")),S.append(x);let T=e=>{f||(f=!0,u(e),N?.close())};O();let C=0;g.onchange=async()=>{let e=++C;t=g.value,v.disabled=!0;let i=c.get(t)??await y(t);e===C&&(o=i,L(D()),O())},A.onclick=()=>T(null),v.onclick=async()=>{if(o==="download"){v.disabled=A.disabled=g.disabled=!0,L("Downloading the speech pack\u2026");let e=!1;try{e=await m.install?.({langs:[t],processLocally:!0})===!0}catch{}return e?c.set(t,"device"):n.notify("The speech pack couldn't be downloaded. Try again later."),T(e?{lang:t,w:"device"}:null)}T({lang:t,w:o})},N=n.openPanel(S,{label:"Dictate",onClose:()=>T(null)})}),E,_=!1,j=(r,l,u)=>{let t;try{t=new m}catch{return n.notify("Dictation couldn't start in this browser.")}try{t.lang=l,t.interimResults=!0,t.continuous=!0,u&&(t.processLocally=!0)}catch{return n.notify("Dictation couldn't start in this browser.")}let o=r.dictate(),f=!!r.text()&&!/\s$/.test(r.text()),a="",d="",D=!1,L=!1,g=!1,O=!1,U,K=setInterval(()=>{r.connected()||x(!0)},500),v=r.onEnd(()=>x(!0)),A=()=>{clearTimeout(U),U=setTimeout(()=>x(!1),oe)},x=e=>{L=!0,e&&(g=!0,a="",o.cancel());try{e?t.abort():t.stop()}catch{}},S=e=>e.trim()?(f?" ":"")+e.trim():"",T=e=>{let i=e.trim();return b!=="webkit"||!d?i:i===d?"":i.startsWith(`${d} `)?i.slice(d.length).trim():i};t.onstart=()=>{D=!0,A(),n.announce("Listening")},t.onspeechstart=A,t.onresult=e=>{if(g)return;A();let i="";for(let R=e.resultIndex;R<e.results.length;R++){let B=e.results[R];if(B.isFinal){let I=T(B[0].transcript);I&&(d=d?`${d} ${I}`:I,o.commit(S(I)),f=!0)}else i+=B[0].transcript}a=T(i),a&&o.update(S(a))};let C=()=>{O||(O=!0,clearInterval(K),clearTimeout(U),v(),!g&&a.trim()&&o.commit(S(a)),E=void 0,r.setPressed(!1),D&&n.announce("Stopped listening"))};t.onerror=e=>{if(!(e.error==="aborted"&&L)){let i=e.error==="aborted"&&e.message?.trim();n.notify(i?`Dictation stopped: ${i}`:pe[e.error]?.[b]||"Dictation stopped.")}C()},t.onend=C,E={stop:()=>x(!1),abort:()=>x(!0)},r.setPressed(!0);try{t.start()}catch{n.notify("Dictation couldn't start. Try again."),C()}},Y=r=>{if(E)return E.stop();if(_)return;let l=p(),u=c.get(l);if(w&&u&&P(u))return j(r,l,u==="device");_=!0,(async()=>{try{await h;let t=p(),o={lang:t,w:c.get(t)??await y(t)};if(!P(o.w)){let f=await q(o.lang,o.w);if(!f)return;o=f,s={engine:b,mode:f.w==="service"?"service":"device",...f.lang!==J(n.root)&&{lang:f.lang}},await k?.set("choice",s).catch(()=>{})}r.connected()&&!E&&j(r,o.lang,o.w==="device")}finally{_=!1}})()},G=!1,V=()=>{G||(G=!0,h.then(()=>y(p())))},X=n.addComposerTool({id:"voice",icon:se,label:"Dictate",available:()=>(V(),!0),press:Y});return()=>{E?.abort(),N?.close(),X(),M()}}}}export{fe as default};
package/dist/voice.min.js CHANGED
@@ -1,8 +1,8 @@
1
- "use strict";(()=>{function H(t){let u=globalThis;(u.pipeupAddons||=[]).push(t),typeof addEventListener=="function"&&addEventListener("load",()=>{u.Pipeup||console.error(`pipeup: the "${t.id}" add-on needs Pipeup on the page too; load pipeup.min.js.`)},{once:!0})}async function F(t,u={}){let w=new Map,R={get:async m=>w.get(m),set:async(m,d)=>void w.set(m,d),lasting:!1};if(u.memory||typeof indexedDB>"u")return R;try{let m=await new Promise((b,f)=>{let l=setTimeout(()=>f(new Error("timed out")),4e3),c=indexedDB.open(`pipeup-addon-${t}`,1);c.onupgradeneeded=()=>c.result.createObjectStore("kv"),c.onsuccess=()=>{clearTimeout(l),b(c.result)},c.onerror=c.onblocked=()=>{clearTimeout(l),f(c.error??new Error("blocked"))}}),d=(b,f)=>new Promise((l,c)=>{let v=f(m.transaction("kv",b).objectStore("kv"));v.onsuccess=()=>l(v.result),v.onerror=()=>c(v.error)});return{get:b=>d("readonly",f=>f.get(b)),set:async(b,f)=>void await d("readwrite",l=>l.put(f,b)),lasting:!0}}catch{return R}}var De=2n**256n-2n**32n-977n;var se=6e4,ae=["en-US","en-GB","es-ES","fr-FR","de-DE","it-IT","pt-BR","nl-NL","ja-JP","ko-KR","zh-CN"],ce=["M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z","M5.5 11.5a6.5 6.5 0 0 0 13 0M12 18v3M9 21h6"],de="Your words are worked out on this device. Nothing is sent.",le={speech:"Your voice is sent to the browser's speech service to be turned into words. Pipeup doesn't keep it.",webkit:"Safari uses Apple's speech recognition. Depending on the language and device the words are worked out here or your voice is sent to Apple; Pipeup can't tell which, and doesn't keep it."},pe="The microphone is blocked. Allow it for this page, then press the microphone again.",ue={"not-allowed":{speech:pe,webkit:"The microphone or speech recognition was refused for this page. Allow it, reload the page, then press the microphone again."},"service-not-allowed":{speech:"This browser's speech service isn't available for that language, or is turned off in its settings.",webkit:"Safari isn't allowed to use Apple's speech recognition. Turn on Siri or Dictation, allow Safari under System Settings \u203A Privacy & Security \u203A Speech Recognition, then reload."},"no-speech":{speech:"Didn't hear anything. Press the microphone to try again.",webkit:"Didn't hear anything. Press the microphone to try again."},network:{speech:"The browser's speech service couldn't be reached. Check your connection and try again.",webkit:"Apple's speech service couldn't be reached. Check your connection and try again."},"audio-capture":{speech:"No microphone was found.",webkit:"No microphone was found."},"language-not-supported":{speech:"This browser can't dictate in that language.",webkit:"Safari can't dictate in that language."}},fe=`.voice-p{margin:0 0 10px}.voice-p.voice-swap{animation:voice-in .25s ease}
1
+ "use strict";(()=>{function H(t){let l=globalThis;(l.pipeupAddons||=[]).push(t),typeof addEventListener=="function"&&addEventListener("load",()=>{l.Pipeup||console.error(`pipeup: the "${t.id}" add-on needs Pipeup on the page too; load pipeup.min.js.`)},{once:!0})}async function W(t,l={}){let b=l.keep;if(b)return{get:async s=>await b.get(s),set:(s,w)=>b.set(s,w),lasting:!0};let M=new Map,k={get:async s=>M.get(s),set:async(s,w)=>void M.set(s,w),lasting:!1};if(l.memory||typeof indexedDB>"u")return k;try{let s=await new Promise((y,c)=>{let m=setTimeout(()=>c(new Error("timed out")),4e3),p=indexedDB.open(`pipeup-addon-${t}`,1);p.onupgradeneeded=()=>p.result.createObjectStore("kv"),p.onsuccess=()=>{clearTimeout(m),y(p.result)},p.onerror=p.onblocked=()=>{clearTimeout(m),c(p.error??new Error("blocked"))}}),w=(y,c)=>new Promise((m,p)=>{let P=c(s.transaction("kv",y).objectStore("kv"));P.onsuccess=()=>m(P.result),P.onerror=()=>p(P.error)});return{get:y=>w("readonly",c=>c.get(y)),set:async(y,c)=>void await w("readwrite",m=>m.put(c,y)),lasting:!0}}catch{return k}}var De=2n**256n-2n**32n-977n;var se=6e4,ae=["en-US","en-GB","es-ES","fr-FR","de-DE","it-IT","pt-BR","nl-NL","ja-JP","ko-KR","zh-CN"],ce=["M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z","M5.5 11.5a6.5 6.5 0 0 0 13 0M12 18v3M9 21h6"],de="Your words are worked out on this device. Nothing is sent.",pe={speech:"Your voice is sent to the browser's speech service to be turned into words. Pipeup doesn't keep it.",webkit:"Safari uses Apple's speech recognition. Depending on the language and device the words are worked out here or your voice is sent to Apple; Pipeup can't tell which, and doesn't keep it."},le="The microphone is blocked. Allow it for this page, then press the microphone again.",ue={"not-allowed":{speech:le,webkit:"The microphone or speech recognition was refused for this page. Allow it, reload the page, then press the microphone again."},"service-not-allowed":{speech:"This browser's speech service isn't available for that language, or is turned off in its settings.",webkit:"Safari isn't allowed to use Apple's speech recognition. Turn on Siri or Dictation, allow Safari under System Settings \u203A Privacy & Security \u203A Speech Recognition, then reload."},"no-speech":{speech:"Didn't hear anything. Press the microphone to try again.",webkit:"Didn't hear anything. Press the microphone to try again."},network:{speech:"The browser's speech service couldn't be reached. Check your connection and try again.",webkit:"Apple's speech service couldn't be reached. Check your connection and try again."},"audio-capture":{speech:"No microphone was found.",webkit:"No microphone was found."},"language-not-supported":{speech:"This browser can't dictate in that language.",webkit:"Safari can't dictate in that language."}},fe=`.voice-p{margin:0 0 10px}.voice-p.voice-swap{animation:voice-in .25s ease}
2
2
  @keyframes voice-in{from{opacity:0}to{opacity:1}}
3
3
  .voice-row{display:flex;gap:8px;align-items:center;margin:0 0 10px}
4
4
  .voice-row select{font:inherit;color:var(--pu-text);background:var(--pu-surface);border:1px solid var(--pu-line);border-radius:8px;padding:4px 6px}
5
5
  .voice-note{color:var(--pu-faint)}.voice-btns{display:flex;gap:8px}
6
6
  .voice-btns button{font:inherit;color:inherit;background:none;cursor:pointer;padding:6px 12px;border:1px solid var(--pu-line);border-radius:8px;transition:background-color .2s var(--pu-ease),opacity .2s var(--pu-ease)}
7
7
  .voice-btns button:hover,.voice-btns button:focus-visible{background:var(--pu-hover);outline:none}
8
- .voice-btns button:disabled{opacity:.5;cursor:default}.voice-btns .voice-start{color:var(--pu-accent)}`,ge=()=>{let t=globalThis;return t.SpeechRecognition||t.webkitSpeechRecognition},q=t=>t.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||navigator.language||"en-US";function W(t){try{return new Intl.DisplayNames(["en"],{type:"language",languageDisplay:"standard"}).of(t)||t}catch{return t}}function Y(){return{id:"voice",api:1,version:"0.4.1",needs:["composer","panel","notify","styles"],network:{when:"after-consent",to:["the browser's speech service","the browser maker's speech pack download"],says:"Only when you press the microphone: your browser may download a speech pack once, and, only if it can't work on this device, your voice goes to the browser maker's speech service."},setup(t){let u=ge();if(!u)return t.off("this browser has no speech engine");let w=globalThis.SpeechRecognition?"speech":"webkit",R=t.addStyles(fe),m,d,b=!1,f=F("voice",{memory:t.ephemeral}).then(async r=>{m=r,d=await r.get("choice").catch(()=>{}),b=!0}),l=new Map,c=async r=>{let p="service";try{if(u.available){let g=await u.available({langs:[r],processLocally:!0});g==="available"?p="device":g==="downloadable"||g==="downloading"?p="download":await u.available({langs:[r],processLocally:!1})==="unavailable"&&(p="none")}}catch{}return l.set(r,p),p},v=()=>d?.lang||q(t.root),j=r=>!!d&&d.engine===w&&(r==="device"||r==="service"&&d.mode==="service"),$,V=(r,p)=>new Promise(g=>{let n=r,o=p,h=!1,s=(e,i,O="")=>{let T=document.createElement(e);return T.className=i,T.textContent=O,T},a=s("p","voice-p"),C=()=>o==="download"?`Your browser will first download a speech pack for ${W(n)}, once. Your words stay on this device.`:o==="none"?`This browser can't dictate in ${W(n)} on this device. Choose another language.`:o==="device"?de:le[w],D=e=>{a.textContent=e,a.classList.remove("voice-swap"),a.offsetWidth,a.classList.add("voice-swap")};a.textContent=C();let y=document.createElement("select"),L=()=>{A.disabled=o==="none"};y.setAttribute("aria-label","Language");for(let e of new Set([r,...ae])){let i=s("option","",W(e));i.value=e,y.append(i)}y.value=n;let E=s("div","voice-row"),_=s("label","","Language");E.append(_,y);let A=s("button","voice-start","Start"),x=s("button","","Not now");A.type=x.type="button";let S=s("div","voice-btns");S.append(A,x);let k=s("div","");k.append(a,E),m&&!m.lasting&&k.append(s("p","voice-p voice-note","This browser won't remember this choice.")),k.append(S);let P=e=>{h||(h=!0,g(e),$?.close())};L();let U=0;y.onchange=async()=>{let e=++U;n=y.value,A.disabled=!0;let i=l.get(n)??await c(n);e===U&&(o=i,D(C()),L())},x.onclick=()=>P(null),A.onclick=async()=>{if(o==="download"){A.disabled=x.disabled=y.disabled=!0,D("Downloading the speech pack\u2026");let e=!1;try{e=await u.install?.({langs:[n],processLocally:!0})===!0}catch{}return e?l.set(n,"device"):t.notify("The speech pack couldn't be downloaded. Try again later."),P(e?{lang:n,w:"device"}:null)}P({lang:n,w:o})},$=t.openPanel(k,{label:"Dictate",onClose:()=>P(null)})}),B,N=!1,G=(r,p,g)=>{let n;try{n=new u}catch{return t.notify("Dictation couldn't start in this browser.")}try{n.lang=p,n.interimResults=!0,n.continuous=!0,g&&(n.processLocally=!0)}catch{return t.notify("Dictation couldn't start in this browser.")}let o=r.dictate(),h=!!r.text()&&!/\s$/.test(r.text()),s="",a="",C=!1,D=!1,y=!1,L=!1,E,_=setInterval(()=>{r.connected()||S(!0)},500),A=r.onEnd(()=>S(!0)),x=()=>{clearTimeout(E),E=setTimeout(()=>S(!1),se)},S=e=>{D=!0,e&&(y=!0,s="",o.cancel());try{e?n.abort():n.stop()}catch{}},k=e=>e.trim()?(h?" ":"")+e.trim():"",P=e=>{let i=e.trim();return w!=="webkit"||!a?i:i===a?"":i.startsWith(`${a} `)?i.slice(a.length).trim():i};n.onstart=()=>{C=!0,x(),t.announce("Listening")},n.onspeechstart=x,n.onresult=e=>{if(y)return;x();let i="";for(let O=e.resultIndex;O<e.results.length;O++){let T=e.results[O];if(T.isFinal){let M=P(T[0].transcript);M&&(a=a?`${a} ${M}`:M,o.commit(k(M)),h=!0)}else i+=T[0].transcript}s=P(i),s&&o.update(k(s))};let U=()=>{L||(L=!0,clearInterval(_),clearTimeout(E),A(),!y&&s.trim()&&o.commit(k(s)),B=void 0,r.setPressed(!1),C&&t.announce("Stopped listening"))};n.onerror=e=>{if(!(e.error==="aborted"&&D)){let i=e.error==="aborted"&&e.message?.trim();t.notify(i?`Dictation stopped: ${i}`:ue[e.error]?.[w]||"Dictation stopped.")}U()},n.onend=U,B={stop:()=>S(!1),abort:()=>S(!0)},r.setPressed(!0);try{n.start()}catch{t.notify("Dictation couldn't start. Try again."),U()}},X=r=>{if(B)return B.stop();if(N)return;let p=v(),g=l.get(p);if(b&&g&&j(g))return G(r,p,g==="device");N=!0,(async()=>{try{await f;let n=v(),o={lang:n,w:l.get(n)??await c(n)};if(!j(o.w)){let h=await V(o.lang,o.w);if(!h)return;o=h,d={engine:w,mode:h.w==="service"?"service":"device",...h.lang!==q(t.root)&&{lang:h.lang}},await m?.set("choice",d).catch(()=>{})}r.connected()&&!B&&G(r,o.lang,o.w==="device")}finally{N=!1}})()},J=!1,Z=()=>{J||(J=!0,f.then(()=>c(v())))},Q=t.addComposerTool({id:"voice",icon:ce,label:"Dictate",available:()=>(Z(),!0),press:X});return()=>{B?.abort(),$?.close(),Q(),R()}}}}H(Y());})();
8
+ .voice-btns button:disabled{opacity:.5;cursor:default}.voice-btns .voice-start{color:var(--pu-accent)}`,ge=()=>{let t=globalThis;return t.SpeechRecognition||t.webkitSpeechRecognition},q=t=>t.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||navigator.language||"en-US";function j(t){try{return new Intl.DisplayNames(["en"],{type:"language",languageDisplay:"standard"}).of(t)||t}catch{return t}}function Y(){return{id:"voice",api:1,version:"0.4.1",needs:["composer","panel","notify","styles"],network:{when:"after-consent",to:["the browser's speech service","the browser maker's speech pack download"],says:"Only when you press the microphone: your browser may download a speech pack once, and, only if it can't work on this device, your voice goes to the browser maker's speech service."},setup(t){let l=ge();if(!l)return t.off("this browser has no speech engine");let b=globalThis.SpeechRecognition?"speech":"webkit",M=t.addStyles(fe),k,s,w=!1,y=W("voice",{memory:t.ephemeral,keep:t.keep}).then(async r=>{k=r,s=await r.get("choice").catch(()=>{}),w=!0}),c=new Map,m=async r=>{let u="service";try{if(l.available){let f=await l.available({langs:[r],processLocally:!0});f==="available"?u="device":f==="downloadable"||f==="downloading"?u="download":await l.available({langs:[r],processLocally:!1})==="unavailable"&&(u="none")}}catch{}return c.set(r,u),u},p=()=>s?.lang||q(t.root),P=r=>!!s&&s.engine===b&&(r==="device"||r==="service"&&s.mode==="service"),N,V=(r,u)=>new Promise(f=>{let n=r,o=u,g=!1,a=(e,i,R="")=>{let B=document.createElement(e);return B.className=i,B.textContent=R,B},d=a("p","voice-p"),D=()=>o==="download"?`Your browser will first download a speech pack for ${j(n)}, once. Your words stay on this device.`:o==="none"?`This browser can't dictate in ${j(n)} on this device. Choose another language.`:o==="device"?de:pe[b],L=e=>{d.textContent=e,d.classList.remove("voice-swap"),d.offsetWidth,d.classList.add("voice-swap")};d.textContent=D();let h=document.createElement("select"),O=()=>{v.disabled=o==="none"};h.setAttribute("aria-label","Language");for(let e of new Set([r,...ae])){let i=a("option","",j(e));i.value=e,h.append(i)}h.value=n;let U=a("div","voice-row"),K=a("label","","Language");U.append(K,h);let v=a("button","voice-start","Start"),A=a("button","","Not now");v.type=A.type="button";let x=a("div","voice-btns");x.append(v,A);let S=a("div","");S.append(d,U),k&&!k.lasting&&S.append(a("p","voice-p voice-note","This browser won't remember this choice.")),S.append(x);let T=e=>{g||(g=!0,f(e),N?.close())};O();let C=0;h.onchange=async()=>{let e=++C;n=h.value,v.disabled=!0;let i=c.get(n)??await m(n);e===C&&(o=i,L(D()),O())},A.onclick=()=>T(null),v.onclick=async()=>{if(o==="download"){v.disabled=A.disabled=h.disabled=!0,L("Downloading the speech pack\u2026");let e=!1;try{e=await l.install?.({langs:[n],processLocally:!0})===!0}catch{}return e?c.set(n,"device"):t.notify("The speech pack couldn't be downloaded. Try again later."),T(e?{lang:n,w:"device"}:null)}T({lang:n,w:o})},N=t.openPanel(S,{label:"Dictate",onClose:()=>T(null)})}),E,_=!1,G=(r,u,f)=>{let n;try{n=new l}catch{return t.notify("Dictation couldn't start in this browser.")}try{n.lang=u,n.interimResults=!0,n.continuous=!0,f&&(n.processLocally=!0)}catch{return t.notify("Dictation couldn't start in this browser.")}let o=r.dictate(),g=!!r.text()&&!/\s$/.test(r.text()),a="",d="",D=!1,L=!1,h=!1,O=!1,U,K=setInterval(()=>{r.connected()||x(!0)},500),v=r.onEnd(()=>x(!0)),A=()=>{clearTimeout(U),U=setTimeout(()=>x(!1),se)},x=e=>{L=!0,e&&(h=!0,a="",o.cancel());try{e?n.abort():n.stop()}catch{}},S=e=>e.trim()?(g?" ":"")+e.trim():"",T=e=>{let i=e.trim();return b!=="webkit"||!d?i:i===d?"":i.startsWith(`${d} `)?i.slice(d.length).trim():i};n.onstart=()=>{D=!0,A(),t.announce("Listening")},n.onspeechstart=A,n.onresult=e=>{if(h)return;A();let i="";for(let R=e.resultIndex;R<e.results.length;R++){let B=e.results[R];if(B.isFinal){let I=T(B[0].transcript);I&&(d=d?`${d} ${I}`:I,o.commit(S(I)),g=!0)}else i+=B[0].transcript}a=T(i),a&&o.update(S(a))};let C=()=>{O||(O=!0,clearInterval(K),clearTimeout(U),v(),!h&&a.trim()&&o.commit(S(a)),E=void 0,r.setPressed(!1),D&&t.announce("Stopped listening"))};n.onerror=e=>{if(!(e.error==="aborted"&&L)){let i=e.error==="aborted"&&e.message?.trim();t.notify(i?`Dictation stopped: ${i}`:ue[e.error]?.[b]||"Dictation stopped.")}C()},n.onend=C,E={stop:()=>x(!1),abort:()=>x(!0)},r.setPressed(!0);try{n.start()}catch{t.notify("Dictation couldn't start. Try again."),C()}},X=r=>{if(E)return E.stop();if(_)return;let u=p(),f=c.get(u);if(w&&f&&P(f))return G(r,u,f==="device");_=!0,(async()=>{try{await y;let n=p(),o={lang:n,w:c.get(n)??await m(n)};if(!P(o.w)){let g=await V(o.lang,o.w);if(!g)return;o=g,s={engine:b,mode:g.w==="service"?"service":"device",...g.lang!==q(t.root)&&{lang:g.lang}},await k?.set("choice",s).catch(()=>{})}r.connected()&&!E&&G(r,o.lang,o.w==="device")}finally{_=!1}})()},J=!1,Z=()=>{J||(J=!0,y.then(()=>m(p())))},Q=t.addComposerTool({id:"voice",icon:ce,label:"Dictate",available:()=>(Z(),!0),press:X});return()=>{E?.abort(),N?.close(),Q(),M()}}}}H(Y());})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipeup/voice",
3
- "version": "0.9.1",
3
+ "version": "1.0.0",
4
4
  "description": "Dictate Pipeup comments with the browser's own speech engine.",
5
5
  "license": "MIT",
6
6
  "author": "Pipeup contributors",
@@ -24,7 +24,7 @@
24
24
  "LICENSE"
25
25
  ],
26
26
  "peerDependencies": {
27
- "pipeup": "0.9.1"
27
+ "pipeup": "1.0.0"
28
28
  },
29
29
  "pipeup": {
30
30
  "network": {