@sphoro/bugcatch-web-sdk 0.20.0 → 0.21.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/README.md +39 -1
- package/dist/bugcatch-call.umd.js +1 -1
- package/dist/bugcatch-core.es.js +652 -497
- package/dist/bugcatch-core.es.js.map +1 -1
- package/dist/bugcatch-panel.es.js +1140 -938
- package/dist/bugcatch-panel.es.js.map +1 -1
- package/dist/bugcatch-panel.umd.js +237 -145
- package/dist/bugcatch-panel.umd.js.map +1 -1
- package/dist/bugcatch.standalone.umd.js +243 -151
- package/dist/bugcatch.standalone.umd.js.map +1 -1
- package/dist/bugcatch.umd.js +8 -8
- package/dist/bugcatch.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/feedback.js +94 -0
- package/src/identity.js +1 -1
- package/src/index.d.ts +56 -6
- package/src/index.js +111 -7
- package/src/panel/compose.js +1 -1
- package/src/panel/feedback.js +205 -0
- package/src/panel/icons.js +6 -0
- package/src/panel/index.js +83 -13
- package/src/panel/styles.js +47 -0
- package/src/widget.js +14 -1
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ BugCatch.init({
|
|
|
55
55
|
```html
|
|
56
56
|
<!-- Pin the version. Without one the CDN serves whatever is latest, so a
|
|
57
57
|
future release changes behaviour on a site nobody redeployed. -->
|
|
58
|
-
<script src="https://unpkg.com/@sphoro/bugcatch-web-sdk@0.
|
|
58
|
+
<script src="https://unpkg.com/@sphoro/bugcatch-web-sdk@0.21.0/dist/bugcatch.umd.js"></script>
|
|
59
59
|
<script>
|
|
60
60
|
BugCatch.init({ apiBase: '…', apiKey: 'bc_live_…', projectId: '…' });
|
|
61
61
|
</script>
|
|
@@ -381,6 +381,7 @@ On top of that:
|
|
|
381
381
|
| `drafts` | `true` | Recover an unfinished report on this device for seven days. |
|
|
382
382
|
| `agentIssues` | `true` | Show the one-tap questions the assistant answers by itself. Draws nothing until the project turns the agent on. |
|
|
383
383
|
| `knownIssues` | `true` | Show what this project already knows is broken, with "me too". Draws nothing until the project turns it on. |
|
|
384
|
+
| `feedback` | `true` | Offer **Give feedback** beside **Report a bug** in the panel, where the project has it on (Settings → Project → General, on by default). `false` hides it on this site; `feedback()` still sends. |
|
|
384
385
|
| `announcements` | `true` | Show what your team publishes under Settings → Project → Widget announcements, in the panel and beside the button. Draws nothing until one is published. `false` to show them your own way with `announcements()`. |
|
|
385
386
|
| `beforeSend` | — | Mutate the payload, or return `false` to cancel. |
|
|
386
387
|
| `updates` | `true` | Tell the reporter live when a report they filed moves. |
|
|
@@ -426,6 +427,8 @@ await BugCatch.agentIssues(); // one-tap questions the assistant ans
|
|
|
426
427
|
|
|
427
428
|
await BugCatch.knownIssues(); await BugCatch.matchKnownIssues(q, { signal }); await BugCatch.meToo(bugId);
|
|
428
429
|
await BugCatch.announcements(); BugCatch.dismissAnnouncement(a); // what your team is telling visitors
|
|
430
|
+
await BugCatch.feedbackEnabled(); await BugCatch.feedback({ message, kind, score }); // not a bug
|
|
431
|
+
BugCatch.open({ mode: 'feedback' }); // the panel, on the feedback form
|
|
429
432
|
|
|
430
433
|
await BugCatch.canCallNow(); await BugCatch.startCall(bugId?); BugCatch.endCall();
|
|
431
434
|
|
|
@@ -917,6 +920,41 @@ BugCatch.dismissAnnouncement(notices[0]);
|
|
|
917
920
|
`announcements()` is already narrowed to the visitor's day and to what this
|
|
918
921
|
browser has not closed, and resolves to an empty list on every failure.
|
|
919
922
|
|
|
923
|
+
### Feedback that is not a bug
|
|
924
|
+
|
|
925
|
+
The panel has two forms: **Report a bug** and **Give feedback**, switched at
|
|
926
|
+
the top. Feedback is for everything that is not "this is broken" — an idea,
|
|
927
|
+
praise, something that confused them — and is deliberately short: one box, an
|
|
928
|
+
optional kind (idea, praise, confusing, other), an optional 1–5 face for how
|
|
929
|
+
the product feels so far, and an address only when the page does not already
|
|
930
|
+
know one. Nobody is asked to sign in, even on a project that requires it for
|
|
931
|
+
reports.
|
|
932
|
+
|
|
933
|
+
It is not filed as a bug. Nothing analyses it, assigns it, holds it to an SLA
|
|
934
|
+
or emails anyone about it; it lands on the dashboard's **Feedback** page, where
|
|
935
|
+
the team reads it, writes back if there is an address, and archives it. It is
|
|
936
|
+
sent with the page and browser details, identify()'s user and your
|
|
937
|
+
setContext() context — never the console or network logs — and goes through
|
|
938
|
+
`beforeSend`, whose second argument is `{ type: 'feedback' }`.
|
|
939
|
+
|
|
940
|
+
On by default; a project switches it off under **Settings → Project →
|
|
941
|
+
General**, and the widget then stops drawing the switch.
|
|
942
|
+
|
|
943
|
+
```js
|
|
944
|
+
// Your own "Send feedback" menu item
|
|
945
|
+
BugCatch.open({ mode: 'feedback' });
|
|
946
|
+
|
|
947
|
+
// Or your own form (works with widget: false)
|
|
948
|
+
if (await BugCatch.feedbackEnabled()) {
|
|
949
|
+
await BugCatch.feedback({
|
|
950
|
+
message: 'Export to CSV would save me an hour a week',
|
|
951
|
+
kind: 'idea', // 'idea' | 'praise' | 'confusing' | 'other' — optional
|
|
952
|
+
score: 4, // 1–5, optional
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
BugCatch.on('feedback', ({ id, kind, score }) => analytics.track('feedback', { kind, score }));
|
|
956
|
+
```
|
|
957
|
+
|
|
920
958
|
### Talking a report through
|
|
921
959
|
|
|
922
960
|
Where the workspace has sphoro.voice configured, a reporter can talk to the team
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(){"use strict";function it(e=8e3){const r=window.AudioContext||window.webkitAudioContext;try{return new r({sampleRate:e})}catch{return new r}}async function st(e){if(e.state==="suspended")try{await e.resume()}catch{}}function lt(e,r,o){const a=r/o,s=new Float32Array(Math.floor(e.length/a));for(let f=0;f<s.length;f++){const l=Math.floor(f*a),m=Math.min(Math.floor((f+1)*a),e.length);let g=0;for(let i=l;i<m;i++)g+=e[i];s[f]=m>l?g/(m-l):0}return s}const ut=132,j=32635;function ft(e){const r=new Uint8Array(e.length);for(let o=0;o<e.length;o++){const a=Math.max(-1,Math.min(1,e[o]));let s=Math.round(a*32767);const f=s<0?128:0;f&&(s=-s),s>j&&(s=j),s+=ut;let l=7;for(let g=16384;!(s&g)&&l>0;l--,g>>=1);const m=s>>l+3&15;r[o]=~(f|l<<4|m)&255}return r}const dt=512;function pt(e,r,o,a=8e3){const s=e.createMediaStreamSource(r),f=e.createScriptProcessor(dt,1,1);f.onaudioprocess=m=>{const g=m.inputBuffer.getChannelData(0),i=e.sampleRate===a?g:lt(g,e.sampleRate,a);o(ft(i))},s.connect(f);const l=e.createGain();return l.gain.value=0,f.connect(l),l.connect(e.destination),{stop(){f.onaudioprocess=null;try{s.disconnect()}catch{}try{f.disconnect()}catch{}try{l.disconnect()}catch{}}}}const mt="voiceai.realtime.v1",yt=[{urls:"stun:stun.l.google.com:19302"}];function z(){return navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:1}})}function F(){var e;return typeof window<"u"&&typeof window.RTCPeerConnection=="function"&&!!((e=navigator.mediaDevices)!=null&&e.getUserMedia)}async function gt({realtimeUrl:e,stream:r,ringsUntil:o,onState:a,onAudioBlocked:s,onMicrophone:f}){const l=(t,n)=>{try{a==null||a(t,n)}catch{}},m=t=>{try{s==null||s(t)}catch{}},g=t=>{try{f==null||f(t)}catch{}};if(!F()){const t=new Error("[BugCatch] this browser cannot make live calls.");throw l("failed",{reason:"unsupported"}),t}let i=r;if(!i)try{i=await z()}catch(t){throw l("failed",{reason:"microphone",error:String((t==null?void 0:t.name)||t)}),t}let y;try{y=await ht(e)}catch(t){throw W(i),l("failed",{reason:"connect"}),t}let c=null,u=null,p=!1,w=null,h=null,E=null,S=!1,b=null,C=null,O=!1;const x=[];let M=!1,D=!1,P=!1,R=null,I=null,B=0;const Ot=2e3,K=5e3,_t=3e4,At=4,k=(t,n)=>{if(!p){p=!0,w&&(clearTimeout(w),w=null),et(),C==null||C(),C=null,Mt(),b==null||b(),b=null;try{c==null||c.close()}catch{}W(i),u&&(u.srcObject=null,u.remove(),u=null);try{y.readyState===WebSocket.OPEN&&y.close(1e3,"call over")}catch{}l(t,n)}},_=t=>{y.readyState===WebSocket.OPEN&&y.send(JSON.stringify(t))},q=async()=>{if(O||E||p||!h||!h.audio&&!h.transcript)return;O=!0;const t=h.encoding||"mulaw";if(t!=="mulaw"){V(`this call is kept as ${t}, which this SDK cannot produce.`);return}const n=Number(h.sample_rate)||8e3;try{const d=it(n);if(await st(d),p){try{d.close()}catch{}return}const v=pt(d,i,T=>{y.readyState===WebSocket.OPEN&&y.send(T)},n);E={context:d,tap:v}}catch(d){V(`could not send the copy this call is keeping: ${String((d==null?void 0:d.message)||d)}`)}};function Mt(){if(O=!1,!E)return;const{context:t,tap:n}=E;E=null;try{n.stop()}catch{}try{t.close()}catch{}}function X(){var n;if(!u||p)return;let t;try{t=(n=u.play)==null?void 0:n.call(u)}catch{Q();return}if(!t||typeof t.then!="function"){H();return}t.then(H,Q)}function H(){b==null||b(),b=null,S&&(S=!1,m(!1))}function Q(){p||(S||(S=!0,m(!0)),It())}function It(){if(b||typeof window>"u"||typeof window.addEventListener!="function")return;const t=()=>X(),n=["pointerdown","keydown","touchstart"];for(const d of n)window.addEventListener(d,t,{capture:!0,passive:!0});b=()=>{for(const d of n)try{window.removeEventListener(d,t,{capture:!0})}catch{}}}const $t=t=>{c=new RTCPeerConnection({iceServers:t!=null&&t.length?t:yt});for(const n of i.getAudioTracks())c.addTrack(n,i);C=Nt(),c.onicecandidate=n=>{n.candidate&&_({type:"signal",signal:{kind:"candidate",payload:n.candidate.toJSON()}})},c.ontrack=n=>{var v;const[d]=n.streams;d&&(u||(u=document.createElement("audio"),u.autoplay=!0,u.playsInline=!0,(v=u.setAttribute)==null||v.call(u,"playsinline",""),u.style.cssText="position:fixed;width:0;height:0;opacity:0;pointer-events:none",document.body.appendChild(u)),u.srcObject=d,X())},c.onconnectionstatechange=Z,c.oniceconnectionstatechange=Z};function Z(){if(!c||p)return;const t=c.connectionState||c.iceConnectionState;if(t==="connected"||t==="completed"){D=!0,B=0,et(),P=!1,l("connected");return}if(t==="disconnected"){if(!D)return;tt(Ot);return}if(t==="failed"){if(!D){k("failed",{reason:"ice"});return}tt(0)}}function tt(t){p||(P||(P=!0,l("reconnecting"),I=setTimeout(()=>k("failed",{reason:"network"}),_t)),!R&&(R=setTimeout(U,t)))}function et(){R&&(clearTimeout(R),R=null),I&&(clearTimeout(I),I=null)}async function U(){if(R=null,p||!c)return;const t=c.connectionState||c.iceConnectionState;if(!(t==="connected"||t==="completed")){if(!M){_({type:"signal",signal:{kind:"restart",payload:{}}}),R=setTimeout(U,K);return}B>=At||(B+=1,await nt(),R=setTimeout(U,K))}}async function nt(){if(!(p||!c))try{const t=await c.createOffer({iceRestart:!0});await c.setLocalDescription(t),_({type:"signal",signal:{kind:"offer",payload:c.localDescription.toJSON()}})}catch{}}function Nt(){let n=null,d=!1;const v=T=>{if(n&&(clearTimeout(n),n=null),T){if(!d)return;d=!1,g(!0);return}d||(n=setTimeout(()=>{n=null,!p&&(d=!0,g(!1))},600))};for(const T of i.getAudioTracks())T.onmute=()=>v(!1),T.onunmute=()=>v(!0),T.onended=()=>{n&&(clearTimeout(n),n=null),!d&&(d=!0,g(!1))};return()=>{n&&(clearTimeout(n),n=null);for(const T of i.getAudioTracks())T.onmute=null,T.onunmute=null,T.onended=null}}const Lt=async()=>{try{const t=await c.createOffer();await c.setLocalDescription(t),_({type:"signal",signal:{kind:"offer",payload:c.localDescription.toJSON()}})}catch{k("failed",{reason:"negotiation"})}},Dt=async t=>{try{await c.setRemoteDescription(new RTCSessionDescription(t));const n=await c.createAnswer();await c.setLocalDescription(n),_({type:"signal",signal:{kind:"answer",payload:c.localDescription.toJSON()}}),await ot()}catch{k("failed",{reason:"negotiation"})}},ot=async()=>{for(;x.length;){const t=x.shift();try{await c.addIceCandidate(new RTCIceCandidate(t))}catch{}}};let rt=Promise.resolve();y.onmessage=t=>{rt=rt.then(()=>Pt(t)).catch(()=>{})};const Pt=async t=>{var d,v,T,at,ct;if(typeof t.data!="string")return;let n;try{n=JSON.parse(t.data)}catch{return}switch(n.type){case"session.created":{if($t((d=n.data)==null?void 0:d.ice_servers),h=((v=n.data)==null?void 0:v.capture)||null,(T=n.data)!=null&&T.peer_present){l("connecting"),q();break}l("ringing"),Bt();break}case"peer.joined":{w&&(clearTimeout(w),w=null),l("connecting"),M=!!((at=n.data)!=null&&at.should_offer),M&&await Lt(),q();break}case"signal":{if(!c)break;const{kind:$,payload:N}=n.data||{};if($==="offer")await Dt(N);else if($==="answer")try{await c.setRemoteDescription(new RTCSessionDescription(N)),await ot()}catch{k("failed",{reason:"negotiation"})}else if($==="candidate")if(c.remoteDescription)try{await c.addIceCandidate(new RTCIceCandidate(N))}catch{}else x.push(N);else $==="restart"&&M&&await nt();break}case"peer.left":k("ended",{reason:"peer_left"});break;case"error":k("failed",{reason:"server",detail:(ct=n.error)==null?void 0:ct.detail});break;case"session.ended":k("ended",{reason:"server"});break}};y.onclose=()=>k("ended",{reason:"disconnected"}),y.onerror=()=>k("failed",{reason:"socket"});function Bt(){if(!o)return;const t=new Date(o).getTime()-Date.now();Number.isFinite(t)&&(w=setTimeout(()=>k("missed"),Math.max(0,t)))}return{hangUp(){_({type:"session.end"}),k("ended",{reason:"hung_up"})},setMuted(t){for(const n of i.getAudioTracks())n.enabled=!t;return t},isMuted(){return i.getAudioTracks().every(t=>!t.enabled)}}}function ht(e){return new Promise((r,o)=>{let a;try{a=new WebSocket(e,mt)}catch(s){o(s);return}a.onopen=()=>{a.onopen=null,a.onerror=null,r(a)},a.onerror=()=>{a.onopen=null,a.onerror=null,o(new Error("[BugCatch] could not connect the call."))}})}function V(e){try{console.warn(`[BugCatch] ${e}`)}catch{}}function W(e){var r;for(const o of((r=e==null?void 0:e.getTracks)==null?void 0:r.call(e))??[])try{o.stop()}catch{}}const J="#0e0e10",L="#ffffff";function wt(e){const r=bt(e);if(!r)return L;const[o,a,s]=r.map(l=>{const m=l/255;return m<=.03928?m/12.92:((m+.055)/1.055)**2.4});return .2126*o+.7152*a+.0722*s>.179?J:L}function bt(e){const r=String(e||"").trim();let o=/^#([0-9a-f]{3})$/i.exec(r);return o?[...o[1]].map(a=>parseInt(a+a,16)):(o=/^#([0-9a-f]{6})$/i.exec(r),o?[0,2,4].map(a=>parseInt(o[1].slice(a,a+2),16)):(o=/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(r),o?[o[1],o[2],o[3]].map(Number):null))}const A={cycleMs:3e3,peak:.045,bursts:[{at:0,seconds:.4,hz:[440,660]},{at:.55,seconds:.4,hz:[440,660]}]};function Tt(){var e;try{return!!((e=navigator.userActivation)!=null&&e.hasBeenActive)}catch{return!1}}function kt(e){if(!e()&&!Tt())return()=>{};const r=typeof window<"u"&&(window.AudioContext||window.webkitAudioContext);if(!r)return()=>{};let o;try{o=new r}catch{return()=>{}}let a=!1,s=null;const f=[],l=({at:i,seconds:y,hz:c})=>{const u=o.currentTime+i,p=o.createGain();p.connect(o.destination),p.gain.setValueAtTime(0,u),p.gain.linearRampToValueAtTime(A.peak,u+.02),p.gain.setValueAtTime(A.peak,u+y-.06),p.gain.linearRampToValueAtTime(0,u+y);for(const w of c){const h=o.createOscillator();h.type="sine",h.frequency.value=w,h.connect(p),h.start(u),h.stop(u+y+.02),f.push(h)}},m=()=>{if(!a)try{A.bursts.forEach(l)}catch{}},g=()=>{a||(m(),s=setInterval(m,A.cycleMs))};return o.state==="running"?g():o.resume().then(g).catch(()=>{}),()=>{a=!0,s&&clearInterval(s);for(const i of f)try{i.stop()}catch{}try{o.close()}catch{}}}function xt(e){const r=Date.parse(e||"");return Number.isNaN(r)?0:Math.max(0,Math.round((r-Date.now())/1e3))}const Y=["flex:1","padding:9px 12px","border:0","border-radius:999px","font:560 13px/1.4 inherit","cursor:pointer"].join(";");function Ct({call:e,accent:r=null,position:o="bottom-right",onAnswer:a,onDecline:s,onExpire:f,touched:l=()=>!1}){if(typeof document>"u"||!document.body)return()=>{};const[m,g]=String(o).split("-"),i=document.createElement("div");i.setAttribute("role","alertdialog"),i.setAttribute("aria-live","assertive"),i.setAttribute("aria-label",`${e.from||"Support"} is calling about ${e.reference}`),i.style.cssText=["position:fixed",`${m==="top"?"top":"bottom"}:20px`,`${g==="left"?"left":"right"}:20px`,"z-index:2147483647","width:300px","max-width:calc(100vw - 40px)","padding:16px","border-radius:16px","background:#111113","color:#f3f2ef",'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${r?`color-mix(in srgb, ${r} 55%, transparent)`:"rgba(255,255,255,.12)"},0 24px 50px -12px rgba(0,0,0,.6)`,"opacity:0","transform:translateY(8px)","transition:opacity .2s ease,transform .2s ease"].join(";");const y=document.createElement("div");y.style.cssText="font-weight:600;font-size:14px;margin-bottom:2px",y.textContent=`${e.from||"Support"} is calling`;const c=document.createElement("div");c.style.cssText="color:#b3b0aa;margin-bottom:10px",c.textContent=e.project?`About your report ${e.reference} · ${e.project}`:`About your report ${e.reference}`;const u=document.createElement("div");u.style.cssText="color:#8f8c86;font-size:12px;margin-bottom:10px";const p=document.createElement("button");p.type="button",p.textContent="Answer",p.style.cssText=`${Y};background:${r||L};color:${r?wt(r):J}`;const w=document.createElement("button");w.type="button",w.textContent="Not now",w.style.cssText=`${Y};background:rgba(255,255,255,.08);color:#f3f2ef`;const h=document.createElement("div");if(h.style.cssText="display:flex;gap:8px",h.append(p,w),i.append(y,c,u),e.recorded){const x=document.createElement("div");x.style.cssText="color:#8f8c86;font-size:12px;margin-bottom:10px",x.textContent="This call will be recorded.",i.append(x)}i.append(h),document.body.appendChild(i);const E=kt(l);let S=!1,b=null;const C=()=>{S||(S=!0,b&&clearInterval(b),E(),i.style.opacity="0",i.style.transform="translateY(8px)",setTimeout(()=>i.remove(),250))},O=()=>{const x=xt(e.ringsUntil);if(u.textContent=x?`Ringing — ${x}s`:"The call has stopped ringing.",!x){C();try{f==null||f()}catch{}}};return O(),b=setInterval(O,1e3),p.addEventListener("click",()=>{C();try{a==null||a()}catch{}}),w.addEventListener("click",()=>{C();try{s==null||s()}catch{}}),requestAnimationFrame(()=>{i.style.opacity="1",i.style.transform="translateY(0)"}),C}const vt=Object.freeze(Object.defineProperty({__proto__:null,joinLiveCall:gt,liveCallSupported:F,openMicrophone:z,showIncomingCall:Ct},Symbol.toStringTag,{value:"Module"})),Rt="0.20.0",G="__bugcatchChunks",Et=e=>`${e}@${Rt}`;function St(e,r){const o=window[G]||(window[G]={});o[Et(e)]=r}St("call",vt)})();
|
|
1
|
+
(function(){"use strict";function it(e=8e3){const r=window.AudioContext||window.webkitAudioContext;try{return new r({sampleRate:e})}catch{return new r}}async function st(e){if(e.state==="suspended")try{await e.resume()}catch{}}function lt(e,r,o){const a=r/o,s=new Float32Array(Math.floor(e.length/a));for(let f=0;f<s.length;f++){const l=Math.floor(f*a),m=Math.min(Math.floor((f+1)*a),e.length);let g=0;for(let i=l;i<m;i++)g+=e[i];s[f]=m>l?g/(m-l):0}return s}const ut=132,j=32635;function ft(e){const r=new Uint8Array(e.length);for(let o=0;o<e.length;o++){const a=Math.max(-1,Math.min(1,e[o]));let s=Math.round(a*32767);const f=s<0?128:0;f&&(s=-s),s>j&&(s=j),s+=ut;let l=7;for(let g=16384;!(s&g)&&l>0;l--,g>>=1);const m=s>>l+3&15;r[o]=~(f|l<<4|m)&255}return r}const dt=512;function pt(e,r,o,a=8e3){const s=e.createMediaStreamSource(r),f=e.createScriptProcessor(dt,1,1);f.onaudioprocess=m=>{const g=m.inputBuffer.getChannelData(0),i=e.sampleRate===a?g:lt(g,e.sampleRate,a);o(ft(i))},s.connect(f);const l=e.createGain();return l.gain.value=0,f.connect(l),l.connect(e.destination),{stop(){f.onaudioprocess=null;try{s.disconnect()}catch{}try{f.disconnect()}catch{}try{l.disconnect()}catch{}}}}const mt="voiceai.realtime.v1",yt=[{urls:"stun:stun.l.google.com:19302"}];function z(){return navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:1}})}function F(){var e;return typeof window<"u"&&typeof window.RTCPeerConnection=="function"&&!!((e=navigator.mediaDevices)!=null&&e.getUserMedia)}async function gt({realtimeUrl:e,stream:r,ringsUntil:o,onState:a,onAudioBlocked:s,onMicrophone:f}){const l=(t,n)=>{try{a==null||a(t,n)}catch{}},m=t=>{try{s==null||s(t)}catch{}},g=t=>{try{f==null||f(t)}catch{}};if(!F()){const t=new Error("[BugCatch] this browser cannot make live calls.");throw l("failed",{reason:"unsupported"}),t}let i=r;if(!i)try{i=await z()}catch(t){throw l("failed",{reason:"microphone",error:String((t==null?void 0:t.name)||t)}),t}let y;try{y=await ht(e)}catch(t){throw W(i),l("failed",{reason:"connect"}),t}let c=null,u=null,p=!1,w=null,h=null,E=null,S=!1,b=null,C=null,O=!1;const x=[];let M=!1,D=!1,P=!1,R=null,I=null,B=0;const Ot=2e3,K=5e3,_t=3e4,At=4,k=(t,n)=>{if(!p){p=!0,w&&(clearTimeout(w),w=null),et(),C==null||C(),C=null,Mt(),b==null||b(),b=null;try{c==null||c.close()}catch{}W(i),u&&(u.srcObject=null,u.remove(),u=null);try{y.readyState===WebSocket.OPEN&&y.close(1e3,"call over")}catch{}l(t,n)}},_=t=>{y.readyState===WebSocket.OPEN&&y.send(JSON.stringify(t))},q=async()=>{if(O||E||p||!h||!h.audio&&!h.transcript)return;O=!0;const t=h.encoding||"mulaw";if(t!=="mulaw"){V(`this call is kept as ${t}, which this SDK cannot produce.`);return}const n=Number(h.sample_rate)||8e3;try{const d=it(n);if(await st(d),p){try{d.close()}catch{}return}const v=pt(d,i,T=>{y.readyState===WebSocket.OPEN&&y.send(T)},n);E={context:d,tap:v}}catch(d){V(`could not send the copy this call is keeping: ${String((d==null?void 0:d.message)||d)}`)}};function Mt(){if(O=!1,!E)return;const{context:t,tap:n}=E;E=null;try{n.stop()}catch{}try{t.close()}catch{}}function X(){var n;if(!u||p)return;let t;try{t=(n=u.play)==null?void 0:n.call(u)}catch{Q();return}if(!t||typeof t.then!="function"){H();return}t.then(H,Q)}function H(){b==null||b(),b=null,S&&(S=!1,m(!1))}function Q(){p||(S||(S=!0,m(!0)),It())}function It(){if(b||typeof window>"u"||typeof window.addEventListener!="function")return;const t=()=>X(),n=["pointerdown","keydown","touchstart"];for(const d of n)window.addEventListener(d,t,{capture:!0,passive:!0});b=()=>{for(const d of n)try{window.removeEventListener(d,t,{capture:!0})}catch{}}}const $t=t=>{c=new RTCPeerConnection({iceServers:t!=null&&t.length?t:yt});for(const n of i.getAudioTracks())c.addTrack(n,i);C=Nt(),c.onicecandidate=n=>{n.candidate&&_({type:"signal",signal:{kind:"candidate",payload:n.candidate.toJSON()}})},c.ontrack=n=>{var v;const[d]=n.streams;d&&(u||(u=document.createElement("audio"),u.autoplay=!0,u.playsInline=!0,(v=u.setAttribute)==null||v.call(u,"playsinline",""),u.style.cssText="position:fixed;width:0;height:0;opacity:0;pointer-events:none",document.body.appendChild(u)),u.srcObject=d,X())},c.onconnectionstatechange=Z,c.oniceconnectionstatechange=Z};function Z(){if(!c||p)return;const t=c.connectionState||c.iceConnectionState;if(t==="connected"||t==="completed"){D=!0,B=0,et(),P=!1,l("connected");return}if(t==="disconnected"){if(!D)return;tt(Ot);return}if(t==="failed"){if(!D){k("failed",{reason:"ice"});return}tt(0)}}function tt(t){p||(P||(P=!0,l("reconnecting"),I=setTimeout(()=>k("failed",{reason:"network"}),_t)),!R&&(R=setTimeout(U,t)))}function et(){R&&(clearTimeout(R),R=null),I&&(clearTimeout(I),I=null)}async function U(){if(R=null,p||!c)return;const t=c.connectionState||c.iceConnectionState;if(!(t==="connected"||t==="completed")){if(!M){_({type:"signal",signal:{kind:"restart",payload:{}}}),R=setTimeout(U,K);return}B>=At||(B+=1,await nt(),R=setTimeout(U,K))}}async function nt(){if(!(p||!c))try{const t=await c.createOffer({iceRestart:!0});await c.setLocalDescription(t),_({type:"signal",signal:{kind:"offer",payload:c.localDescription.toJSON()}})}catch{}}function Nt(){let n=null,d=!1;const v=T=>{if(n&&(clearTimeout(n),n=null),T){if(!d)return;d=!1,g(!0);return}d||(n=setTimeout(()=>{n=null,!p&&(d=!0,g(!1))},600))};for(const T of i.getAudioTracks())T.onmute=()=>v(!1),T.onunmute=()=>v(!0),T.onended=()=>{n&&(clearTimeout(n),n=null),!d&&(d=!0,g(!1))};return()=>{n&&(clearTimeout(n),n=null);for(const T of i.getAudioTracks())T.onmute=null,T.onunmute=null,T.onended=null}}const Lt=async()=>{try{const t=await c.createOffer();await c.setLocalDescription(t),_({type:"signal",signal:{kind:"offer",payload:c.localDescription.toJSON()}})}catch{k("failed",{reason:"negotiation"})}},Dt=async t=>{try{await c.setRemoteDescription(new RTCSessionDescription(t));const n=await c.createAnswer();await c.setLocalDescription(n),_({type:"signal",signal:{kind:"answer",payload:c.localDescription.toJSON()}}),await ot()}catch{k("failed",{reason:"negotiation"})}},ot=async()=>{for(;x.length;){const t=x.shift();try{await c.addIceCandidate(new RTCIceCandidate(t))}catch{}}};let rt=Promise.resolve();y.onmessage=t=>{rt=rt.then(()=>Pt(t)).catch(()=>{})};const Pt=async t=>{var d,v,T,at,ct;if(typeof t.data!="string")return;let n;try{n=JSON.parse(t.data)}catch{return}switch(n.type){case"session.created":{if($t((d=n.data)==null?void 0:d.ice_servers),h=((v=n.data)==null?void 0:v.capture)||null,(T=n.data)!=null&&T.peer_present){l("connecting"),q();break}l("ringing"),Bt();break}case"peer.joined":{w&&(clearTimeout(w),w=null),l("connecting"),M=!!((at=n.data)!=null&&at.should_offer),M&&await Lt(),q();break}case"signal":{if(!c)break;const{kind:$,payload:N}=n.data||{};if($==="offer")await Dt(N);else if($==="answer")try{await c.setRemoteDescription(new RTCSessionDescription(N)),await ot()}catch{k("failed",{reason:"negotiation"})}else if($==="candidate")if(c.remoteDescription)try{await c.addIceCandidate(new RTCIceCandidate(N))}catch{}else x.push(N);else $==="restart"&&M&&await nt();break}case"peer.left":k("ended",{reason:"peer_left"});break;case"error":k("failed",{reason:"server",detail:(ct=n.error)==null?void 0:ct.detail});break;case"session.ended":k("ended",{reason:"server"});break}};y.onclose=()=>k("ended",{reason:"disconnected"}),y.onerror=()=>k("failed",{reason:"socket"});function Bt(){if(!o)return;const t=new Date(o).getTime()-Date.now();Number.isFinite(t)&&(w=setTimeout(()=>k("missed"),Math.max(0,t)))}return{hangUp(){_({type:"session.end"}),k("ended",{reason:"hung_up"})},setMuted(t){for(const n of i.getAudioTracks())n.enabled=!t;return t},isMuted(){return i.getAudioTracks().every(t=>!t.enabled)}}}function ht(e){return new Promise((r,o)=>{let a;try{a=new WebSocket(e,mt)}catch(s){o(s);return}a.onopen=()=>{a.onopen=null,a.onerror=null,r(a)},a.onerror=()=>{a.onopen=null,a.onerror=null,o(new Error("[BugCatch] could not connect the call."))}})}function V(e){try{console.warn(`[BugCatch] ${e}`)}catch{}}function W(e){var r;for(const o of((r=e==null?void 0:e.getTracks)==null?void 0:r.call(e))??[])try{o.stop()}catch{}}const J="#0e0e10",L="#ffffff";function wt(e){const r=bt(e);if(!r)return L;const[o,a,s]=r.map(l=>{const m=l/255;return m<=.03928?m/12.92:((m+.055)/1.055)**2.4});return .2126*o+.7152*a+.0722*s>.179?J:L}function bt(e){const r=String(e||"").trim();let o=/^#([0-9a-f]{3})$/i.exec(r);return o?[...o[1]].map(a=>parseInt(a+a,16)):(o=/^#([0-9a-f]{6})$/i.exec(r),o?[0,2,4].map(a=>parseInt(o[1].slice(a,a+2),16)):(o=/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(r),o?[o[1],o[2],o[3]].map(Number):null))}const A={cycleMs:3e3,peak:.045,bursts:[{at:0,seconds:.4,hz:[440,660]},{at:.55,seconds:.4,hz:[440,660]}]};function Tt(){var e;try{return!!((e=navigator.userActivation)!=null&&e.hasBeenActive)}catch{return!1}}function kt(e){if(!e()&&!Tt())return()=>{};const r=typeof window<"u"&&(window.AudioContext||window.webkitAudioContext);if(!r)return()=>{};let o;try{o=new r}catch{return()=>{}}let a=!1,s=null;const f=[],l=({at:i,seconds:y,hz:c})=>{const u=o.currentTime+i,p=o.createGain();p.connect(o.destination),p.gain.setValueAtTime(0,u),p.gain.linearRampToValueAtTime(A.peak,u+.02),p.gain.setValueAtTime(A.peak,u+y-.06),p.gain.linearRampToValueAtTime(0,u+y);for(const w of c){const h=o.createOscillator();h.type="sine",h.frequency.value=w,h.connect(p),h.start(u),h.stop(u+y+.02),f.push(h)}},m=()=>{if(!a)try{A.bursts.forEach(l)}catch{}},g=()=>{a||(m(),s=setInterval(m,A.cycleMs))};return o.state==="running"?g():o.resume().then(g).catch(()=>{}),()=>{a=!0,s&&clearInterval(s);for(const i of f)try{i.stop()}catch{}try{o.close()}catch{}}}function xt(e){const r=Date.parse(e||"");return Number.isNaN(r)?0:Math.max(0,Math.round((r-Date.now())/1e3))}const Y=["flex:1","padding:9px 12px","border:0","border-radius:999px","font:560 13px/1.4 inherit","cursor:pointer"].join(";");function Ct({call:e,accent:r=null,position:o="bottom-right",onAnswer:a,onDecline:s,onExpire:f,touched:l=()=>!1}){if(typeof document>"u"||!document.body)return()=>{};const[m,g]=String(o).split("-"),i=document.createElement("div");i.setAttribute("role","alertdialog"),i.setAttribute("aria-live","assertive"),i.setAttribute("aria-label",`${e.from||"Support"} is calling about ${e.reference}`),i.style.cssText=["position:fixed",`${m==="top"?"top":"bottom"}:20px`,`${g==="left"?"left":"right"}:20px`,"z-index:2147483647","width:300px","max-width:calc(100vw - 40px)","padding:16px","border-radius:16px","background:#111113","color:#f3f2ef",'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${r?`color-mix(in srgb, ${r} 55%, transparent)`:"rgba(255,255,255,.12)"},0 24px 50px -12px rgba(0,0,0,.6)`,"opacity:0","transform:translateY(8px)","transition:opacity .2s ease,transform .2s ease"].join(";");const y=document.createElement("div");y.style.cssText="font-weight:600;font-size:14px;margin-bottom:2px",y.textContent=`${e.from||"Support"} is calling`;const c=document.createElement("div");c.style.cssText="color:#b3b0aa;margin-bottom:10px",c.textContent=e.project?`About your report ${e.reference} · ${e.project}`:`About your report ${e.reference}`;const u=document.createElement("div");u.style.cssText="color:#8f8c86;font-size:12px;margin-bottom:10px";const p=document.createElement("button");p.type="button",p.textContent="Answer",p.style.cssText=`${Y};background:${r||L};color:${r?wt(r):J}`;const w=document.createElement("button");w.type="button",w.textContent="Not now",w.style.cssText=`${Y};background:rgba(255,255,255,.08);color:#f3f2ef`;const h=document.createElement("div");if(h.style.cssText="display:flex;gap:8px",h.append(p,w),i.append(y,c,u),e.recorded){const x=document.createElement("div");x.style.cssText="color:#8f8c86;font-size:12px;margin-bottom:10px",x.textContent="This call will be recorded.",i.append(x)}i.append(h),document.body.appendChild(i);const E=kt(l);let S=!1,b=null;const C=()=>{S||(S=!0,b&&clearInterval(b),E(),i.style.opacity="0",i.style.transform="translateY(8px)",setTimeout(()=>i.remove(),250))},O=()=>{const x=xt(e.ringsUntil);if(u.textContent=x?`Ringing — ${x}s`:"The call has stopped ringing.",!x){C();try{f==null||f()}catch{}}};return O(),b=setInterval(O,1e3),p.addEventListener("click",()=>{C();try{a==null||a()}catch{}}),w.addEventListener("click",()=>{C();try{s==null||s()}catch{}}),requestAnimationFrame(()=>{i.style.opacity="1",i.style.transform="translateY(0)"}),C}const vt=Object.freeze(Object.defineProperty({__proto__:null,joinLiveCall:gt,liveCallSupported:F,openMicrophone:z,showIncomingCall:Ct},Symbol.toStringTag,{value:"Module"})),Rt="0.21.0",G="__bugcatchChunks",Et=e=>`${e}@${Rt}`;function St(e,r){const o=window[G]||(window[G]={});o[Et(e)]=r}St("call",vt)})();
|
|
2
2
|
//# sourceMappingURL=bugcatch-call.umd.js.map
|