@sphoro/bugcatch-web-sdk 0.17.0 → 0.19.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.
Files changed (52) hide show
  1. package/README.md +104 -6
  2. package/dist/bugcatch-call.es.js +587 -0
  3. package/dist/bugcatch-call.es.js.map +1 -0
  4. package/dist/bugcatch-call.umd.js +2 -0
  5. package/dist/bugcatch-call.umd.js.map +1 -0
  6. package/dist/bugcatch-core.es.js +3292 -0
  7. package/dist/bugcatch-core.es.js.map +1 -0
  8. package/dist/bugcatch-panel.es.js +2531 -0
  9. package/dist/bugcatch-panel.es.js.map +1 -0
  10. package/dist/bugcatch-panel.umd.js +1311 -0
  11. package/dist/bugcatch-panel.umd.js.map +1 -0
  12. package/dist/bugcatch.es.js +2 -5625
  13. package/dist/bugcatch.es.js.map +1 -1
  14. package/dist/bugcatch.standalone.umd.js +1619 -0
  15. package/dist/bugcatch.standalone.umd.js.map +1 -0
  16. package/dist/bugcatch.umd.js +195 -1318
  17. package/dist/bugcatch.umd.js.map +1 -1
  18. package/package.json +6 -6
  19. package/src/accent.js +38 -0
  20. package/src/announcements.js +147 -0
  21. package/src/busy.js +18 -0
  22. package/src/call.js +7 -0
  23. package/src/capture.js +9 -0
  24. package/src/chunks.js +56 -0
  25. package/src/gesture.js +33 -0
  26. package/src/icons.js +18 -0
  27. package/src/identity.js +13 -8
  28. package/src/incoming-call.js +25 -45
  29. package/src/index.d.ts +93 -2
  30. package/src/index.js +164 -42
  31. package/src/lazy.js +23 -0
  32. package/src/notice.js +68 -0
  33. package/src/panel/call.js +242 -0
  34. package/src/panel/compose.js +416 -0
  35. package/src/panel/composer.js +235 -0
  36. package/src/panel/icons.js +36 -0
  37. package/src/panel/index.js +265 -0
  38. package/src/panel/known.js +130 -0
  39. package/src/panel/messages.js +176 -0
  40. package/src/panel/notices.js +34 -0
  41. package/src/panel/picks.js +65 -0
  42. package/src/panel/refs.js +321 -0
  43. package/src/panel/render.js +132 -0
  44. package/src/panel/signin.js +228 -0
  45. package/src/panel/styles.js +1016 -0
  46. package/src/panel/thread.js +494 -0
  47. package/src/session.js +8 -3
  48. package/src/umd/call.js +6 -0
  49. package/src/umd/lazy.js +98 -0
  50. package/src/umd/panel.js +6 -0
  51. package/src/updates.js +97 -34
  52. package/src/widget.js +385 -3376
@@ -1,84 +1,106 @@
1
- (function(X,pe){typeof exports=="object"&&typeof module<"u"?module.exports=pe():typeof define=="function"&&define.amd?define(pe):(X=typeof globalThis<"u"?globalThis:X||self,X.BugCatch=pe())})(this,function(){"use strict";var Io=X=>{throw TypeError(X)};var Ur=(X,pe,je)=>pe.has(X)||Io("Cannot "+je);var Mo=(X,pe,je)=>pe.has(X)?Io("Cannot add the same private member more than once"):pe instanceof WeakSet?pe.add(X):pe.set(X,je);var H=(X,pe,je)=>(Ur(X,pe,"access private method"),je);var L,Mt,Lo,Be,Ao,ln,Lt,At,dn,Bo,jo,Ro,Bt,zo;const X="[REDACTED]",pe=new Set(["auth","bearer","session","sid","phpsessid","otp","pass","passwd","pwd","sig","codeverifier"]),je=["token","secret","password","passwd","apikey","signature","credential","sessionid","privatekey","accesskey","authorization"],Oo=new Set(["key","code","state","nonce","salt","hash"]),un=[/eyJ[\w-]{8,}\.[\w-]{8,}\.[\w-]+/g,/(?:AKIA|ASIA|AROA|AIDA|AGPA|ANPA|ANVA|APKA)[0-9A-Z]{16}/g,/AIza[\w-]{35}/g,/gh[pousr]_[A-Za-z0-9]{36,}/g,/github_pat_\w{22,}/g,/[sprk]k_(?:live|test)_[0-9A-Za-z]{10,}/g,/xox[abposr]-[A-Za-z0-9-]{10,}/g,/sk-ant-[\w-]{20,}/g,/sk-[\w-]{20,}/g,/glpat-[\w-]{20,}/g,/shp(?:at|ss|ca|pa)_[A-Za-z0-9]{32}/g],Qo=/\bBearer\s+[\w.~+/=-]{8,}/gi,qo=/^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#@]+)@/;function pn(t){try{return decodeURIComponent(t.replace(/\+/g," "))}catch{return t}}function Ho(t){return pn(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function Do(t){return t.length>=20?!0:un.some(e=>(e.lastIndex=0,e.test(t)))}function No(t,e){const n=Ho(t);if(!n)return!1;if(pe.has(n))return!0;for(const o of je)if(n.includes(o))return!0;return Oo.has(n)?Do(pn(e)):!1}function jt(t){return t.split("&").map(e=>{const n=e.indexOf("=");if(n===-1)return e;const o=e.slice(0,n),i=e.slice(n+1);return i&&No(o,i)?`${o}=${X}`:e}).join("&")}function hn(t){let e=t;for(const n of un)n.lastIndex=0,e=e.replace(n,X);return e}function fn(t){if(t==null)return t;let e=String(t);if(!e)return e;e=e.replace(qo,`$1${X}@`);const n=e.indexOf("#");let o=n===-1?e:e.slice(0,n),i=n===-1?"":e.slice(n);const r=o.indexOf("?");if(r!==-1&&(o=o.slice(0,r+1)+jt(o.slice(r+1))),i.length>1){const s=i.slice(1),l=s.indexOf("?");l!==-1?i=`#${s.slice(0,l+1)}${jt(s.slice(l+1))}`:s.includes("=")&&(i=`#${jt(s)}`)}return hn(o+i)}function Uo(t){if(t==null)return t;let e=String(t);return e&&(e=e.replace(/[A-Za-z][A-Za-z0-9+.-]{0,30}:\/\/[^\s"'<>)\]]+/g,n=>fn(n)),e=e.replace(Qo,`Bearer ${X}`),hn(e))}function ct(t){const e=[];return{push(n){e.push(n),e.length>t&&e.splice(0,e.length-t)},all:()=>e.slice(),clear:()=>{e.length=0},get size(){return e.length}}}const gn=["log","info","warn","error","debug"];function Ko(t,{levels:e=["warn","error","log","info"]}={}){const n={};for(const o of gn)n[o]=console[o],e.includes(o)&&(console[o]=(...i)=>{try{t.push({level:o,text:i.map(yn).join(" ").slice(0,2e3),at:new Date().toISOString()})}catch{}n[o].apply(console,i)});return()=>{for(const o of gn)console[o]=n[o]}}function Po(t,{slowMs:e=2e3}={}){const n=window.fetch,o=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send;return n&&(window.fetch=async function(...s){var m,h,f;const l=performance.now(),g=typeof s[0]=="string"?s[0]:(m=s[0])==null?void 0:m.url,b=((h=s[1])==null?void 0:h.method)||(typeof s[0]=="object"?(f=s[0])==null?void 0:f.method:"GET")||"GET";try{const p=await n.apply(this,s);return Rt(t,{url:g,method:b,status:p.status,ms:Math.round(performance.now()-l),slowMs:e}),p}catch(p){throw Rt(t,{url:g,method:b,status:0,error:String((p==null?void 0:p.message)||p),ms:Math.round(performance.now()-l),slowMs:e}),p}}),XMLHttpRequest.prototype.open=function(s,l,...g){return this.__bc={method:s,url:l,started:0},o.call(this,s,l,...g)},XMLHttpRequest.prototype.send=function(...s){return this.__bc&&(this.__bc.started=performance.now(),this.addEventListener("loadend",()=>{Rt(t,{url:this.__bc.url,method:this.__bc.method,status:this.status,ms:Math.round(performance.now()-this.__bc.started),slowMs:e})})),i.apply(this,s)},()=>{n&&(window.fetch=n),XMLHttpRequest.prototype.open=o,XMLHttpRequest.prototype.send=i}}function Rt(t,{url:e,method:n,status:o,ms:i,error:r,slowMs:s}){try{t.push({url:fn(String(e)).slice(0,500),method:n,status:o,ms:i,error:r===void 0?void 0:Uo(r),failed:o===0||o>=400,slow:i>=s,at:new Date().toISOString()})}catch{}}function _o(t){const e=o=>{const i=String(o.message||"Error"),r=o.filename?` (${o.filename}:${o.lineno}:${o.colno})`:"";t.push({level:"error",text:(/^uncaught/i.test(i)?i:`Uncaught ${i}`)+r,at:new Date().toISOString()})},n=o=>{t.push({level:"error",text:`Unhandled rejection: ${yn(o.reason)}`,at:new Date().toISOString()})};return window.addEventListener("error",e),window.addEventListener("unhandledrejection",n),()=>{window.removeEventListener("error",e),window.removeEventListener("unhandledrejection",n)}}async function bn(){const t=mn();try{if(navigator.getBattery){const e=await navigator.getBattery();t.battery=Math.round(e.level*100),t.charging=e.charging}}catch{}return t}function mn(){var e,n,o,i,r,s,l,g,b;const t=window.navigator||{};return{userAgent:t.userAgent,platform:t.platform,language:t.language,languages:t.languages?[...t.languages]:null,online:t.onLine,cookieEnabled:t.cookieEnabled,maxTouchPoints:t.maxTouchPoints??null,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:(e=window.screen)==null?void 0:e.width,height:(n=window.screen)==null?void 0:n.height,availWidth:(o=window.screen)==null?void 0:o.availWidth,availHeight:(i=window.screen)==null?void 0:i.availHeight,colorDepth:(r=window.screen)==null?void 0:r.colorDepth,orientation:((l=(s=window.screen)==null?void 0:s.orientation)==null?void 0:l.type)||null},devicePixelRatio:window.devicePixelRatio,referrer:document.referrer||null,title:document.title,capturedAt:new Date().toISOString(),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timezoneOffsetMinutes:new Date().getTimezoneOffset(),browserMode:t.webdriver?"Automated":"Normal",doNotTrack:t.doNotTrack||null,hardwareConcurrency:t.hardwareConcurrency??null,deviceMemory:t.deviceMemory??null,connection:t.connection?{effectiveType:t.connection.effectiveType,downlink:t.connection.downlink,rtt:t.connection.rtt,saveData:t.connection.saveData}:null,prefersColorScheme:(g=window.matchMedia)!=null&&g.call(window,"(prefers-color-scheme: dark)").matches?"dark":"light",prefersReducedMotion:((b=window.matchMedia)==null?void 0:b.call(window,"(prefers-reduced-motion: reduce)").matches)||!1}}function yn(t){if(typeof t=="string")return t;if(t instanceof Error)return`${t.name}: ${t.message}`;try{return JSON.stringify(t)}catch{return String(t)}}const Vo=typeof window<"u"&&window.fetch?window.fetch.bind(window):null,Wo=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.open:null,Fo=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.send:null,wn=["image/png","image/jpeg","image/webp","image/gif","video/webm","video/mp4","audio/webm","audio/ogg","audio/mpeg","audio/mp4","audio/wav","application/pdf","text/plain","text/csv","application/json","application/zip","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],lt=["image/png","image/jpeg","image/webp","video/webm","video/mp4"],Yo=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getUserMedia&&typeof MediaRecorder<"u")},Xo=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getDisplayMedia&&typeof MediaRecorder<"u")},Jo=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getDisplayMedia)};function dt(t,e=wn){const n=String(t||"").split(";")[0].trim().toLowerCase();return e.includes(n)?n:null}async function Zo(){const t=await navigator.mediaDevices.getDisplayMedia({video:{frameRate:1},audio:!1});try{const e=t.getVideoTracks()[0],n=document.createElement("video");n.srcObject=t,n.muted=!0,n.playsInline=!0,await n.play(),await new Promise(l=>requestAnimationFrame(()=>requestAnimationFrame(l)));const{width:o,height:i}=e.getSettings(),r=document.createElement("canvas");r.width=o||n.videoWidth,r.height=i||n.videoHeight,r.getContext("2d").drawImage(n,0,0,r.width,r.height);const s=await new Promise(l=>r.toBlob(l,"image/png"));if(!s)throw new Error("The browser returned an empty screenshot.");return{blob:s,mime:"image/png",kind:"SCREENSHOT"}}finally{t.getTracks().forEach(e=>e.stop())}}async function Go({maxMs:t=12e4,onTick:e,onAutoStop:n}={}){const o=await navigator.mediaDevices.getDisplayMedia({video:{frameRate:15},audio:!1}),i=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"].find(p=>MediaRecorder.isTypeSupported(p))||"video/webm",r=new MediaRecorder(o,{mimeType:i,videoBitsPerSecond:25e5}),s=[];r.ondataavailable=p=>{p.data.size&&s.push(p.data)},r.start(1e3);const l=Date.now(),g=setInterval(()=>e==null?void 0:e(Math.floor((Date.now()-l)/1e3)),500),b=()=>{clearInterval(g),clearTimeout(f),o.getTracks().forEach(p=>p.stop())},m=new Promise(p=>{r.onstop=()=>{b(),p(new Blob(s,{type:i}))}}),h=()=>{r.state!=="inactive"&&r.stop(),n==null||n()};o.getVideoTracks()[0].addEventListener("ended",h);const f=setTimeout(h,t);return{mime:i,maxMs:t,async stop(){return clearTimeout(f),r.state!=="inactive"&&r.stop(),{blob:await m,mime:i,kind:"VIDEO"}},cancel(){b(),r.state!=="inactive"&&r.stop()}}}async function ea({maxMs:t=12e4,onTick:e,onAutoStop:n}={}){const o=await navigator.mediaDevices.getUserMedia({audio:!0}),i=["audio/webm;codecs=opus","audio/webm","audio/mp4","audio/ogg"].find(p=>MediaRecorder.isTypeSupported(p))||"",r=new MediaRecorder(o,i?{mimeType:i}:void 0),s=[];r.ondataavailable=p=>{p.data.size&&s.push(p.data)},r.start(1e3);const l=Date.now(),g=setInterval(()=>e==null?void 0:e(Math.floor((Date.now()-l)/1e3)),500),b=()=>{clearInterval(g),clearTimeout(f),o.getTracks().forEach(p=>p.stop())},m=new Promise(p=>{r.onstop=()=>{b(),p(new Blob(s,{type:r.mimeType||i}))}}),f=setTimeout(()=>{r.state!=="inactive"&&r.stop(),n==null||n()},t);return{mime:r.mimeType||i,maxMs:t,async stop(){clearTimeout(f),r.state!=="inactive"&&r.stop();const p=await m;return{blob:p,mime:p.type||r.mimeType||i,kind:"AUDIO"}},cancel(){b(),r.state!=="inactive"&&r.stop()}}}async function ta({apiBase:t,apiKey:e,blob:n,mime:o,onProgress:i}){const r=dt(o||n.type);if(!r)throw new Error(`[BugCatch] ${o||n.type||"that file"} can't be attached.`);const s=await Vo(`${t}/ingest/presign`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":e},body:JSON.stringify({mime:r})});if(!s.ok)throw new Error(`[BugCatch] could not start the upload (HTTP ${s.status}).`);const{key:l,uploadUrl:g}=await s.json();return await na(g,n,r,i),{storageKey:l,mime:r}}function na(t,e,n,o){return new Promise((i,r)=>{const s=new XMLHttpRequest;Wo.call(s,"PUT",t,!0),s.setRequestHeader("Content-Type",n),s.upload.onprogress=l=>{l.lengthComputable&&(o==null||o(l.loaded/l.total))},s.onload=()=>{s.status>=200&&s.status<300?i():r(new Error(`[BugCatch] upload failed (HTTP ${s.status}).`))},s.onerror=()=>r(new Error("[BugCatch] the browser blocked the upload to storage. If this is a new environment, the media bucket is probably missing its CORS policy.")),s.onabort=()=>r(new Error("[BugCatch] upload cancelled.")),Fo.call(s,e)})}function qe(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(0)} KB`:`${(t/1024/1024).toFixed(1)} MB`}function ut(t){const e=Math.floor(t/60),n=t%60;return`${e}:${String(n).padStart(2,"0")}`}const oa=7*24*60*60*1e3;let pt;function aa(){return pt||(pt=new Promise((t,e)=>{const n=indexedDB.open("bugcatch-drafts",1);n.onupgradeneeded=()=>n.result.createObjectStore("drafts"),n.onsuccess=()=>t(n.result),n.onerror=()=>e(n.error),n.onblocked=()=>e(new Error("Draft storage is blocked."))}).catch(t=>{throw pt=null,t})),pt}function ra(t,e){var n,o;return JSON.stringify([t.apiBase,t.projectId,e||((n=t.user)==null?void 0:n.email)||((o=t.user)==null?void 0:o.id)||"anonymous"])}async function ht(t,e){const n=await aa();return new Promise((o,i)=>{const r=n.transaction("drafts",e===void 0?"readonly":"readwrite"),s=r.objectStore("drafts"),l=e===void 0?s.get(t):e===null?s.delete(t):s.put({...e,savedAt:Date.now()},t);r.oncomplete=()=>o(l.result),r.onerror=()=>i(r.error),r.onabort=()=>i(r.error||new Error("Draft was not saved."))})}async function ia(t){const e=await ht(t);return e&&Date.now()-e.savedAt>oa?(await ht(t,null),null):e||null}const sa="bugcatch.session.",zt=t=>`${sa}${t}`,Ot=new Map;function $e(t){var e;if(!t)return null;try{const n=(e=window.localStorage)==null?void 0:e.getItem(zt(t));if(n){const o=JSON.parse(n);if(o!=null&&o.token)return o}}catch{}return Ot.get(t)||null}function ca(t,e){var o;if(!t||!(e!=null&&e.token))return;const n={token:e.token,email:e.email||null,name:e.name||null};Ot.set(t,n);try{(o=window.localStorage)==null||o.setItem(zt(t),JSON.stringify(n))}catch{}}function Qt(t){var e;if(t){Ot.delete(t);try{(e=window.localStorage)==null||e.removeItem(zt(t))}catch{}}}function ft({apiKey:t,token:e}){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function vn(t){try{const e=await t.json(),n=Array.isArray(e.message)?e.message.join(", "):e.message;if(n)return n}catch{}return`Request failed (HTTP ${t.status})`}async function la({apiBase:t,apiKey:e,projectId:n,token:o}){const i={mode:"off",signedIn:null,signedOutReason:null};try{const r=await fetch(`${t}/ingest/login?projectId=${encodeURIComponent(n)}`,{headers:ft({apiKey:e,token:o})});if(!r.ok)return i;const s=await r.json();return{mode:s.mode==="optional"||s.mode==="required"?s.mode:"off",signedIn:s.signedIn||null,signedOutReason:s.signedOutReason||null}}catch{return i}}async function da({apiBase:t,apiKey:e,projectId:n,email:o,name:i}){const r=await fetch(`${t}/ingest/login/start`,{method:"POST",headers:ft({apiKey:e}),body:JSON.stringify({projectId:n,email:o,...i?{name:i}:{}})});if(!r.ok)throw new Error(await vn(r));return r.json()}async function ua({apiBase:t,apiKey:e,projectId:n,email:o,code:i}){const r=await fetch(`${t}/ingest/login`,{method:"POST",headers:ft({apiKey:e}),body:JSON.stringify({projectId:n,email:o,code:i})});if(!r.ok)throw new Error(await vn(r));const s=await r.json(),l={token:s.sessionToken,email:s.email,name:s.name};return ca(n,l),l}async function pa({apiBase:t,apiKey:e,projectId:n,token:o}){if(Qt(n),!o)return{signedOut:!0};try{await fetch(`${t}/ingest/logout`,{method:"POST",headers:ft({apiKey:e,token:o}),body:JSON.stringify({projectId:n})})}catch{}return{signedOut:!0}}const gt="bugcatch.watch",xn=[2e3,5e3,15e3,3e4,6e4],ha=5e3;let We=null;function fa(t){We=t||null}const qt=()=>We?`${gt}:${We}`:gt;function Ht(){return We?`?projectId=${encodeURIComponent(We)}`:""}function ge(){try{const t=window.localStorage;return(t==null?void 0:t.getItem(qt()))||(t==null?void 0:t.getItem(gt))||null}catch{return null}}function Dt(t){var e;if(t)try{(e=window.localStorage)==null||e.setItem(qt(),t)}catch{}}function Re(){var t,e;try{(t=window.localStorage)==null||t.removeItem(qt()),(e=window.localStorage)==null||e.removeItem(gt)}catch{}kn()}const bt="bugcatch.unread";function ga(t){return[(t==null?void 0:t.bugId)||"report",(t==null?void 0:t.kind)||"status",(t==null?void 0:t.at)||""].join("|")}function Fe(){var t;try{const e=(t=window.localStorage)==null?void 0:t.getItem(bt),n=e?JSON.parse(e):[];return Array.isArray(n)?n.filter(o=>o&&typeof o.id=="string"):[]}catch{return[]}}function mt(t){var e,n;try{t.length?(e=window.localStorage)==null||e.setItem(bt,JSON.stringify(t)):(n=window.localStorage)==null||n.removeItem(bt)}catch{}}function ba(t){const e={...t,id:ga(t)},n=Fe().filter(o=>o.id!==e.id);return n.push(e),mt(n.slice(-20)),e}function ma(t){const e=Fe().filter(n=>n.id!==t);return mt(e),e}function ya(t){const e=Fe().filter(n=>n.bugId!==t);return mt(e),e}function kn(){return mt([]),[]}function wa(t,e){let n=t,o=n.indexOf(`
2
-
3
- `);for(;o!==-1;){const i=n.slice(0,o);n=n.slice(o+2);const r=i.split(`
4
- `).filter(s=>s.startsWith("data:")).map(s=>s.slice(5).trim()).join(`
5
- `);if(r)try{e(JSON.parse(r))}catch{}o=n.indexOf(`
6
-
7
- `)}return n}async function va({apiBase:t}){const e=ge();if(!e)return{reporter:null,reports:[]};const n=await fetch(`${t}/ingest/watch/reports${Ht()}`,{headers:{Authorization:`Bearer ${e}`}});if(n.status===401)return Re(),{reporter:null,reports:[]};if(!n.ok)throw new Error(`[BugCatch] could not load your reports (HTTP ${n.status})`);return n.json()}async function xa({apiBase:t,bugId:e}){const n=ge();if(!n)return null;const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}${Ht()}`,{headers:{Authorization:`Bearer ${n}`}});if(o.status===401)return Re(),null;if(o.status===404)return null;if(!o.ok)throw new Error(`[BugCatch] could not load that report (HTTP ${o.status})`);return o.json()}async function Sn({apiBase:t,bugId:e,body:n,attachments:o}){const i=ge();if(!i)throw new Error("[BugCatch] nothing filed from this browser yet.");const r=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/messages`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({...n?{body:n}:{},...o!=null&&o.length?{attachments:o}:{}})});if(r.status===401)throw Re(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!r.ok){let s="";try{const l=await r.json();s=Array.isArray(l.message)?l.message.join(", "):l.message||""}catch{}throw new Error(s||`[BugCatch] could not send that (HTTP ${r.status}).`)}return r.json()}async function ka({apiBase:t,apiKey:e,projectId:n}){const o={agent:!1,live:!1};try{const i=await fetch(`${t}/ingest/calls?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!i.ok)return o;const r=await i.json();return r!=null&&r.available?r.kinds&&typeof r.kinds=="object"?{agent:!1,live:!!r.kinds.live}:{agent:!1,live:r.kind==="live"}:o}catch{return o}}async function Sa({apiBase:t,apiKey:e,token:n,payload:o}){const i=await fetch(`${t}/ingest/calls`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":e,...n?{"X-BugCatch-Session":n}:{}},body:JSON.stringify(o)});if(!i.ok){let r="";try{const l=await i.json();r=Array.isArray(l.message)?l.message.join(", "):l.message||""}catch{}const s=new Error(r||`[BugCatch] the call could not be started (HTTP ${i.status}).`);throw s.status=i.status,s}return i.json()}async function $n({apiBase:t,bugId:e}){const n=ge();if(!n)return!1;try{const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)return!1;const i=await o.json();return!!(i!=null&&i.offered)}catch{return!1}}async function $a({apiBase:t,bugId:e}){const n=ge();if(!n)throw new Error("[BugCatch] nothing filed from this browser yet.");const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{method:"POST",headers:{Authorization:`Bearer ${n}`}});if(o.status===401)throw Re(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!o.ok){let i="";try{const r=await o.json();i=Array.isArray(r.message)?r.message.join(", "):r.message||""}catch{}throw new Error(i||`[BugCatch] the call could not be started (HTTP ${o.status}).`)}return o.json()}async function Ca({apiBase:t,bugId:e,callId:n}){const o=ge();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/give-up`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}async function Ta({apiBase:t,bugId:e,callId:n}){const o=ge();if(!o)throw new Error("[BugCatch] nothing filed from this browser yet.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/answer`,{method:"POST",headers:{Authorization:`Bearer ${o}`}});if(i.status===401)throw Re(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(i.status===409)throw new Error("[BugCatch] that call is no longer ringing.");if(!i.ok){let r="";try{const s=await i.json();r=Array.isArray(s.message)?s.message.join(", "):s.message||""}catch{}throw new Error(r||`[BugCatch] the call could not be answered (HTTP ${i.status}).`)}return i.json()}async function Cn({apiBase:t,bugId:e,callId:n}){const o=ge();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/decline`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}function Ea({apiBase:t,onUpdate:e}){let n=!0,o=0,i=null;async function r(){const s=ge();if(!s)throw new Error("nothing filed from this browser yet");i=new AbortController;const l=await fetch(`${t}/ingest/watch/stream${Ht()}`,{headers:{Authorization:`Bearer ${s}`,Accept:"text/event-stream"},signal:i.signal});if(l.status===401)throw n=!1,Re(),new Error("subscription no longer valid");if(!l.ok||!l.body)throw new Error(`stream failed (${l.status})`);const g=Date.now(),b=l.body.getReader(),m=new TextDecoder;let h="";try{for(;;){const{value:f,done:p}=await b.read();if(p)break;h+=m.decode(f,{stream:!0}),h=wa(h,x=>{if(x.type!=="ping")try{e(x)}catch{}})}}finally{Date.now()-g>=ha&&(o=0)}}return(async()=>{for(;n;){try{await r()}catch(l){if((l==null?void 0:l.name)==="AbortError")return}if(!n)return;const s=xn[Math.min(o,xn.length-1)];o+=1,await new Promise(l=>setTimeout(l,s))}})(),()=>{n=!1,i==null||i.abort(),i=null}}const Ia={OPEN:"is open",IN_PROGRESS:"is being worked on",RESOLVED:"has been fixed",CLOSED:"has been closed"},Ma={status:null,severity:"#e8912a",assignment:"#4da3ff",reply:"#35b87f"};function Tn(t){return t!=null&&t.summary?t.summary:`Your report ${Ia[t==null?void 0:t.status]||"was updated"}`}function La(t){var e;return(t==null?void 0:t.kind)==="reply"&&((e=t.reply)!=null&&e.body)?`${t.reply.author?`${t.reply.author}: `:""}${t.reply.body}`:(t==null?void 0:t.title)||""}function Aa(t){const e=Date.parse(t||"");if(!Number.isFinite(e))return"";const n=Math.round((Date.now()-e)/6e4);if(n<1)return"just now";if(n<60)return`${n}m ago`;const o=Math.round(n/60);if(o<24)return`${o}h ago`;const i=Math.round(o/24);return i===1?"yesterday":`${i}d ago`}function Ba(){var t;try{const e=document.querySelector('[data-bugcatch="widget"]'),n=(t=e==null?void 0:e.shadowRoot)==null?void 0:t.querySelector(".dock"),o=(n==null?void 0:n.getBoundingClientRect().height)||0;return o?Math.round(o)+12:0}catch{return 0}}function ja({accent:t="#4F46E5",position:e="bottom-right",onRead:n=null,onReadAll:o=null,onCall:i=null}={}){const[r,s]=String(e).split("-"),l=r==="top",g=s==="left",b=new Map;let m=null,h=null;function f(){return m!=null&&m.isConnected||(m=document.createElement("div"),m.setAttribute("data-bugcatch","updates"),m.style.cssText=["position:fixed",`${l?"top":"bottom"}:${20+Ba()}px`,`${g?"left":"right"}:20px`,"z-index:2147482998","display:flex","flex-direction:column","gap:10px",`align-items:${g?"flex-start":"flex-end"}`,"width:min(320px,calc(100vw - 40px))","pointer-events:none"].join(";"),document.body.appendChild(m)),m}function p(S){if(!S._bcShown){S.remove();return}S.style.opacity="0",S.style.transform=`translateY(${l?-8:8}px)`,setTimeout(()=>S.remove(),250)}function x(S,M){const I=document.createElement("button");return I.type="button",I.textContent=S,I.style.cssText=["padding:5px 11px",`border:${M?"0":"1px solid rgba(236,238,242,.24)"}`,"border-radius:6px",`background:${M?t:"transparent"}`,`color:${M?"#fff":"#eceef2"}`,"font:600 12px/1.4 inherit","cursor:pointer","white-space:nowrap"].join(";"),I}function A(S){var C;const M=document.createElement("div");M.setAttribute("role","status"),M.style.cssText=["box-sizing:border-box","width:100%","padding:12px 14px","border-radius:10px","background:#1b1e26","color:#eceef2",'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"box-shadow:0 12px 30px rgba(0,0,0,0.45)",`border-left:3px solid ${Ma[S==null?void 0:S.kind]||t}`,"opacity:0",`transform:translateY(${l?-8:8}px)`,"transition:opacity .2s ease,transform .2s ease","pointer-events:auto"].join(";");const I=document.createElement("div");I.style.cssText="font-weight:600;margin-bottom:2px",I.textContent=Tn(S);const j=document.createElement("div");j.style.cssText=(S==null?void 0:S.kind)==="reply"?"color:#a4abba;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden":"color:#a4abba",j.textContent=La(S);const Q=document.createElement("div");Q.style.cssText="display:flex;align-items:center;gap:8px;margin-top:10px;flex-wrap:wrap";const U=document.createElement("span");U.style.cssText="color:#7b8394;font-size:11px;margin-right:auto",U.textContent=Aa(S==null?void 0:S.at);const P=x("Mark as read",!1);if(P.setAttribute("aria-label",`Mark as read: ${Tn(S)}`),P.addEventListener("click",()=>n==null?void 0:n(S.id)),Q.append(U,P),(C=S==null?void 0:S.call)!=null&&C.offered&&typeof i=="function"){const $=x("Talk it through",!0);$.addEventListener("click",()=>i(S)),Q.insertBefore($,P)}return M.append(I,j,Q),M}function z(S){const M=document.createElement("div");M.style.cssText=["box-sizing:border-box","width:100%","display:flex","align-items:center","gap:8px","padding:8px 12px","border-radius:10px","background:#1b1e26","color:#a4abba",'font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"box-shadow:0 12px 30px rgba(0,0,0,0.45)","pointer-events:auto"].join(";");const I=document.createElement("span");I.style.cssText="margin-right:auto",I.textContent=`${S} more update${S===1?"":"s"}`;const j=x("Mark all as read",!1);return j.style.cssText+=";padding:4px 9px",j.addEventListener("click",()=>o==null?void 0:o()),M.append(I,j),M}return{render(S){const M=Array.isArray(S)?S:[];if(!M.length){for(const $ of b.values())p($);b.clear(),h==null||h.remove(),h=null,m==null||m.remove(),m=null;return}const I=M.slice(-3),j=new Set(I.map($=>$.id)),Q=f();for(const[$,_]of b)j.has($)||(p(_),b.delete($));const U=[];for(const $ of I){if(b.has($.id))continue;const _=A($);b.set($.id,_),U.push(_)}const P=l?[...I].reverse():I;for(const $ of P)Q.appendChild(b.get($.id));const C=M.length-I.length;C>0?(h==null||h.remove(),h=z(C),l?Q.appendChild(h):Q.insertBefore(h,Q.firstChild)):(h==null||h.remove(),h=null),requestAnimationFrame(()=>{for(const $ of U)$.isConnected&&($._bcShown=!0,$.style.opacity="1",$.style.transform="translateY(0)")})},destroy(){b.clear(),h=null,m==null||m.remove(),m=null}}}async function Ra({apiBase:t,apiKey:e,projectId:n,sessionToken:o}){try{const i=await fetch(`${t}/ingest/known-issues?projectId=${encodeURIComponent(n)}`,{headers:Nt(e,o)});if(!i.ok)return[];const r=await i.json();return Array.isArray(r==null?void 0:r.issues)?r.issues:[]}catch{return[]}}async function za({apiBase:t,apiKey:e,projectId:n,q:o,sessionToken:i,signal:r}){try{const s=`${t}/ingest/known-issues/match?projectId=${encodeURIComponent(n)}&q=${encodeURIComponent(o)}`,l=await fetch(s,{headers:Nt(e,i),signal:r});if(!l.ok)return[];const g=await l.json();return Array.isArray(g==null?void 0:g.issues)?g.issues:[]}catch{return[]}}async function Oa({apiBase:t,apiKey:e,projectId:n,bugId:o,reporterEmail:i,reporterName:r,metadata:s,logs:l,sessionToken:g}){const b=await fetch(`${t}/ingest/known-issues/${encodeURIComponent(o)}/join`,{method:"POST",headers:Nt(e,g),body:JSON.stringify({projectId:n,reporterEmail:i,reporterName:r,metadata:s,logs:l})});if(!b.ok)throw new Error(await Qa(b));const m=await b.json();return m!=null&&m.watchToken&&Dt(m.watchToken),m}function Nt(t,e){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function Qa(t){try{const e=await t.json(),n=Array.isArray(e.message)?e.message.join(", "):e.message;if(n)return n}catch{}return`Could not add you to this issue (HTTP ${t.status})`}async function qa({apiBase:t,bugId:e,...n}){const o=ge();if(!o)throw new Error("This browser cannot access that report.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/verification`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify(n)}),r=await i.json().catch(()=>({}));if(!i.ok)throw new Error(r.message||"Could not record feedback. Please refresh and try again.");return r}const Ha=2e4,En=120,Ut=200;function In(t){if(typeof t!="string"||!t.trim()||t.length>400)return null;try{return new RegExp(t.trim(),"gi")}catch{return null}}const Mn=t=>String(t).replace(/\s+/g," ").trim().slice(0,En);function Ln(t,e){const n=t&&t.normalize;if(n==="phone"){const o=e.replace(/\D/g,"");return o.length===10?o:o.length===12&&o.startsWith("91")?o.slice(2):o.length===11&&o.startsWith("0")?o.slice(1):e}return n==="upper"?e.toUpperCase():e}function An(t){const e=t.length>1&&t[1]!==void 0?t[1]:t[0];return typeof e!="string"||!e.trim()?null:{value:Mn(e),start:t.index,end:t.index+t[0].length}}function Da(t,e){return((t.slice(e.start,e.valueStart)+t.slice(e.valueEnd,e.end)).match(/[a-z]/gi)||[]).length}const Na=/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[A-Za-z0-9][\w-]{5,40}/gi,Bn=/[\w-]/;function Ua(t,e){const n=(t||[]).filter(f=>f&&f.scan!==!1&&f.pattern),o=[],i=[],r=(e||[]).map(f=>typeof f=="string"?f.slice(0,Ha):"");r.forEach((f,p)=>{f&&n.forEach((x,A)=>{const z=In(x.pattern);if(!z)return;let S,M=0;for(z.lastIndex=0;(S=z.exec(f))!==null&&!(++M>Ut);){if(S[0]===""){z.lastIndex+=1;continue}const I=An(S);if(!I)continue;const j=S.length>1&&S[1]!==void 0?S[1]:S[0],Q=S.index+Math.max(0,S[0].indexOf(j)),U={block:p,order:A,key:x.key,label:x.label,value:Ln(x,I.value),start:I.start,end:I.end,valueStart:Q,valueEnd:Q+j.length};U.weight=Da(f,U),o.push(U)}})});const s=o.filter(f=>!o.some(p=>p!==f&&p.block===f.block&&p.key!==f.key&&p.valueStart<=f.valueStart&&p.valueEnd>=f.valueEnd&&p.weight>f.weight)),l=new Map;for(const f of s){const p=f.value.toLowerCase();l.has(p)||l.set(p,{value:f.value,keys:new Map});const x=l.get(p);x.keys.has(f.key)||x.keys.set(f.key,{key:f.key,label:f.label,order:f.order})}const g=[],b=[],m=new Map;for(const f of l.values()){const p=[...f.keys.values()].sort((z,S)=>z.order-S.order);if(p.length>1){b.push({value:f.value,kinds:p.map(({key:z,label:S})=>({key:z,label:S}))});continue}const[{key:x,label:A}]=p;m.has(x)||m.set(x,[]),m.get(x).push({key:x,label:A,value:f.value})}for(const f of m.values())if(f.length===1)g.push(f[0]);else for(const p of f)b.push({value:p.value,kinds:[{key:p.key,label:p.label}]});const h=new Set(l.keys());return r.forEach((f,p)=>{if(!f)return;const x=o.filter(M=>M.block===p),A=new RegExp(Na.source,"gi");let z,S=0;for(;(z=A.exec(f))!==null&&++S<=Ut;){const M=z.index,I=M+z[0].length;if(x.some($=>M<$.end&&I>$.start))continue;const j=f[M-1]||"",Q=f[I]||"";if(Bn.test(j)||Bn.test(Q)||j==="/"||j==="@"||Q==="@"||j==="."||Q==="."&&/\w/.test(f[I+1]||"")||!/\d/.test(z[0]))continue;const U=Mn(z[0]),P=U.toLowerCase();if(h.has(P))continue;const C=(t||[]).filter($=>$&&jn($,U)).map(({key:$,label:_})=>({key:$,label:_}));C.length&&(h.add(P),i.push({value:U,kinds:C}))}}),{found:g,conflicts:b,loose:i}}function jn(t,e){const n=typeof e=="string"?e.trim():"";if(!n||n.length>En)return null;const o=In(t&&(t.valuePattern||t.pattern));if(!o)return null;let i,r=0;for(o.lastIndex=0;(i=o.exec(n))!==null&&!(++r>Ut);){if(i[0]===""){o.lastIndex+=1;continue}const s=i.index+i[0].length===n.length,l=i.index===0||i.index===1;if(s&&l){const g=An(i);if(g)return Ln(t,g.value)}}return null}async function Ka({apiBase:t,apiKey:e,projectId:n}){const o={policy:"off",fields:[]};try{const i=await fetch(`${t}/ingest/reference-fields?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!i.ok)return o;const r=await i.json(),s=r.policy==="optional"||r.policy==="required"?r.policy:"off",l=Array.isArray(r.fields)?r.fields:[];return l.length?{policy:s,fields:l}:o}catch{return o}}const Pa=/Did this answer your question\?/i;async function _a({apiBase:t,apiKey:e,projectId:n}){try{const o=await fetch(`${t}/ingest/agent-issues?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!o.ok)return[];const i=await o.json();return(Array.isArray(i==null?void 0:i.issues)?i.issues:[]).filter(r=>r&&typeof r.key=="string"&&typeof r.title=="string").map(r=>({key:r.key,title:r.title,hint:typeof r.hint=="string"?r.hint:""}))}catch{return[]}}const Rn={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"},zn=[{value:"LOW",label:"Low"},{value:"MEDIUM",label:"Medium"},{value:"HIGH",label:"High"},{value:"CRITICAL",label:"Critical"}],B={camera:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8.5A1.5 1.5 0 0 1 4.5 7h2.2a1 1 0 0 0 .83-.45l.94-1.4A1 1 0 0 1 9.3 4.7h5.4a1 1 0 0 1 .83.45l.94 1.4a1 1 0 0 0 .83.45h2.2A1.5 1.5 0 0 1 21 8.5v9A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5z"/><circle cx="12" cy="13" r="3.4"/></svg>',video:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="6" width="12.5" height="12" rx="2.2"/><path d="M15.5 10.8l4.2-2.6a.6.6 0 0 1 .92.5v6.6a.6.6 0 0 1-.92.5l-4.2-2.6z"/></svg>',paperclip:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20 11.5l-7.6 7.6a4.6 4.6 0 0 1-6.5-6.5l8-8a3.2 3.2 0 0 1 4.5 4.5l-8 8a1.8 1.8 0 0 1-2.5-2.5l7.1-7.1"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>',mic:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="2.5" width="6" height="11" rx="3"/><path d="M5.5 11a6.5 6.5 0 0 0 13 0M12 17.5V21M9 21h6"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l16-8-6 16-2.6-6.2L4 12Z"/></svg>',stopSquare:'<svg viewBox="0 0 24 24" fill="currentColor"><rect x="7" y="7" width="10" height="10" rx="2"/></svg>',back:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 5.5L8 12l6.5 6.5"/></svg>',phone:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6.2 3.5h3l1.5 3.7-2 1.4a11.5 11.5 0 0 0 5.7 5.7l1.4-2 3.7 1.5v3a1.8 1.8 0 0 1-2 1.8A15.6 15.6 0 0 1 4.4 5.5a1.8 1.8 0 0 1 1.8-2z"/></svg>',phoneOff:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6.2 3.5h3l1.5 3.7-2 1.4a11.5 11.5 0 0 0 5.7 5.7l1.4-2 3.7 1.5v3a1.8 1.8 0 0 1-2 1.8A15.6 15.6 0 0 1 4.4 5.5a1.8 1.8 0 0 1 1.8-2z"/><path d="M3 21L21 3"/></svg>',bug:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M8 6a4 4 0 0 1 8 0"/><rect x="7" y="7.5" width="10" height="12" rx="5"/><path d="M7 11H3.5M7 15.5H4M17 11h3.5M17 15.5h3M12 8v11.5"/></svg>',check:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.8l4.6 4.4L19 7"/></svg>',retry:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 12a8 8 0 1 1-2.6-5.9M20 4v4.5h-4.5"/></svg>',sun:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.6v2.2M12 19.2v2.2M2.6 12h2.2M19.2 12h2.2M5.4 5.4l1.6 1.6M17 17l1.6 1.6M18.6 5.4L17 7M7 17l-1.6 1.6"/></svg>',moon:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13.4A8.2 8.2 0 1 1 10.6 4a6.6 6.6 0 0 0 9.4 9.4z"/></svg>',auto:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="2.8" y="4.4" width="18.4" height="12.6" rx="2.2"/><path d="M8.6 20.2h6.8"/><path d="M12 4.4v12.6"/><path d="M12 7.2a3.6 3.6 0 0 1 0 7.2z" fill="currentColor" stroke="none"/></svg>',sparkle:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.5c.5 4.6 2.9 7 7.5 7.5v.1c-4.6.5-7 2.9-7.5 7.5h-.1c-.5-4.6-2.9-7-7.5-7.5V10c4.6-.5 7-2.9 7.5-7.5z"/><path d="M18.6 15.2c.2 1.9 1.2 2.9 3.1 3.1v.1c-1.9.2-2.9 1.2-3.1 3.1h-.1c-.2-1.9-1.2-2.9-3.1-3.1v-.1c1.9-.2 2.9-1.2 3.1-3.1z" opacity=".7"/></svg>',chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11.5a7.5 7 0 0 1 7.5-7h1a7.5 7 0 0 1 0 14H9l-4.2 2.6a.5.5 0 0 1-.76-.5l.5-3.1A7 7 0 0 1 4 11.5z"/><path d="M8.5 11.5h.01M12 11.5h.01M15.5 11.5h.01" stroke-width="2.6"/></svg>',chevronDown:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9.5l6 6 6-6"/></svg>',chevronRight:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 6l6 6-6 6"/></svg>',alert:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7.5v5.2M12 16.3h.01"/></svg>',inbox:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 13.5l2.6-7.2A1.5 1.5 0 0 1 7.5 5.3h9a1.5 1.5 0 0 1 1.4 1L20.5 13.5V18a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 18z"/><path d="M3.5 13.5h4.8l1.2 2.2h5l1.2-2.2h4.8"/></svg>'},Va='<svg class="bc-brand" viewBox="0 0 254 60" fill="none" role="img" aria-label="sphoro BugCatch"><g fill="currentColor"><path transform="translate(0.00 40.00) scale(0.04000 -0.04000)" d="M276.00 -14.00Q179.00 -14.00 117.00 28.00Q55.00 70.00 42.00 148.00L158.00 178.00Q165.00 143.00 181.50 123.00Q198.00 103.00 222.50 94.50Q247.00 86.00 276.00 86.00Q320.00 86.00 341.00 101.50Q362.00 117.00 362.00 140.00Q362.00 163.00 342.00 175.50Q322.00 188.00 278.00 196.00L250.00 201.00Q198.00 211.00 155.00 228.50Q112.00 246.00 86.00 277.00Q60.00 308.00 60.00 357.00Q60.00 431.00 114.00 470.50Q168.00 510.00 256.00 510.00Q339.00 510.00 394.00 473.00Q449.00 436.00 466.00 376.00L349.00 340.00Q341.00 378.00 316.50 394.00Q292.00 410.00 256.00 410.00Q220.00 410.00 201.00 397.50Q182.00 385.00 182.00 363.00Q182.00 339.00 202.00 327.50Q222.00 316.00 256.00 310.00L284.00 305.00Q340.00 295.00 385.50 278.50Q431.00 262.00 457.50 231.50Q484.00 201.00 484.00 149.00Q484.00 71.00 427.50 28.50Q371.00 -14.00 276.00 -14.00Z"/><path transform="translate(19.56 40.00) scale(0.04000 -0.04000)" d="M70.00 -200.00V496.00H194.00V436.00H212.00Q229.00 465.00 265.00 487.50Q301.00 510.00 368.00 510.00Q428.00 510.00 479.00 480.50Q530.00 451.00 561.00 394.00Q592.00 337.00 592.00 256.00V240.00Q592.00 159.00 561.00 102.00Q530.00 45.00 479.00 15.50Q428.00 -14.00 368.00 -14.00Q323.00 -14.00 292.50 -3.50Q262.00 7.00 243.50 23.50Q225.00 40.00 214.00 57.00H196.00V-200.00ZM330.00 96.00Q389.00 96.00 427.50 133.50Q466.00 171.00 466.00 243.00V253.00Q466.00 325.00 427.00 362.50Q388.00 400.00 330.00 400.00Q272.00 400.00 233.00 362.50Q194.00 325.00 194.00 253.00V243.00Q194.00 171.00 233.00 133.50Q272.00 96.00 330.00 96.00Z"/><path transform="translate(43.68 40.00) scale(0.04000 -0.04000)" d="M70.00 0.00V700.00H196.00V435.00H214.00Q222.00 451.00 239.00 467.00Q256.00 483.00 284.50 493.50Q313.00 504.00 357.00 504.00Q415.00 504.00 458.50 477.50Q502.00 451.00 526.00 404.50Q550.00 358.00 550.00 296.00V0.00H424.00V286.00Q424.00 342.00 396.50 370.00Q369.00 398.00 318.00 398.00Q260.00 398.00 228.00 359.50Q196.00 321.00 196.00 252.00V0.00Z"/><path transform="translate(66.92 40.00) scale(0.04000 -0.04000)" d="M306.00 -14.00Q232.00 -14.00 173.00 16.00Q114.00 46.00 80.00 103.00Q46.00 160.00 46.00 240.00V256.00Q46.00 336.00 80.00 393.00Q114.00 450.00 173.00 480.00Q232.00 510.00 306.00 510.00Q380.00 510.00 439.00 480.00Q498.00 450.00 532.00 393.00Q566.00 336.00 566.00 256.00V240.00Q566.00 160.00 532.00 103.00Q498.00 46.00 439.00 16.00Q380.00 -14.00 306.00 -14.00ZM306.00 98.00Q364.00 98.00 402.00 135.50Q440.00 173.00 440.00 243.00V253.00Q440.00 323.00 402.50 360.50Q365.00 398.00 306.00 398.00Q248.00 398.00 210.00 360.50Q172.00 323.00 172.00 253.00V243.00Q172.00 173.00 210.00 135.50Q248.00 98.00 306.00 98.00Z"/><path transform="translate(90.00 40.00) scale(0.04000 -0.04000)" d="M70.00 0.00V496.00H194.00V440.00H212.00Q223.00 470.00 248.50 484.00Q274.00 498.00 308.00 498.00H368.00V386.00H306.00Q258.00 386.00 227.00 360.50Q196.00 335.00 196.00 282.00V0.00Z"/><path transform="translate(104.44 40.00) scale(0.04000 -0.04000)" d="M306.00 -14.00Q232.00 -14.00 173.00 16.00Q114.00 46.00 80.00 103.00Q46.00 160.00 46.00 240.00V256.00Q46.00 336.00 80.00 393.00Q114.00 450.00 173.00 480.00Q232.00 510.00 306.00 510.00Q380.00 510.00 439.00 480.00Q498.00 450.00 532.00 393.00Q566.00 336.00 566.00 256.00V240.00Q566.00 160.00 532.00 103.00Q498.00 46.00 439.00 16.00Q380.00 -14.00 306.00 -14.00ZM306.00 98.00Q364.00 98.00 402.00 135.50Q440.00 173.00 440.00 243.00V253.00Q440.00 323.00 402.50 360.50Q365.00 398.00 306.00 398.00Q248.00 398.00 210.00 360.50Q172.00 323.00 172.00 253.00V243.00Q172.00 173.00 210.00 135.50Q248.00 98.00 306.00 98.00Z"/></g><rect x="139.52" y="16" width="1.6" height="26" rx="0.8" fill="var(--bc-brand-mark, #5647ee)" opacity="0.4"/><g fill="var(--bc-brand-mark, #5647ee)"><path transform="translate(154.52 35.00) scale(0.01400 -0.01400)" d="M46.35 0.00V105.85H139.75V594.15H46.35V700.00H404.08Q467.38 700.00 514.79 678.41Q562.20 656.83 588.70 617.33Q615.20 577.83 615.20 523.35V513.70Q615.20 465.52 597.03 434.76Q578.85 404.00 553.91 387.24Q528.98 370.48 506.28 363.30V346.35Q528.98 340.35 555.24 323.50Q581.50 306.65 600.00 275.48Q618.50 244.30 618.50 194.48V184.48Q618.50 126.65 591.41 85.24Q564.33 43.83 516.83 21.91Q469.33 0.00 406.85 0.00ZM259.85 109.15H392.60Q441.20 109.15 469.80 132.60Q498.40 156.05 498.40 198.55V208.20Q498.40 250.70 470.21 274.15Q442.02 297.60 392.60 297.60H259.85ZM259.85 406.75H391.65Q437.55 406.75 466.32 430.11Q495.10 453.48 495.10 493.80V503.80Q495.10 544.95 466.74 567.90Q438.37 590.85 391.65 590.85H259.85Z"/><path transform="translate(167.00 35.00) scale(0.01400 -0.01400)" d="M335.30 -14.00Q251.22 -14.00 190.60 16.63Q129.97 47.25 97.56 104.96Q65.15 162.68 65.15 243.03V700.00H185.25V239.73Q185.25 170.48 224.03 131.99Q262.80 93.50 335.30 93.50Q407.80 93.50 446.57 131.99Q485.35 170.48 485.35 239.73V700.00H605.45V243.03Q605.45 162.68 573.13 104.96Q540.80 47.25 480.09 16.63Q419.38 -14.00 335.30 -14.00Z"/><path transform="translate(179.60 35.00) scale(0.01400 -0.01400)" d="M311.85 -14.00Q238.45 -14.00 179.69 18.50Q120.92 51.00 86.69 113.67Q52.45 176.35 52.45 266.87V433.13Q52.45 569.00 128.01 641.50Q203.57 714.00 332.35 714.00Q460.13 714.00 529.23 645.59Q598.33 577.17 598.33 460.87V456.70H479.87V464.52Q479.87 504.67 463.76 536.86Q447.65 569.05 414.99 587.77Q382.32 606.50 332.35 606.50Q257.63 606.50 215.09 560.70Q172.55 514.90 172.55 435.13V264.87Q172.55 185.93 215.09 138.89Q257.63 91.85 334.00 91.85Q410.02 91.85 445.12 132.83Q480.22 173.80 480.22 237.92V249.67H302.85V351.88H598.33V0.00H487.52V69.10H470.57Q462.70 50.87 446.06 31.44Q429.42 12.00 397.64 -1.00Q365.85 -14.00 311.85 -14.00Z"/><path transform="translate(192.05 35.00) scale(0.01400 -0.01400)" d="M332.35 -14.00Q203.57 -14.00 128.01 58.50Q52.45 131.00 52.45 266.87V433.13Q52.45 569.00 128.01 641.50Q203.57 714.00 332.35 714.00Q460.13 714.00 529.58 643.85Q599.03 573.70 599.03 451.57V445.75H480.57V455.22Q480.57 521.42 444.15 563.96Q407.72 606.50 332.35 606.50Q257.63 606.50 215.09 560.70Q172.55 514.90 172.55 435.13V264.87Q172.55 185.93 215.09 139.71Q257.63 93.50 332.35 93.50Q407.72 93.50 444.15 136.45Q480.57 179.40 480.57 244.78V262.25H599.03V248.43Q599.03 126.30 529.58 56.15Q460.13 -14.00 332.35 -14.00Z"/><path transform="translate(204.25 35.00) scale(0.01400 -0.01400)" d="M19.75 0.00 211.62 700.00H421.33L613.20 0.00H489.27L447.42 159.60H185.53L143.68 0.00ZM214.25 270.40H418.70L324.95 624.87H308.00Z"/><path transform="translate(216.31 35.00) scale(0.01400 -0.01400)" d="M233.95 0.00V590.85H24.70V700.00H563.30V590.85H354.05V0.00Z"/><path transform="translate(227.75 35.00) scale(0.01400 -0.01400)" d="M332.35 -14.00Q203.57 -14.00 128.01 58.50Q52.45 131.00 52.45 266.87V433.13Q52.45 569.00 128.01 641.50Q203.57 714.00 332.35 714.00Q460.13 714.00 529.58 643.85Q599.03 573.70 599.03 451.57V445.75H480.57V455.22Q480.57 521.42 444.15 563.96Q407.72 606.50 332.35 606.50Q257.63 606.50 215.09 560.70Q172.55 514.90 172.55 435.13V264.87Q172.55 185.93 215.09 139.71Q257.63 93.50 332.35 93.50Q407.72 93.50 444.15 136.45Q480.57 179.40 480.57 244.78V262.25H599.03V248.43Q599.03 126.30 529.58 56.15Q460.13 -14.00 332.35 -14.00Z"/><path transform="translate(239.95 35.00) scale(0.01400 -0.01400)" d="M69.50 0.00V700.00H189.60V405.58H466.40V700.00H586.50V0.00H466.40V296.42H189.60V0.00Z"/></g></svg>',On=[{value:"light",label:"Light",icon:B.sun},{value:"dark",label:"Dark",icon:B.moon},{value:"auto",label:"System default",icon:B.auto}],Qn="bugcatch:theme";function Wa(){try{const t=localStorage.getItem(Qn);return On.some(e=>e.value===t)?t:null}catch{return null}}function Fa(t){try{localStorage.setItem(Qn,t)}catch{}}const Ya=t=>`
1
+ (function(v,$){typeof exports=="object"&&typeof module<"u"?module.exports=$():typeof define=="function"&&define.amd?define($):(v=typeof globalThis<"u"?globalThis:v||self,v.BugCatch=$())})(this,function(){"use strict";var pt=v=>{throw TypeError(v)};var uo=(v,$,U)=>$.has(v)||pt("Cannot "+U);var gt=(v,$,U)=>$.has(v)?pt("Cannot add the same private member more than once"):$ instanceof WeakSet?$.add(v):$.set(v,U);var m=(v,$,U)=>(uo(v,$,"access private method"),U);var d,ne,mt,K,wt,ke,oe,ie,ve,bt,yt,xt,re,kt;const v="[REDACTED]",$=new Set(["auth","bearer","session","sid","phpsessid","otp","pass","passwd","pwd","sig","codeverifier"]),U=["token","secret","password","passwd","apikey","signature","credential","sessionid","privatekey","accesskey","authorization"],vt=new Set(["key","code","state","nonce","salt","hash"]),Ce=[/eyJ[\w-]{8,}\.[\w-]{8,}\.[\w-]+/g,/(?:AKIA|ASIA|AROA|AIDA|AGPA|ANPA|ANVA|APKA)[0-9A-Z]{16}/g,/AIza[\w-]{35}/g,/gh[pousr]_[A-Za-z0-9]{36,}/g,/github_pat_\w{22,}/g,/[sprk]k_(?:live|test)_[0-9A-Za-z]{10,}/g,/xox[abposr]-[A-Za-z0-9-]{10,}/g,/sk-ant-[\w-]{20,}/g,/sk-[\w-]{20,}/g,/glpat-[\w-]{20,}/g,/shp(?:at|ss|ca|pa)_[A-Za-z0-9]{32}/g],Ct=/\bBearer\s+[\w.~+/=-]{8,}/gi,It=/^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#@]+)@/;function Ie(t){try{return decodeURIComponent(t.replace(/\+/g," "))}catch{return t}}function St(t){return Ie(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function $t(t){return t.length>=20?!0:Ce.some(e=>(e.lastIndex=0,e.test(t)))}function Bt(t,e){const n=St(t);if(!n)return!1;if($.has(n))return!0;for(const o of U)if(n.includes(o))return!0;return vt.has(n)?$t(Ie(e)):!1}function se(t){return t.split("&").map(e=>{const n=e.indexOf("=");if(n===-1)return e;const o=e.slice(0,n),s=e.slice(n+1);return s&&Bt(o,s)?`${o}=${v}`:e}).join("&")}function Se(t){let e=t;for(const n of Ce)n.lastIndex=0,e=e.replace(n,v);return e}function $e(t){if(t==null)return t;let e=String(t);if(!e)return e;e=e.replace(It,`$1${v}@`);const n=e.indexOf("#");let o=n===-1?e:e.slice(0,n),s=n===-1?"":e.slice(n);const i=o.indexOf("?");if(i!==-1&&(o=o.slice(0,i+1)+se(o.slice(i+1))),s.length>1){const r=s.slice(1),a=r.indexOf("?");a!==-1?s=`#${r.slice(0,a+1)}${se(r.slice(a+1))}`:r.includes("=")&&(s=`#${se(r)}`)}return Se(o+s)}function Et(t){if(t==null)return t;let e=String(t);return e&&(e=e.replace(/[A-Za-z][A-Za-z0-9+.-]{0,30}:\/\/[^\s"'<>)\]]+/g,n=>$e(n)),e=e.replace(Ct,`Bearer ${v}`),Se(e))}function W(t){const e=[];return{push(n){e.push(n),e.length>t&&e.splice(0,e.length-t)},all:()=>e.slice(),clear:()=>{e.length=0},get size(){return e.length}}}const Be=["log","info","warn","error","debug"];function Tt(t,{levels:e=["warn","error","log","info"]}={}){const n={};for(const o of Be)n[o]=console[o],e.includes(o)&&(console[o]=(...s)=>{try{t.push({level:o,text:s.map(Ae).join(" ").slice(0,2e3),at:new Date().toISOString()})}catch{}n[o].apply(console,s)});return()=>{for(const o of Be)console[o]=n[o]}}function At(t,{slowMs:e=2e3}={}){const n=window.fetch,o=XMLHttpRequest.prototype.open,s=XMLHttpRequest.prototype.send;return n&&(window.fetch=async function(...r){var h,u,B;const a=performance.now(),c=typeof r[0]=="string"?r[0]:(h=r[0])==null?void 0:h.url,l=((u=r[1])==null?void 0:u.method)||(typeof r[0]=="object"?(B=r[0])==null?void 0:B.method:"GET")||"GET";try{const y=await n.apply(this,r);return ae(t,{url:c,method:l,status:y.status,ms:Math.round(performance.now()-a),slowMs:e}),y}catch(y){throw ae(t,{url:c,method:l,status:0,error:String((y==null?void 0:y.message)||y),ms:Math.round(performance.now()-a),slowMs:e}),y}}),XMLHttpRequest.prototype.open=function(r,a,...c){return this.__bc={method:r,url:a,started:0},o.call(this,r,a,...c)},XMLHttpRequest.prototype.send=function(...r){return this.__bc&&(this.__bc.started=performance.now(),this.addEventListener("loadend",()=>{ae(t,{url:this.__bc.url,method:this.__bc.method,status:this.status,ms:Math.round(performance.now()-this.__bc.started),slowMs:e})})),s.apply(this,r)},()=>{n&&(window.fetch=n),XMLHttpRequest.prototype.open=o,XMLHttpRequest.prototype.send=s}}function ae(t,{url:e,method:n,status:o,ms:s,error:i,slowMs:r}){try{t.push({url:$e(String(e)).slice(0,500),method:n,status:o,ms:s,error:i===void 0?void 0:Et(i),failed:o===0||o>=400,slow:s>=r,at:new Date().toISOString()})}catch{}}function jt(t){const e=o=>{const s=String(o.message||"Error"),i=o.filename?` (${o.filename}:${o.lineno}:${o.colno})`:"";t.push({level:"error",text:(/^uncaught/i.test(s)?s:`Uncaught ${s}`)+i,at:new Date().toISOString()})},n=o=>{t.push({level:"error",text:`Unhandled rejection: ${Ae(o.reason)}`,at:new Date().toISOString()})};return window.addEventListener("error",e),window.addEventListener("unhandledrejection",n),()=>{window.removeEventListener("error",e),window.removeEventListener("unhandledrejection",n)}}async function Ee(){const t=Te();try{if(navigator.getBattery){const e=await navigator.getBattery();t.battery=Math.round(e.level*100),t.charging=e.charging}}catch{}return t}function Te(){var e,n,o,s,i,r,a,c,l;const t=window.navigator||{};return{userAgent:t.userAgent,platform:t.platform,language:t.language,languages:t.languages?[...t.languages]:null,online:t.onLine,cookieEnabled:t.cookieEnabled,maxTouchPoints:t.maxTouchPoints??null,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:(e=window.screen)==null?void 0:e.width,height:(n=window.screen)==null?void 0:n.height,availWidth:(o=window.screen)==null?void 0:o.availWidth,availHeight:(s=window.screen)==null?void 0:s.availHeight,colorDepth:(i=window.screen)==null?void 0:i.colorDepth,orientation:((a=(r=window.screen)==null?void 0:r.orientation)==null?void 0:a.type)||null},devicePixelRatio:window.devicePixelRatio,referrer:document.referrer||null,title:document.title,capturedAt:new Date().toISOString(),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timezoneOffsetMinutes:new Date().getTimezoneOffset(),browserMode:t.webdriver?"Automated":"Normal",doNotTrack:t.doNotTrack||null,hardwareConcurrency:t.hardwareConcurrency??null,deviceMemory:t.deviceMemory??null,connection:t.connection?{effectiveType:t.connection.effectiveType,downlink:t.connection.downlink,rtt:t.connection.rtt,saveData:t.connection.saveData}:null,prefersColorScheme:(c=window.matchMedia)!=null&&c.call(window,"(prefers-color-scheme: dark)").matches?"dark":"light",prefersReducedMotion:((l=window.matchMedia)==null?void 0:l.call(window,"(prefers-reduced-motion: reduce)").matches)||!1}}function Ae(t){if(typeof t=="string")return t;if(t instanceof Error)return`${t.name}: ${t.message}`;try{return JSON.stringify(t)}catch{return String(t)}}const Ot='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9.5l6 6 6-6"/></svg>',Lt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.6v2.2M12 19.2v2.2M2.6 12h2.2M19.2 12h2.2M5.4 5.4l1.6 1.6M17 17l1.6 1.6M18.6 5.4L17 7M7 17l-1.6 1.6"/></svg>',Rt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13.4A8.2 8.2 0 1 1 10.6 4a6.6 6.6 0 0 0 9.4 9.4z"/></svg>',Mt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="2.8" y="4.4" width="18.4" height="12.6" rx="2.2"/><path d="M8.6 20.2h6.8"/><path d="M12 4.4v12.6"/><path d="M12 7.2a3.6 3.6 0 0 1 0 7.2z" fill="currentColor" stroke="none"/></svg>',je='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7.5v5.2M12 16.3h.01"/></svg>',Oe='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>',Kt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.8l4.6 4.4L19 7"/></svg>',Le='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 11v5.5M12 7.7h.01"/></svg>',Ut='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.3 4.2a2 2 0 0 1 3.4 0l7.6 13.1a2 2 0 0 1-1.7 3H4.4a2 2 0 0 1-1.7-3z"/><path d="M12 9.5v4.2M12 17h.01"/></svg>',ce="0.19.0",Nt="__bugcatchChunks",Pt=t=>`${t}@${ce}`;function Re(t){var e;return typeof window<"u"?(e=window[Nt])==null?void 0:e[Pt(t)]:void 0}function le(t,e){return console.warn(`[BugCatch] ${t}`),new Error("[BugCatch] part of the widget could not be loaded — check the connection and try again.")}function Me(t){let e=null;return(...n)=>(e||(e=t(...n).catch(o=>{throw e=null,o})),e)}const E=typeof document<"u"?document.currentScript:null,_t=(E==null?void 0:E.src)||null,Ke=(E==null?void 0:E.nonce)||"",Ue=(E==null?void 0:E.getAttribute("crossorigin"))??null,Ne=(E==null?void 0:E.referrerPolicy)||"";function Dt({chunkBase:t,loadedFrom:e,baseURI:n}){if(t)return new URL(String(t).replace(/\/*$/,"/"),n).href;if(!e)return null;const o=new URL(e);return/\.js$/i.test(o.pathname)?new URL(".",o).href:new URL(`${o.pathname.replace(/\/*$/,"/")}dist/`,o).href}function Pe(t,e){const n=Re(t);if(n)return Promise.resolve(n);const o=Dt({chunkBase:e,loadedFrom:_t,baseURI:document.baseURI});if(!o)return Promise.reject(le(`bugcatch-${t}.umd.js could not be loaded: this copy of the SDK was not loaded from an address, so there is nowhere to look for it. Set init({ chunkBase }) to the folder that holds it.`));const s=`${o}bugcatch-${t}.umd.js?v=${encodeURIComponent(ce)}`;return new Promise((i,r)=>{const a=document.createElement("script");a.src=s,a.async=!0,Ke&&(a.nonce=Ke),Ue!==null&&(a.crossOrigin=Ue),Ne&&(a.referrerPolicy=Ne);const c=()=>{a.onload=null,a.onerror=null,a.remove()};a.onload=()=>{c();const l=Re(t);l?i(l):r(le(`${s} loaded, but not as version ${ce} of the SDK — serve every file in dist/ from the same release.`))},a.onerror=()=>{c(),r(le(`could not load ${s}. The files in dist/ are served together: a self-hosted copy needs all of them (or init({ chunkBase }) pointing at them), and a Content-Security-Policy has to allow them in script-src.`))},(document.head||document.documentElement).appendChild(a)})}const _e=Me(t=>Pe("panel",t)),J=Me(t=>Pe("call",t)),Ht={info:Le,success:Kt,warning:Ut,critical:je};function zt(t,{where:e,onDismiss:n}){const o=document.createElement("div");o.className=`notice tone-${t.tone} at-${e}`,o.setAttribute("role","status");const s=document.createElement("span");s.className="notice-ico",s.setAttribute("aria-hidden","true"),s.innerHTML=Ht[t.tone]||Le;const i=document.createElement("div");i.className="notice-text";const r=document.createElement("strong");if(r.textContent=t.title,i.append(r),t.body){const a=document.createElement("p");a.textContent=t.body,i.append(a)}if(t.ctaHref&&t.ctaLabel){const a=document.createElement("a");a.className="notice-cta",a.href=t.ctaHref,a.textContent=t.ctaLabel,/^https?:/i.test(t.ctaHref)&&(a.target="_blank",a.rel="noopener noreferrer"),i.append(a)}if(o.append(s,i),t.dismissible&&n){const a=document.createElement("button");a.type="button",a.className="notice-x",a.setAttribute("aria-label","Dismiss this notice"),a.innerHTML=Oe,a.addEventListener("click",()=>n(t)),o.append(a)}return o}const De="#0e0e10",ue="#ffffff";function He(t){const e=Ft(t);if(!e)return ue;const[n,o,s]=e.map(r=>{const a=r/255;return a<=.03928?a/12.92:((a+.055)/1.055)**2.4});return .2126*n+.7152*o+.0722*s>.179?De:ue}function Ft(t){const e=String(t||"").trim();let n=/^#([0-9a-f]{3})$/i.exec(e);return n?[...n[1]].map(o=>parseInt(o+o,16)):(n=/^#([0-9a-f]{6})$/i.exec(e),n?[0,2,4].map(o=>parseInt(n[1].slice(o,o+2),16)):(n=/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(e),n?[n[1],n[2],n[3]].map(Number):null))}const ze={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"},H=[{value:"light",label:"Light",icon:Lt},{value:"dark",label:"Dark",icon:Rt},{value:"auto",label:"System default",icon:Mt}],Fe="bugcatch:theme";function Xt(){try{const t=localStorage.getItem(Fe);return H.some(e=>e.value===t)?t:null}catch{return null}}function qt(t){try{localStorage.setItem(Fe,t)}catch{}}const Xe=`
2
+ --bc-bg: #151517;
3
+ --bc-elevated: #1c1c1f;
4
+ --bc-fg: #f3f2ef;
5
+ --bc-muted: #b3b0aa;
6
+ --bc-faint: #8f8c86;
7
+ --bc-border: #2c2c30;
8
+ --bc-subtle: #1f1f22;
9
+ --bc-stone: #26262a;
10
+ --bc-raised: #303035;
11
+ --bc-danger: #ff6b81;
12
+ --bc-ok: #4fd19a;
13
+ --bc-warn: #f2a54a;
14
+ --bc-info: #79a8ff;
15
+ --bc-brand-ink: #b3b0aa;
16
+ --bc-brand-mark: #a59cff;
17
+ --bc-shadow: 0 0 0 1px rgba(255,255,255,.08), 0 30px 70px -20px rgba(0,0,0,.8), 0 8px 20px -8px rgba(0,0,0,.5);
18
+ --bc-shadow-sm: 0 0 0 1px rgba(255,255,255,.07), 0 1px 2px rgba(0,0,0,.4);
19
+ --bc-accent-soft: color-mix(in srgb, var(--bc-accent) 14%, var(--bc-bg));`,Wt=t=>`
8
20
  :host { all: initial; }
9
21
  :host {
10
- --bc-accent: ${t};
11
- --bc-on-accent: #fff;
12
- --bc-bg: #fff;
13
- --bc-elevated: #fff;
14
- --bc-fg: #14161c;
15
- --bc-muted: #5f6673;
16
- --bc-faint: #9aa1ad;
17
- --bc-border: #e6e8ee;
18
- --bc-subtle: #f4f5f8;
19
- --bc-raised: #fff;
20
- --bc-danger: #dc2626;
21
- --bc-ok: #059669;
22
+ --bc-accent: ${t||"var(--bc-fg)"};
23
+ --bc-on-accent: ${t?He(t):"var(--bc-bg)"};
24
+ /* The assistant's orb: BugCatch's own three colours, or the host's accent
25
+ with two lighter cuts of it, so the orb on their site is in their colour. */
26
+ --bc-o1: ${t?`color-mix(in srgb, ${t} 32%, #fff)`:"#ffb3c9"};
27
+ --bc-o2: ${t||"#6a5cff"};
28
+ --bc-o3: ${t?`color-mix(in srgb, ${t} 22%, #ffe7d1)`:"#ffd6a0"};
29
+ --bc-bg: #ffffff;
30
+ --bc-elevated: #ffffff;
31
+ --bc-fg: #0e0e10;
32
+ --bc-muted: #57544e;
33
+ --bc-faint: #75726c;
34
+ --bc-border: #e9e7e3;
35
+ --bc-subtle: #f6f5f3;
36
+ /* Warm stone: fields, chips and the selected track. */
37
+ --bc-stone: #f0efec;
38
+ --bc-raised: #ffffff;
39
+ --bc-danger: #c8233d;
40
+ --bc-ok: #0b7a55;
41
+ --bc-warn: #a4570a;
42
+ --bc-info: #2459d1;
22
43
  /* The lockup's own two colours, kept off --bc-accent on purpose: that one is
23
44
  the customer's brand, and this mark is ours. Values are the light cut of
24
45
  brand/sphoro-bugcatch-primary.svg, pulled up from near-black so the mark
25
46
  identifies itself without out-shouting the host's page. */
26
- --bc-brand-ink: #4b5563;
47
+ --bc-brand-ink: #57544e;
27
48
  --bc-brand-mark: #5647ee;
28
- --bc-shadow: 0 24px 64px rgba(10,12,16,.22), 0 4px 14px rgba(10,12,16,.08);
29
- --bc-shadow-sm: 0 1px 2px rgba(10,12,16,.06), 0 1px 1px rgba(10,12,16,.04);
30
- --bc-dur: 240ms;
49
+ --bc-shadow: 0 0 0 1px rgba(20,18,14,.06), 0 30px 70px -24px rgba(20,18,14,.30), 0 8px 20px -8px rgba(20,18,14,.10);
50
+ --bc-shadow-sm: 0 0 0 1px rgba(20,18,14,.06), 0 1px 2px rgba(20,18,14,.05);
51
+ --bc-dur: 220ms;
31
52
  --bc-ease: cubic-bezier(.22,1,.36,1);
53
+ /* Film grain over the orb, so it reads as light rather than a gradient. */
54
+ --bc-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
32
55
  /* The system everything below is drawn from. Derived tokens are written in
33
56
  terms of the ones above, so a host that overrides --bc-accent or the
34
57
  palette gets a coherent result without touching these. */
35
- --bc-accent-soft: color-mix(in srgb, var(--bc-accent) 10%, var(--bc-bg));
36
- --bc-accent-line: color-mix(in srgb, var(--bc-accent) 32%, var(--bc-border));
37
- --bc-ring: 0 0 0 3px color-mix(in srgb, var(--bc-accent) 22%, transparent);
58
+ --bc-accent-soft: color-mix(in srgb, var(--bc-accent) 7%, var(--bc-bg));
59
+ --bc-accent-line: color-mix(in srgb, var(--bc-accent) 30%, var(--bc-border));
60
+ --bc-ring: 0 0 0 3px color-mix(in srgb, var(--bc-accent) 16%, transparent);
38
61
  --bc-r-sm: 8px;
39
62
  --bc-r-md: 12px;
40
- --bc-r-lg: 18px;
41
- --bc-r-xl: 22px;
63
+ --bc-r-lg: 16px;
64
+ --bc-r-xl: 20px;
42
65
  --bc-fs-xs: 11.5px;
43
66
  --bc-fs-sm: 12.5px;
44
67
  --bc-fs-md: 14px;
45
- --bc-fs-lg: 16px;
68
+ --bc-fs-lg: 15px;
46
69
  --bc-pad: 18px;
47
70
  }
48
- :host(.bc-dark) {
49
- --bc-bg: #14161c;
50
- --bc-elevated: #1c1f27;
51
- --bc-fg: #eef0f4;
52
- --bc-muted: #a3aab6;
53
- --bc-faint: #6f7682;
54
- --bc-border: #2a2e38;
55
- --bc-subtle: #1f222a;
56
- --bc-raised: #363d4b;
57
- --bc-brand-ink: #9aa1ad;
58
- --bc-brand-mark: #9b8fff;
59
- --bc-shadow: 0 24px 64px rgba(0,0,0,.6), 0 4px 14px rgba(0,0,0,.4);
60
- --bc-shadow-sm: 0 1px 2px rgba(0,0,0,.4);
61
- --bc-accent-soft: color-mix(in srgb, var(--bc-accent) 18%, var(--bc-bg));
71
+ :host(.bc-dark) {${Xe}
62
72
  }
63
73
  @media (prefers-color-scheme: dark) {
64
- :host(.bc-auto) {
65
- --bc-bg: #14161c;
66
- --bc-elevated: #1c1f27;
67
- --bc-fg: #eef0f4;
68
- --bc-muted: #a3aab6;
69
- --bc-faint: #6f7682;
70
- --bc-border: #2a2e38;
71
- --bc-subtle: #1f222a;
72
- --bc-raised: #363d4b;
73
- --bc-brand-ink: #9aa1ad;
74
- --bc-brand-mark: #9b8fff;
75
- --bc-shadow: 0 24px 64px rgba(0,0,0,.6), 0 4px 14px rgba(0,0,0,.4);
76
- --bc-shadow-sm: 0 1px 2px rgba(0,0,0,.4);
77
- --bc-accent-soft: color-mix(in srgb, var(--bc-accent) 18%, var(--bc-bg));
74
+ :host(.bc-auto) {${Xe}
78
75
  }
79
76
  }
80
77
 
81
78
  * { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif; }
79
+
80
+ /* ---------- the orb ----------
81
+ The assistant's face, on the launcher and at the top of every screen: three
82
+ blurred colour blobs turning slowly under a highlight and grain. Hue from
83
+ --bc-o1..3. Sized by whoever places it. */
84
+ .orb {
85
+ position: relative; display: block; flex: none; aspect-ratio: 1; border-radius: 50%;
86
+ background: var(--bc-o2); overflow: hidden; isolation: isolate;
87
+ box-shadow: inset -3px -5px 10px rgba(0,0,0,.22), inset 2px 3px 7px rgba(255,255,255,.35);
88
+ }
89
+ .orb::before {
90
+ content: ''; position: absolute; inset: -20%; border-radius: 50%; z-index: -1;
91
+ background:
92
+ radial-gradient(circle at 70% 72%, var(--bc-o3) 0%, transparent 44%),
93
+ radial-gradient(circle at 26% 66%, var(--bc-o1) 0%, transparent 48%),
94
+ radial-gradient(circle at 58% 30%, var(--bc-o2) 0%, transparent 62%);
95
+ filter: blur(3px);
96
+ animation: bc-orb-turn 18s linear infinite;
97
+ }
98
+ .orb::after {
99
+ content: ''; position: absolute; inset: 0; border-radius: 50%;
100
+ background: radial-gradient(circle at 34% 26%, rgba(255,255,255,.85) 0 5%, rgba(255,255,255,0) 30%), var(--bc-grain);
101
+ mix-blend-mode: soft-light;
102
+ }
103
+ @keyframes bc-orb-turn { to { transform: rotate(360deg); } }
82
104
  svg { width: 1em; height: 1em; display: block; }
83
105
  /* The hidden attribute has to actually hide. Nearly every element in here is given a
84
106
  display by a rule below, and any of them beats the attribute's own UA style
@@ -106,422 +128,60 @@ button:focus-visible, a:focus-visible, select:focus-visible, [tabindex]:focus-vi
106
128
  }
107
129
  }
108
130
 
109
- @keyframes bc-fade-in { from { opacity: 0 } to { opacity: 1 } }
110
- @keyframes bc-fade-out { to { opacity: 0 } }
111
- @keyframes bc-panel-in { from { opacity: 0; transform: translateY(14px) scale(.965) } to { opacity: 1; transform: none } }
112
- @keyframes bc-panel-out { to { opacity: 0; transform: translateY(10px) scale(.975) } }
113
- @keyframes bc-sheet-in { from { transform: translateY(100%) } to { transform: none } }
114
- @keyframes bc-sheet-out { to { transform: translateY(100%) } }
115
131
  @keyframes bc-launcher-in { from { opacity: 0; transform: translateY(12px) scale(.86) } to { opacity: 1; transform: none } }
116
132
  @keyframes bc-item-in { from { opacity: 0; transform: translateY(8px) scale(.96) } to { opacity: 1; transform: none } }
117
- @keyframes bc-rise { from { opacity: 0; transform: translateY(6px) } to { opacity: 1; transform: none } }
118
- @keyframes bc-pulse { 0%, 100% { opacity: 1; transform: scale(1) } 50% { opacity: .3; transform: scale(.78) } }
119
- @keyframes bc-spin { to { transform: rotate(360deg) } }
120
- @keyframes bc-pop { 0% { transform: scale(.5); opacity: 0 } 65% { transform: scale(1.1) } 100% { transform: scale(1); opacity: 1 } }
121
- @keyframes bc-nudge { 25% { transform: translateX(-3px) } 75% { transform: translateX(3px) } }
122
- @keyframes bc-typing { 0%, 60%, 100% { transform: translateY(0); opacity: .45 } 30% { transform: translateY(-3px); opacity: 1 } }
123
- @keyframes bc-shimmer { from { background-position: 200% 0 } to { background-position: -200% 0 } }
124
133
 
125
- /* ---------- dock: theme switch stacked above the launcher ---------- */
134
+ /* ---------- dock: the launcher, and what is said beside it ---------- */
126
135
  .dock {
127
136
  position: fixed; z-index: 2147483000;
128
137
  display: flex; flex-direction: column; gap: 10px;
129
138
  }
130
139
  .dock[hidden] { display: none; }
131
- /* While the panel is open the switch rises above the scrim so it stays usable
132
- — the panel shares the shadow root, so a change re-themes the open form. */
140
+ /* While the panel is open the launcher rises above the scrim, so the button
141
+ that opened it is the button that closes it. */
133
142
  .dock.over { z-index: 2147483002; }
134
143
  .dock.at-right { align-items: flex-end; }
135
144
  .dock.at-left { align-items: flex-start; }
136
- /* Anchored to the top, "above the launcher" would be off-screen, so the
137
- switch goes under it and the launcher stays the outermost thing. */
145
+ /* Anchored to the top, "above the launcher" would be off-screen, so cards go
146
+ under it and the launcher stays the outermost thing. */
138
147
  .dock.at-top { flex-direction: column-reverse; }
139
148
 
140
- .themer {
141
- display: inline-flex; align-items: center; gap: 2px;
142
- padding: 3px; border-radius: 999px;
143
- background: var(--bc-elevated); border: 1px solid var(--bc-border);
144
- box-shadow: 0 6px 18px rgba(10,12,16,.14), 0 1px 3px rgba(10,12,16,.08);
145
- animation: bc-launcher-in 420ms var(--bc-ease) both;
146
- }
147
- .themer button {
148
- display: grid; place-items: center;
149
- width: 28px; height: 28px; padding: 0;
150
- border: none; border-radius: 999px; background: transparent;
151
- color: var(--bc-muted); cursor: pointer;
152
- transition: background var(--bc-dur) var(--bc-ease), color var(--bc-dur) var(--bc-ease), transform 140ms var(--bc-ease);
153
- }
154
- .themer button svg { font-size: 15px; }
155
- .themer button:hover { background: var(--bc-subtle); color: var(--bc-fg); }
156
- .themer button:active { transform: scale(.92); }
157
- .themer button[aria-pressed="true"] { background: var(--bc-accent); color: var(--bc-on-accent); }
158
- .themer button[aria-pressed="true"]:hover { background: var(--bc-accent); filter: brightness(1.06); }
159
-
160
- /* ---------- launcher ---------- */
149
+ /* ---------- launcher ----------
150
+ A pill with the assistant's orb in it: ink by default, the host's colour when
151
+ they set one. The shadow is neutral, never tinted, so a bright brand colour
152
+ does not glow on somebody's checkout. */
161
153
  .launcher {
162
154
  position: relative;
163
- display: inline-flex; align-items: center; gap: 9px;
164
- height: 48px; padding: 0 20px 0 16px; border: none; border-radius: 999px;
155
+ display: inline-flex; align-items: center; gap: 10px;
156
+ height: 46px; padding: 0 18px 0 7px; border: none; border-radius: 999px;
165
157
  background: var(--bc-accent); color: var(--bc-on-accent);
166
- font-size: var(--bc-fs-md); font-weight: 620; line-height: 1; letter-spacing: -.01em;
158
+ font-size: var(--bc-fs-md); font-weight: 560; line-height: 1; letter-spacing: -.01em;
167
159
  cursor: pointer;
168
- box-shadow: 0 10px 28px color-mix(in srgb, var(--bc-accent) 34%, rgba(10,12,16,.2)), 0 2px 6px rgba(10,12,16,.16);
169
- transition: transform var(--bc-dur) var(--bc-ease), box-shadow var(--bc-dur) var(--bc-ease), filter 160ms linear, padding var(--bc-dur) var(--bc-ease);
160
+ box-shadow: 0 1px 2px rgba(0,0,0,.18), 0 10px 26px -8px rgba(0,0,0,.36);
161
+ transition: transform var(--bc-dur) var(--bc-ease), box-shadow var(--bc-dur) var(--bc-ease), filter 160ms linear;
170
162
  animation: bc-launcher-in 420ms var(--bc-ease) both;
171
163
  }
172
- /* Two glyphs in one box: the bubble when closed, a chevron when open, turned
164
+ /* Two faces in one circle: the orb when closed, a chevron when open, turned
173
165
  into each other so the button reads as the panel's own handle. */
174
- .launcher .ico { position: relative; width: 20px; height: 20px; flex: none; }
166
+ .launcher .ico { position: relative; width: 32px; height: 32px; flex: none; }
175
167
  .launcher .ico > span {
176
- position: absolute; inset: 0;
168
+ position: absolute; inset: 0; display: grid; place-items: center; border-radius: 50%;
177
169
  transition: transform 320ms var(--bc-ease), opacity 200ms ease;
178
170
  }
179
- .launcher .ico svg { width: 20px; height: 20px; }
171
+ .launcher .ico .orb { width: 32px; }
172
+ .launcher .ico .when-open { background: color-mix(in srgb, var(--bc-on-accent) 14%, transparent); }
173
+ .launcher .ico svg { width: 18px; height: 18px; }
180
174
  .launcher .ico .when-open { opacity: 0; transform: rotate(-90deg) scale(.6); }
181
175
  .launcher[aria-expanded="true"] .ico .when-closed { opacity: 0; transform: rotate(90deg) scale(.6); }
182
176
  .launcher[aria-expanded="true"] .ico .when-open { opacity: 1; transform: none; }
183
- .launcher:hover { transform: translateY(-2px); filter: brightness(1.06); }
177
+ .launcher:hover { transform: translateY(-1px); box-shadow: 0 1px 2px rgba(0,0,0,.18), 0 14px 30px -8px rgba(0,0,0,.42); }
184
178
  .launcher:active { transform: translateY(0) scale(.97); transition-duration: 90ms; }
185
179
  .launcher:focus-visible { outline-offset: 3px; }
186
180
  .launcher[hidden] { display: none; }
187
181
 
188
- /* ---------- scrim + panel ---------- */
189
- .scrim {
190
- position: fixed; inset: 0; z-index: 2147483001;
191
- background: rgba(10,12,16,.32);
192
- animation: bc-fade-in 200ms ease both;
193
- }
194
- .scrim.closing { animation: bc-fade-out 170ms ease both; }
195
-
196
- /* The panel belongs to the launcher, so it opens out of that corner rather
197
- than in the middle of the screen. --bc-dock-h is measured at open time
198
- because the dock's height depends on whether the launcher is showing.
199
- A column: the header and footer stay put and only the middle scrolls. */
200
- .panel {
201
- position: fixed;
202
- display: flex; flex-direction: column;
203
- width: calc(100vw - 40px); max-width: 420px;
204
- max-height: calc(100vh - var(--bc-dock-h, 84px) - 52px);
205
- overflow: hidden;
206
- background: var(--bc-bg); color: var(--bc-fg);
207
- border-radius: var(--bc-r-xl); box-shadow: var(--bc-shadow);
208
- border: 1px solid color-mix(in srgb, var(--bc-border) 70%, transparent);
209
- animation: bc-panel-in 340ms var(--bc-ease) both;
210
- }
211
- /* A conversation wants a height of its own, so the thread fills the panel
212
- rather than shrinking to its first two messages. */
213
- .panel.is-chat { height: 640px; }
214
- .scrim.at-right .panel { right: 20px; transform-origin: bottom right; }
215
- .scrim.at-left .panel { left: 20px; transform-origin: bottom left; }
216
- .scrim.at-bottom .panel { bottom: calc(20px + var(--bc-dock-h, 84px) + 12px); }
217
- .scrim.at-top .panel { top: calc(20px + var(--bc-dock-h, 84px) + 12px); transform-origin: top right; }
218
- .scrim.closing .panel { animation: bc-panel-out 170ms ease both; }
219
-
220
- /* ---------- header ---------- */
221
- .head {
222
- flex: none; position: relative;
223
- display: flex; align-items: center; gap: 12px;
224
- padding: 16px 14px 14px var(--bc-pad);
225
- background: linear-gradient(180deg, var(--bc-accent-soft), var(--bc-bg));
226
- border-bottom: 1px solid var(--bc-border);
227
- }
228
- .head .mark, .avatar {
229
- flex: none; display: grid; place-items: center; position: relative;
230
- width: 38px; height: 38px; border-radius: 50%;
231
- background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));
232
- color: var(--bc-on-accent); font-size: 19px;
233
- box-shadow: 0 4px 12px color-mix(in srgb, var(--bc-accent) 30%, transparent);
234
- }
235
- /* The little "online" dot says someone — or something — is on the other end. */
236
- .head .mark::after {
237
- content: ''; position: absolute; right: -1px; bottom: -1px;
238
- width: 11px; height: 11px; border-radius: 50%;
239
- background: #22c55e; border: 2px solid var(--bc-bg);
240
- }
241
- .head .titles { flex: 1; min-width: 0; }
242
- .head h2 {
243
- margin: 0 0 2px; font-size: var(--bc-fs-lg); font-weight: 680; letter-spacing: -.02em; line-height: 1.25;
244
- overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
245
- }
246
- .head p { margin: 0; font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted); }
247
- .icon-btn {
248
- flex: none; display: grid; place-items: center;
249
- width: 32px; height: 32px; padding: 0; font-size: 16px;
250
- border: none; border-radius: 10px; background: none; color: var(--bc-muted);
251
- cursor: pointer; transition: background 160ms, color 160ms, transform 160ms var(--bc-ease);
252
- }
253
- .icon-btn:hover { background: color-mix(in srgb, var(--bc-fg) 7%, transparent); color: var(--bc-fg); }
254
- .icon-btn:active { transform: scale(.92); }
255
- .icon-btn.back { margin: 0 -6px 0 -8px; }
256
-
257
- .body {
258
- flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
259
- padding: 16px var(--bc-pad) 6px;
260
- }
261
-
262
- label { display: block; font-size: var(--bc-fs-sm); font-weight: 620; color: var(--bc-fg); margin-bottom: 7px; }
263
- label .opt { font-weight: 450; color: var(--bc-faint); }
264
- input[type=text], input[type=email], textarea {
265
- width: 100%; padding: 11px 13px; font-size: var(--bc-fs-md); line-height: 1.45;
266
- color: var(--bc-fg); background: var(--bc-elevated);
267
- border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
268
- transition: border-color 160ms, box-shadow 160ms, background 160ms;
269
- }
270
- input[type=text]::placeholder, input[type=email]::placeholder, textarea::placeholder { color: var(--bc-faint); }
271
- input[type=text]:hover, input[type=email]:hover, textarea:hover { border-color: color-mix(in srgb, var(--bc-fg) 18%, var(--bc-border)); }
272
- input[type=text]:focus, input[type=email]:focus, textarea:focus {
273
- outline: none; border-color: var(--bc-accent); box-shadow: var(--bc-ring);
274
- }
275
- input.invalid, input.invalid:focus, textarea.invalid {
276
- border-color: var(--bc-danger); animation: bc-nudge 240ms ease;
277
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--bc-danger) 16%, transparent);
278
- }
279
- .field-err {
280
- display: flex; align-items: center; gap: 6px;
281
- margin: 7px 0 0; font-size: var(--bc-fs-xs); font-weight: 550; color: var(--bc-danger);
282
- animation: bc-rise 200ms var(--bc-ease) both;
283
- }
284
- .field-err svg { font-size: 13px; flex: none; }
285
- textarea { min-height: 88px; resize: vertical; }
286
- .field { margin-bottom: 16px; }
287
- .field-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
288
- .field-head .count { font-size: var(--bc-fs-xs); color: var(--bc-faint); font-variant-numeric: tabular-nums; }
289
-
290
- /* The draft line: a quiet note, or a card offering to pick up where they left off. */
291
- .compose > .note { font-size: var(--bc-fs-xs); color: var(--bc-faint); margin: -4px 0 10px; }
292
- .compose > .note:empty { display: none; }
293
- .compose > .note:has(button) {
294
- display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
295
- margin: 0 0 14px; padding: 10px 12px; border-radius: var(--bc-r-md);
296
- background: var(--bc-accent-soft); border: 1px solid var(--bc-accent-line);
297
- font-size: var(--bc-fs-sm); color: var(--bc-fg);
298
- }
299
- .compose > .note button {
300
- padding: 5px 11px; font: inherit; font-size: var(--bc-fs-xs); font-weight: 620;
301
- border-radius: 999px; cursor: pointer;
302
- border: 1px solid var(--bc-accent); background: var(--bc-accent); color: var(--bc-on-accent);
303
- }
304
- .compose > .note button[data-discard] { background: transparent; color: var(--bc-muted); border-color: var(--bc-border); }
305
-
306
- /* ---------- what is already known ----------
307
- A help surface in front of the form, not a step in it. Everything here is
308
- dismissible by ignoring it: the reporter can read what is already known and
309
- leave, tap "this is happening to me too" and leave, or type straight past all
310
- of it and file the report they came to file. Nothing is ever in their way,
311
- because a reporter who feels handled rather than helped stops reporting. */
312
- .known { margin-bottom: 16px; }
313
- .known .known-h {
314
- margin: 0 0 8px; font-size: 11px; font-weight: 680; letter-spacing: .06em;
315
- text-transform: uppercase; color: var(--bc-faint);
316
- }
317
- .known .issue {
318
- display: flex; align-items: center; gap: 10px;
319
- padding: 10px 12px; margin-bottom: 6px;
320
- background: var(--bc-subtle); border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
321
- animation: bc-rise 240ms var(--bc-ease) both;
322
- }
323
- .known .issue.is-joined { border-color: color-mix(in srgb, var(--bc-ok) 55%, var(--bc-border)); }
324
- .known .issue .who { flex: 1; min-width: 0; }
325
- .known .issue .t {
326
- display: block; font-size: 13px; font-weight: 620; line-height: 1.4;
327
- color: var(--bc-fg); overflow-wrap: anywhere;
328
- }
329
- .known .issue .meta {
330
- display: block; margin-top: 3px; font-size: var(--bc-fs-xs); line-height: 1.45; color: var(--bc-muted);
331
- }
332
- .known .issue .join {
333
- flex: none; padding: 7px 12px; font-size: 12px; font-weight: 620;
334
- color: var(--bc-accent); background: var(--bc-bg);
335
- border: 1px solid var(--bc-accent-line); border-radius: 999px; cursor: pointer;
336
- transition: background 160ms, border-color 160ms;
337
- }
338
- .known .issue .join:hover { border-color: var(--bc-accent); background: var(--bc-accent-soft); }
339
- .known .issue .join:disabled { color: var(--bc-ok); border-color: var(--bc-border); background: var(--bc-bg); cursor: default; opacity: 1; }
340
- .known .ask { display: flex; gap: 8px; margin: 2px 0 8px; }
341
- .known .ask input { flex: 1; min-width: 0; }
342
- .known .ask button { flex: none; }
343
- .known .err { margin: 6px 0 0; font-size: 12px; line-height: 1.45; color: var(--bc-danger); }
344
-
345
- /* ---------- questions the assistant answers by itself ---------- */
346
- .picks { margin-bottom: 16px; }
347
- .picks .picks-h {
348
- margin: 0 0 8px; font-size: 11px; font-weight: 680; letter-spacing: .06em;
349
- text-transform: uppercase; color: var(--bc-faint);
350
- }
351
- .picks .picks-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px; }
352
- .picks .pick {
353
- display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
354
- padding: 9px 11px; text-align: left; cursor: pointer; font: inherit;
355
- color: var(--bc-fg); background: var(--bc-subtle);
356
- border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
357
- transition: background 160ms, border-color 160ms, transform 160ms;
358
- animation: bc-rise 240ms var(--bc-ease) both;
359
- }
360
- .picks .pick:hover { border-color: var(--bc-accent-line); background: var(--bc-accent-soft); }
361
- .picks .pick[aria-pressed="true"] { border-color: var(--bc-accent); background: var(--bc-accent-soft); }
362
- .picks .pick .t { font-size: 13px; font-weight: 620; line-height: 1.35; overflow-wrap: anywhere; }
363
- .picks .pick .h { font-size: var(--bc-fs-xs); line-height: 1.4; color: var(--bc-muted); }
364
- .picks .picks-note {
365
- display: flex; gap: 6px; align-items: flex-start; margin: 8px 0 0;
366
- font-size: 12px; line-height: 1.45; color: var(--bc-muted);
367
- }
368
- .picks .picks-note svg { flex: none; margin-top: 2px; color: var(--bc-ok); }
369
-
370
- /* ---------- who is reporting ----------
371
- A strip above the form rather than a screen in front of it: where signing in
372
- is optional this is a line the reporter may ignore, and it should read as
373
- one. Where it is required the composer below is not rendered at all and this
374
- strip is the whole panel. */
375
- .whoami {
376
- display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
377
- margin-bottom: 16px; padding: 10px 12px;
378
- font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted);
379
- background: var(--bc-subtle); border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
380
- }
381
- .whoami.asking { display: block; padding: 14px; }
382
- .whoami .who { flex: 1; min-width: 0; }
383
- .whoami .who strong { color: var(--bc-fg); font-weight: 620; word-break: break-all; }
384
- .link {
385
- padding: 0; font: inherit; font-size: var(--bc-fs-sm); font-weight: 620; line-height: 1.45;
386
- color: var(--bc-accent); background: none; border: none; cursor: pointer; border-radius: 4px;
387
- }
388
- .link:hover { text-decoration: underline; text-underline-offset: 2px; }
389
- .link:disabled { color: var(--bc-faint); cursor: default; text-decoration: none; }
390
- .whoami .why { margin: 0 0 10px; font-size: var(--bc-fs-sm); line-height: 1.5; color: var(--bc-muted); }
391
- .whoami .why strong { color: var(--bc-fg); font-weight: 620; word-break: break-all; }
392
- .whoami .row { display: flex; gap: 8px; align-items: stretch; }
393
- .whoami .row input { flex: 1; min-width: 0; }
394
- .whoami .row button { flex: none; }
395
- .whoami .after { display: flex; gap: 14px; align-items: center; margin-top: 9px; }
396
- .whoami .err { color: var(--bc-danger); }
397
- .whoami .code { letter-spacing: .28em; font-variant-numeric: tabular-nums; }
398
-
399
- /* ---------- which record is this about ----------
400
- Drawn only after Send has been pressed and only when what they wrote did not
401
- already answer it, so most people never see this at all. Presented as the
402
- assistant asking one friendly follow-up, not as a form field that appeared. */
403
- .refs {
404
- position: relative;
405
- margin-bottom: 16px; padding: 14px;
406
- font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted);
407
- background: var(--bc-accent-soft);
408
- border: 1px solid var(--bc-accent-line); border-radius: var(--bc-r-lg);
409
- animation: bc-item-in 280ms var(--bc-ease) both;
410
- }
411
- .refs .ask-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 11px; }
412
- .refs .ask-head .avatar { width: 28px; height: 28px; font-size: 14px; box-shadow: none; }
413
- .refs .ask-head strong { display: block; font-size: 13.5px; font-weight: 650; color: var(--bc-fg); margin-bottom: 1px; }
414
- .refs .why { margin: 0; font-size: var(--bc-fs-sm); line-height: 1.5; color: var(--bc-muted); }
415
- .refs .row { display: flex; gap: 8px; align-items: stretch; }
416
- .refs .row input { flex: 1; min-width: 0; background: var(--bc-bg); }
417
- .refs select {
418
- flex: none; max-width: 45%; padding: 10px 10px; font-size: 13px;
419
- color: var(--bc-fg); background: var(--bc-bg);
420
- border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
421
- }
422
- .refs select:focus { outline: none; border-color: var(--bc-accent); box-shadow: var(--bc-ring); }
423
- .refs .chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 9px; }
424
- .refs .chips button {
425
- padding: 5px 11px; font: inherit; font-size: 12px; font-weight: 600; border-radius: 999px; cursor: pointer;
426
- color: var(--bc-muted); background: var(--bc-bg); border: 1px solid var(--bc-border);
427
- transition: background 160ms, color 160ms, border-color 160ms;
428
- }
429
- .refs .chips button[aria-pressed="true"] { color: var(--bc-on-accent); background: var(--bc-accent); border-color: var(--bc-accent); }
430
- .refs .hint { margin: 8px 0 0; font-size: 12px; line-height: 1.45; color: var(--bc-muted); }
431
- .refs .after { display: flex; gap: 12px; align-items: center; margin-top: 9px; }
432
- .refs .err { color: var(--bc-danger); font-weight: 550; }
433
- .refs .link { margin-top: 10px; font-size: 12px; color: var(--bc-muted); }
434
- .refs .link:hover { color: var(--bc-fg); }
435
- .refs .chips.cands button { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 550; }
436
- /* "We read your ID" — under the description, while they type. */
437
- .ref-found {
438
- display: flex; align-items: center; gap: 6px;
439
- margin: -6px 0 14px; font-size: 12px; line-height: 1.45; color: var(--bc-muted);
440
- }
441
- .ref-found svg { width: 14px; height: 14px; flex: none; color: var(--bc-ok); }
442
- .ref-found b { font-weight: 600; color: var(--bc-fg); }
443
-
444
- /* ---------- severity ---------- */
445
- .seg { display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px; padding: 3px; background: var(--bc-subtle); border: 1px solid var(--bc-border); border-radius: var(--bc-r-md); }
446
- .seg button {
447
- display: inline-flex; align-items: center; justify-content: center; gap: 6px;
448
- padding: 8px 4px; font-size: var(--bc-fs-sm); font-weight: 550; line-height: 1;
449
- color: var(--bc-muted); background: none; border: none; border-radius: 9px;
450
- cursor: pointer; transition: background 180ms var(--bc-ease), color 180ms, box-shadow 180ms;
451
- }
452
- .seg button::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--sev, var(--bc-faint)); opacity: .55; transition: opacity 180ms; }
453
- .seg button[data-prio="LOW"] { --sev: #94a3b8; }
454
- .seg button[data-prio="MEDIUM"] { --sev: #3b82f6; }
455
- .seg button[data-prio="HIGH"] { --sev: #f59e0b; }
456
- .seg button[data-prio="CRITICAL"] { --sev: #ef4444; }
457
- .seg button:hover { color: var(--bc-fg); }
458
- .seg button[aria-pressed=true] {
459
- background: var(--bc-raised); color: var(--bc-fg); font-weight: 650;
460
- box-shadow: 0 1px 3px rgba(10,12,16,.14), 0 0 0 1px color-mix(in srgb, var(--bc-border) 60%, transparent);
461
- }
462
- .seg button[aria-pressed=true]::before { opacity: 1; }
463
- .seg button:focus-visible { outline-offset: -2px; }
464
-
465
- /* ---------- attachments ---------- */
466
- .tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 8px; }
467
- .tool {
468
- display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
469
- padding: 12px 8px; font-size: 12px; font-weight: 600; line-height: 1;
470
- color: var(--bc-fg); background: var(--bc-elevated);
471
- border: 1px dashed color-mix(in srgb, var(--bc-fg) 20%, var(--bc-border)); border-radius: var(--bc-r-md); cursor: pointer;
472
- transition: border-color 160ms, background 160ms, color 160ms, transform 160ms var(--bc-ease);
473
- }
474
- .tool svg { font-size: 19px; color: var(--bc-muted); transition: color 160ms; }
475
- .tool:hover:not(:disabled) { border-color: var(--bc-accent); border-style: solid; background: var(--bc-accent-soft); transform: translateY(-1px); }
476
- .tool:hover:not(:disabled) svg { color: var(--bc-accent); }
477
- .tool:active:not(:disabled) { transform: translateY(0) scale(.98); }
478
- .tool:disabled { opacity: .45; cursor: not-allowed; }
479
- .attach-hint { margin: 8px 0 0; font-size: var(--bc-fs-xs); color: var(--bc-faint); }
480
-
481
- .thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
482
- .thumbs:empty { display: none; }
483
- .thumb {
484
- position: relative; width: 76px; height: 76px; flex: none;
485
- border-radius: var(--bc-r-md); overflow: hidden;
486
- border: 1px solid var(--bc-border); background: var(--bc-subtle);
487
- box-shadow: var(--bc-shadow-sm);
488
- animation: bc-item-in 300ms var(--bc-ease) both;
489
- }
490
- .thumb img, .thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }
491
- .thumb .kind {
492
- position: absolute; left: 4px; bottom: 4px;
493
- display: inline-flex; align-items: center; gap: 3px;
494
- padding: 2px 5px; border-radius: 5px;
495
- background: rgba(10,12,16,.72); color: #fff; font-size: 9.5px; font-weight: 600; line-height: 1.3;
496
- }
497
- .thumb .drop {
498
- position: absolute; top: 4px; right: 4px;
499
- width: 20px; height: 20px; font-size: 11px; border-radius: 50%;
500
- background: rgba(10,12,16,.7); color: #fff; border: none;
501
- display: grid; place-items: center; cursor: pointer;
502
- opacity: 0; transition: opacity 160ms, background 160ms;
503
- }
504
- .thumb:hover .drop, .thumb:focus-within .drop { opacity: 1; }
505
- .thumb .drop:hover { background: var(--bc-danger); }
506
- .thumb .veil {
507
- position: absolute; inset: 0; display: grid; place-items: center; gap: 6px;
508
- background: rgba(10,12,16,.62); color: #fff; font-size: 10.5px; font-weight: 600;
509
- align-content: center;
510
- }
511
- .thumb .veil.err { background: color-mix(in srgb, var(--bc-danger) 82%, transparent); }
512
- .spinner {
513
- width: 17px; height: 17px; border-radius: 50%;
514
- border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;
515
- animation: bc-spin 720ms linear infinite;
516
- }
517
- .bar { width: 46px; height: 3px; border-radius: 2px; background: rgba(255,255,255,.3); overflow: hidden; }
518
- .bar i { display: block; height: 100%; width: 0; background: #fff; transition: width 180ms linear; }
519
- .veil button {
520
- background: none; border: none; color: #fff; font-size: 14px; padding: 2px;
521
- cursor: pointer; display: grid; place-items: center;
522
- }
523
-
524
- /* ---------- messages ---------- */
182
+ /* ---------- messages ----------
183
+ The panel's banner, and the load-failure card below — which is drawn when
184
+ the panel's own sheet never arrived, so these rules live in this one. */
525
185
  .msg {
526
186
  display: flex; align-items: flex-start; gap: 8px;
527
187
  font-size: var(--bc-fs-sm); line-height: 1.45; padding: 10px 12px; border-radius: var(--bc-r-md); margin-bottom: 14px;
@@ -531,543 +191,66 @@ textarea { min-height: 88px; resize: vertical; }
531
191
  .msg.err { background: color-mix(in srgb, var(--bc-danger) 10%, var(--bc-bg)); color: var(--bc-danger); border: 1px solid color-mix(in srgb, var(--bc-danger) 30%, transparent); }
532
192
  .msg.info { background: var(--bc-subtle); color: var(--bc-muted); border: 1px solid var(--bc-border); }
533
193
 
534
- /* ---------- footer ---------- */
535
- .foot {
536
- flex: none;
537
- display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
538
- padding: 12px var(--bc-pad); border-top: 1px solid var(--bc-border);
539
- background: var(--bc-bg);
540
- }
541
- .foot .spacer { flex: 1; }
542
- .foot .note { flex-basis: 100%; order: -1; font-size: var(--bc-fs-xs); color: var(--bc-muted); }
543
- .foot .note:empty { display: none; }
544
- /* Quiet, but still legible: this sits in someone else's product, so it identifies
545
- where the report goes without competing with their interface. Quiet is bought
546
- with colour (--bc-brand-ink sits a step below body text) rather than with a
547
- blanket opacity — the BUGCATCH half is only about 3px of cap height at this
548
- size, and dimming it on top of that left it as a smudge in both themes. */
549
- .powered {
550
- flex: none; display: flex; align-items: center; justify-content: center; gap: 6px;
551
- padding: 7px var(--bc-pad) 9px; font-size: 10.5px; color: var(--bc-faint);
552
- background: var(--bc-subtle); border-top: 1px solid var(--bc-border);
553
- }
554
- .brand-credit { display: flex; align-items: center; transition: color 0.15s; border-radius: 4px; }
555
- .bc-brand { height: 16px; width: auto; display: block; color: var(--bc-brand-ink); }
556
- .brand-credit:hover .bc-brand { color: var(--bc-fg); }
557
- button.ghost, button.primary {
558
- display: inline-flex; align-items: center; justify-content: center; gap: 7px;
559
- height: 38px; padding: 0 15px; font: inherit; font-size: 13px; font-weight: 620; line-height: 1;
560
- border-radius: 999px; cursor: pointer; white-space: nowrap;
561
- transition: transform 160ms var(--bc-ease), filter 160ms, background 160ms, border-color 160ms, color 160ms, box-shadow 160ms;
562
- }
563
- button.ghost { background: var(--bc-bg); border: 1px solid var(--bc-border); color: var(--bc-fg); }
564
- button.ghost:hover:not(:disabled) { background: var(--bc-subtle); border-color: color-mix(in srgb, var(--bc-fg) 20%, var(--bc-border)); }
565
- button.ghost svg, button.primary svg { font-size: 15px; }
566
- button.primary {
567
- padding: 0 18px;
568
- background: var(--bc-accent); border: 1px solid var(--bc-accent); color: var(--bc-on-accent);
569
- box-shadow: 0 4px 12px color-mix(in srgb, var(--bc-accent) 30%, transparent);
570
- }
571
- button.primary:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.07); }
572
- button.primary:active:not(:disabled) { transform: none; }
573
- button:disabled { opacity: .5; cursor: not-allowed; }
574
- button.primary:disabled { box-shadow: none; }
575
- button.primary .spinner { border-color: rgba(255,255,255,.4); border-top-color: #fff; width: 14px; height: 14px; }
576
- button.quiet {
577
- padding: 0 4px; height: 38px; font: inherit; font-size: 13px; font-weight: 600;
578
- border: none; background: none; color: var(--bc-muted); cursor: pointer; border-radius: 8px;
579
- }
580
- button.quiet:hover { color: var(--bc-accent); }
581
-
582
- /* ---------- your reports ---------- */
583
- .history {
584
- flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
585
- padding: 8px 12px 14px;
586
- }
587
- /* On the conversation screen the inner thread does the scrolling, so this
588
- wrapper must not — two nested scrollers means the composer drifts out of
589
- reach exactly when someone reaches for it. */
590
- .history.chatting {
591
- overflow: hidden; display: flex; flex-direction: column; padding: 0;
592
- }
593
- .history .row {
594
- display: flex; align-items: center; gap: 12px;
595
- padding: 12px 10px; border-bottom: 1px solid var(--bc-border);
596
- }
597
- .history .row:last-child { border-bottom: 0; }
598
- .history .who { flex: 1; min-width: 0; }
599
- .history .ttl {
600
- display: block; font-size: 13.5px; font-weight: 600; color: var(--bc-fg);
601
- overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
602
- }
603
- .history .sub { display: block; margin-top: 3px; font-size: var(--bc-fs-xs); color: var(--bc-faint); }
604
- .history .row-ico {
605
- flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px;
606
- background: var(--bc-subtle); color: var(--bc-muted); font-size: 16px;
607
- }
608
- .history .chev { flex: none; color: var(--bc-faint); font-size: 15px; transition: transform 160ms var(--bc-ease), color 160ms; }
609
- .empty {
610
- display: flex; flex-direction: column; align-items: center; gap: 10px;
611
- padding: 40px 16px; text-align: center; font-size: var(--bc-fs-sm); color: var(--bc-muted);
612
- }
613
- .empty .empty-ico {
614
- display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
615
- background: var(--bc-accent-soft); color: var(--bc-accent); font-size: 20px;
616
- }
617
- .skel {
618
- display: block; height: 12px; border-radius: 6px; margin: 6px 0;
619
- background: linear-gradient(90deg, var(--bc-subtle) 25%, color-mix(in srgb, var(--bc-fg) 8%, var(--bc-subtle)) 50%, var(--bc-subtle) 75%);
620
- background-size: 200% 100%; animation: bc-shimmer 1.4s linear infinite;
621
- }
622
- /* A history row is a button now — it opens the report — so the UA styling has
623
- to go, and the hit area has to read as one. */
624
- .history button.row {
625
- width: 100%; background: none; border: 0; margin-bottom: 2px;
626
- font: inherit; color: inherit; text-align: left; cursor: pointer;
627
- border-radius: var(--bc-r-md); transition: background 160ms;
628
- animation: bc-rise 260ms var(--bc-ease) both;
629
- }
630
- .history button.row:hover { background: var(--bc-subtle); }
631
- .history button.row:hover .chev { transform: translateX(2px); color: var(--bc-accent); }
632
- .history button.row:focus-visible { outline-offset: -2px; }
633
-
634
- /* ---------- one report, and what happened to it ---------- */
635
- .report-head {
636
- flex: none;
637
- display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: center;
638
- padding: 10px var(--bc-pad); border-bottom: 1px solid var(--bc-border);
639
- background: var(--bc-bg);
640
- }
641
- .report-head .ttl { font-size: 13px; font-weight: 620; color: var(--bc-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
642
- .report-head .sub { font-size: var(--bc-fs-xs); color: var(--bc-faint); }
643
- .report-head .pill { grid-row: 1 / span 2; grid-column: 2; align-self: center; }
644
-
645
- /* The thread scrolls; the composer under it does not. Both live inside the
646
- panel, which stops scrolling as a whole on this screen — a composer that
647
- scrolls away is a composer people think isn't there. */
648
- .thread {
649
- flex: 1 1 auto; min-height: 140px;
650
- display: flex; flex-direction: column; gap: 4px;
651
- padding: 14px var(--bc-pad) 12px; overflow-y: auto; overscroll-behavior: contain;
652
- }
653
-
654
- /* The day a run of messages happened on, as a centred label on a rule. */
655
- .day-sep {
656
- display: flex; align-items: center; gap: 10px; margin: 10px 0 6px;
657
- font-size: 11px; font-weight: 620; letter-spacing: .02em; color: var(--bc-faint);
658
- }
659
- .day-sep::before, .day-sep::after { content: ''; flex: 1; height: 1px; background: var(--bc-border); }
660
-
661
- /* A status change is not a message and is not drawn as one: no side, no
662
- bubble, no author. Centred and quiet, the way every chat marks the things
663
- that happened rather than the things that were said. */
664
- .sys {
665
- align-self: center; max-width: 90%; text-align: center; margin: 6px 0;
666
- padding: 4px 10px; border-radius: 999px; background: var(--bc-subtle);
667
- font-size: var(--bc-fs-xs); line-height: 1.45; color: var(--bc-muted);
668
- animation: bc-fade-in 240ms ease both;
669
- }
670
- .sys .dot {
671
- display: inline-block; width: 6px; height: 6px; margin-right: 6px;
672
- border-radius: 50%; background: var(--bc-muted); vertical-align: middle;
673
- }
674
- .sys .t { color: var(--bc-faint); }
675
- .sys.k-status .dot { background: #4da3ff; }
676
- .sys.k-severity .dot { background: #e8912a; }
677
- .sys.k-assignment .dot { background: #8b7dff; }
678
- .sys.k-filed .dot { background: var(--bc-faint); }
679
-
680
- /* ---------- the conversation ----------
681
-
682
- A status change and a message are different things and are drawn as such: a
683
- change is a line on the rail above, a message is a bubble with a side. The
684
- reporter's own words sit right and accented, the other side's left beside an
685
- avatar that says who it is — the arrangement every chat has taught people to
686
- read without a legend. */
687
- .msg-row, .typing-row {
688
- display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: 8px;
689
- max-width: 88%; margin-top: 10px;
690
- animation: bc-rise 260ms var(--bc-ease) both;
691
- }
692
- .msg-row.cont { margin-top: 3px; }
693
- .msg-row > *, .typing-row > * { grid-column: 2; }
694
- /* Fixed rows, so the avatar lines up with the bubble whatever else the
695
- message carries (a name above it, answers or a time below it). */
696
- .msg-row > .msg-who, .typing-row > .msg-who { grid-row: 1; }
697
- .msg-row > .bubble, .typing-row > .bubble { grid-row: 2; }
698
- .msg-row > .quick { grid-row: 3; }
699
- .msg-row > .msg-when { grid-row: 4; }
700
- .msg-row > .av, .typing-row > .av { grid-column: 1; grid-row: 2; align-self: end; }
701
- /* In a run of messages the avatar sits by the last bubble and the time under
702
- it, the way a chat marks where one turn ends. */
703
- .msg-row:has(+ .msg-row.cont) > .av { visibility: hidden; }
704
- .msg-row:has(+ .msg-row.cont) > .msg-when { display: none; }
705
- .msg-row.theirs, .typing-row { align-self: flex-start; }
706
- .msg-row.mine { align-self: flex-end; grid-template-columns: minmax(0, 1fr); justify-items: end; }
707
- .msg-row.mine > * { grid-column: 1; }
708
- .av {
709
- display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
710
- font-size: 11px; font-weight: 700; letter-spacing: .02em;
711
- background: color-mix(in srgb, var(--bc-fg) 10%, var(--bc-bg)); color: var(--bc-fg);
712
- }
713
- .av.bot {
714
- background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));
715
- color: var(--bc-on-accent); font-size: 14px;
716
- }
717
- .msg-who { font-size: var(--bc-fs-xs); font-weight: 620; color: var(--bc-muted); padding: 0 4px 4px; }
718
- .msg-who.auto { color: var(--bc-accent); display: inline-flex; align-items: center; gap: 5px; }
719
- .msg-who .tag {
720
- padding: 1px 6px; border-radius: 999px; font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
721
- text-transform: uppercase; background: var(--bc-accent-soft); color: var(--bc-accent);
722
- }
723
- .bubble {
724
- justify-self: start; max-width: 100%;
725
- padding: 9px 13px; border-radius: 18px; font-size: 13.5px; line-height: 1.5;
726
- background: var(--bc-subtle); color: var(--bc-fg);
727
- border: 1px solid var(--bc-border);
728
- border-bottom-left-radius: 6px;
729
- }
730
- /* pre-wrap belongs to the text, not the box — on the box it would also render
731
- the markup's own indentation. */
732
- .msg-text { display: block; white-space: pre-wrap; overflow-wrap: anywhere; }
733
- .msg-row.mine .bubble {
734
- justify-self: end;
735
- background: var(--bc-accent); border-color: var(--bc-accent); color: var(--bc-on-accent);
736
- border-bottom-left-radius: 18px; border-bottom-right-radius: 6px;
737
- }
738
- .msg-row.cont.theirs .bubble { border-top-left-radius: 6px; }
739
- .msg-row.cont.mine .bubble { border-top-right-radius: 6px; }
740
- /* A reply nobody wrote: tinted with the accent and labelled "Assistant" above
741
- it, so it reads the same to someone who cannot see the colour at all. */
742
- .msg-row.auto .bubble { background: var(--bc-accent-soft); border-color: var(--bc-accent-line); }
743
- /* An attachment-only message has no text to wrap, so the bubble would be an
744
- empty coloured box around it. */
745
- .bubble.silent { padding: 0; background: none !important; border: 0; }
746
- .msg-when { font-size: 10.5px; color: var(--bc-faint); padding: 4px 4px 0; }
747
- .msg-row.pending { opacity: .6; }
748
- .msg-row.pending .msg-when::after { content: ' · Sending…'; }
749
- .msg-row.failed .bubble { border: 1px solid var(--bc-danger); }
750
- .msg-row.failed .msg-when { color: var(--bc-danger); }
751
- .msg-row.failed .msg-when::after { content: ' · Not sent'; }
752
-
753
- /* The assistant, thinking. Three dots in a bubble where its next message will be. */
754
- .typing { display: inline-flex; align-items: center; gap: 4px; padding: 12px 14px; justify-self: start; }
755
- .typing i {
756
- width: 7px; height: 7px; border-radius: 50%; background: var(--bc-accent); opacity: .45;
757
- animation: bc-typing 1.2s ease-in-out infinite;
758
- }
759
- .typing i:nth-child(2) { animation-delay: .15s; }
760
- .typing i:nth-child(3) { animation-delay: .3s; }
761
- .typing-row .msg-when { padding-top: 4px; }
762
-
763
- .att { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
764
- .bubble.silent .att { margin-top: 0; }
765
- .att img {
766
- display: block; max-width: 100%; max-height: 190px; border-radius: var(--bc-r-md);
767
- border: 1px solid var(--bc-border); object-fit: cover; cursor: zoom-in;
768
- }
769
- .att audio { width: 232px; max-width: 100%; height: 34px; }
770
- .att video { max-width: 100%; max-height: 190px; border-radius: var(--bc-r-md); }
771
- .att .file {
772
- display: flex; align-items: center; gap: 8px; text-decoration: none;
773
- padding: 8px 10px; border-radius: 10px; font-size: 12px;
774
- background: var(--bc-elevated); border: 1px solid var(--bc-border); color: var(--bc-fg);
775
- }
776
- .att .file:hover { border-color: var(--bc-accent); }
777
- .att .file .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
778
- .att .file .sz { flex: none; color: var(--bc-faint); font-size: 11px; }
779
-
780
- /* ---------- the assistant's live card ----------
781
- A status header pinned between the report and the conversation: where the
782
- investigation is, as words and as a stepper. It does not scroll with the
783
- thread, because "is anything happening?" is the question on screen the
784
- whole time. */
785
- .assistant {
786
- flex: none; position: relative; overflow: hidden;
787
- margin: 10px 12px 0; padding: 12px 14px 12px;
788
- border-radius: var(--bc-r-lg); border: 1px solid var(--bc-border);
194
+ /* ---------- announcements ----------
195
+ What the project's team is telling visitors (notice.js): beside the launcher
196
+ for the ones written for that, and at the top of the panel for all of them.
197
+ In this sheet because the card beside the button exists before the panel's
198
+ does. The tone is a colour and an icon; the site's accent is the info one. */
199
+ .notice {
200
+ --tone: var(--bc-info);
201
+ display: flex; align-items: flex-start; gap: 10px;
202
+ padding: 11px 10px 11px 12px; border-radius: var(--bc-r-md);
789
203
  background: var(--bc-subtle);
790
- transition: background 400ms ease, border-color 400ms ease;
791
- }
792
- .assistant.analyzing, .assistant.confirming, .assistant.needs_info {
793
- background: var(--bc-accent-soft); border-color: var(--bc-accent-line);
794
- }
795
- .assistant.resolved { background: color-mix(in srgb, var(--bc-ok) 9%, var(--bc-bg)); border-color: color-mix(in srgb, var(--bc-ok) 35%, var(--bc-border)); }
796
- /* On a call: the call's own card is what they are watching. This one states
797
- what is happening and stops moving — no spinner, no travelling bar. */
798
- .assistant.calling { background: var(--bc-bg); }
799
- .assistant.calling .as-progress i { transition: none; }
800
- .as-body.entering { animation: bc-rise 360ms var(--bc-ease) both; }
801
- .as-top { display: flex; gap: 11px; align-items: center; }
802
- .as-orb {
803
- flex: none; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
804
- color: var(--bc-on-accent); font-size: 15px;
805
- background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));
806
- }
807
- .assistant.resolved .as-orb { background: var(--bc-ok); }
808
- .assistant.reviewing .as-orb { background: color-mix(in srgb, var(--bc-fg) 55%, var(--bc-bg)); }
809
- .as-spin { width: 16px; height: 16px; border-radius: 50%; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; animation: as-rot .8s linear infinite; }
810
- @keyframes as-rot { to { transform: rotate(360deg); } }
811
- .as-text { min-width: 0; flex: 1; }
812
- .as-title { font-weight: 650; font-size: 13.5px; letter-spacing: -.01em; color: var(--bc-fg); }
813
- .as-detail { font-size: 12px; line-height: 1.45; color: var(--bc-muted); margin-top: 2px; }
814
- .as-progress {
815
- height: 4px; margin: 12px 0 0; border-radius: 999px; overflow: hidden;
816
- background: color-mix(in srgb, var(--bc-fg) 9%, transparent);
817
- }
818
- .as-progress i {
819
- display: block; height: 100%; border-radius: inherit; background: var(--bc-accent);
820
- transition: width 700ms var(--bc-ease);
821
- }
822
- .assistant.resolved .as-progress i { background: var(--bc-ok); }
823
- .assistant.analyzing .as-progress i {
824
- background: linear-gradient(90deg, var(--bc-accent) 0%, color-mix(in srgb, var(--bc-accent) 45%, #fff) 50%, var(--bc-accent) 100%);
825
- background-size: 200% 100%; animation: bc-shimmer 1.6s linear infinite;
826
- }
827
- .as-steps {
828
- list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
829
- margin: 8px 0 0; padding: 0; font-size: 10.5px; color: var(--bc-faint);
830
- }
831
- .as-steps li { display: flex; align-items: center; gap: 5px; min-width: 0; white-space: nowrap; }
832
- .as-steps li .lbl { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
833
- .as-steps li .dot { flex: none; width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid currentColor; }
834
- .as-steps li.done { color: var(--bc-muted); }
835
- .as-steps li.done .dot { background: var(--bc-accent); border-color: var(--bc-accent); }
836
- .assistant.resolved .as-steps li.done .dot { background: var(--bc-ok); border-color: var(--bc-ok); }
837
- .as-steps li.active { color: var(--bc-fg); font-weight: 650; }
838
- .as-steps li.active .dot { border-color: var(--bc-accent); background: var(--bc-bg); animation: as-pulse 1.2s ease-in-out infinite; }
839
- @keyframes as-pulse { 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--bc-accent) 25%, transparent); } }
840
- @media (prefers-reduced-motion: reduce) { .as-spin, .as-steps li.active .dot, .assistant.analyzing .as-progress i, .typing i { animation: none; } }
841
-
842
- /* Quick answers under the confirm question — only on the latest message. */
843
- .msg-row .quick { display: none; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
844
- .msg-row:last-child .quick { display: flex; }
845
- .quick button {
846
- display: inline-flex; align-items: center; gap: 6px;
847
- font: inherit; font-size: 13px; font-weight: 600; padding: 8px 14px; border-radius: 999px; cursor: pointer;
848
- border: 1px solid var(--bc-accent); background: var(--bc-bg); color: var(--bc-accent);
849
- transition: background 160ms, color 160ms, transform 160ms var(--bc-ease);
850
- animation: bc-item-in 300ms var(--bc-ease) both;
851
- }
852
- .quick button:first-child { background: var(--bc-accent); color: var(--bc-on-accent); }
853
- .quick button:nth-child(2) { animation-delay: 60ms; }
854
- .quick button svg { font-size: 13px; }
855
- .quick button:hover { transform: translateY(-1px); background: var(--bc-accent-soft); }
856
- .quick button:first-child:hover { background: var(--bc-accent); filter: brightness(1.07); }
857
-
858
- /* ---------- the composer ---------- */
859
- .composer {
860
- flex: none; position: relative;
861
- padding: 10px 12px max(12px, env(safe-area-inset-bottom, 0px));
862
- /* The panel's own background, not --bc-elevated: the two are the same white
863
- in light mode but differ in dark, where a raised composer reads as a band
864
- laid over the panel rather than the foot of it. */
865
- background: var(--bc-bg);
866
- border-top: 1px solid var(--bc-border);
867
- }
868
- /* One rounded field holding the tools, the text and the send button — the
869
- shape every chat box has, so nobody has to look for where to type. */
870
- .composer .row {
871
- display: flex; align-items: flex-end; gap: 4px;
872
- padding: 5px; border-radius: 22px;
873
- background: var(--bc-subtle); border: 1px solid var(--bc-border);
874
- transition: border-color 160ms, box-shadow 160ms;
875
- }
876
- .composer .row:focus-within { border-color: var(--bc-accent); box-shadow: var(--bc-ring); }
877
- .composer textarea {
878
- flex: 1; min-width: 0; min-height: 34px; max-height: 120px; resize: none; padding: 7px 6px;
879
- font: inherit; font-size: 13.5px; line-height: 1.45;
880
- color: var(--bc-fg); background: transparent; border: 0; border-radius: 0;
881
- }
882
- .composer textarea:hover, .composer textarea:focus { border: 0; box-shadow: none; outline: none; }
883
- .composer .tools { display: flex; gap: 0; grid-template-columns: none; }
884
- .composer .tool {
885
- width: 34px; height: 34px; min-height: 0; padding: 0; display: grid; place-items: center; cursor: pointer;
886
- border-radius: 50%; border: 0; background: none; font-size: 17px;
887
- color: var(--bc-muted);
888
- }
889
- .composer .tool svg { font-size: 17px; }
890
- .composer .tool:hover:not(:disabled) { background: color-mix(in srgb, var(--bc-fg) 8%, transparent); color: var(--bc-fg); border: 0; transform: none; }
891
- .composer .tool:hover:not(:disabled) svg { color: var(--bc-fg); }
892
- .composer .tool:disabled { opacity: .4; cursor: default; }
893
- .composer .tool.recording { color: var(--bc-danger); background: color-mix(in srgb, var(--bc-danger) 12%, transparent); }
894
- .composer .tool.recording svg { color: var(--bc-danger); }
895
- .composer .send {
896
- width: 34px; height: 34px; flex: none; border-radius: 50%; cursor: pointer; font-size: 16px;
897
- background: var(--bc-accent); border: 0; color: var(--bc-on-accent);
898
- display: grid; place-items: center;
899
- transition: transform 160ms var(--bc-ease), opacity 160ms, filter 160ms;
900
- }
901
- .composer .send:hover:not(:disabled) { filter: brightness(1.08); transform: scale(1.05); }
902
- .composer .send:disabled { opacity: .35; cursor: default; background: var(--bc-faint); }
903
- .composer .staged { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 0 8px; }
904
- .composer .chip {
905
- display: flex; align-items: center; gap: 6px; max-width: 200px;
906
- padding: 5px 7px 5px 10px; border-radius: 999px; font-size: var(--bc-fs-xs);
907
- background: var(--bc-subtle); border: 1px solid var(--bc-border); color: var(--bc-muted);
204
+ border: 1px solid var(--bc-border);
205
+ color: var(--bc-fg);
206
+ font-size: var(--bc-fs-sm); line-height: 1.45; text-align: left;
908
207
  animation: bc-item-in 240ms var(--bc-ease) both;
909
208
  }
910
- .composer .chip .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--bc-fg); }
911
- .composer .chip button {
912
- flex: none; width: 18px; height: 18px; display: grid; place-items: center;
913
- border: 0; border-radius: 50%; background: none; color: var(--bc-faint); cursor: pointer;
914
- }
915
- .composer .chip button:hover { color: var(--bc-danger); }
916
- .composer .chip.uploading { border-style: dashed; }
917
- .composer .note { padding: 6px 6px 0; font-size: var(--bc-fs-xs); color: var(--bc-muted); }
918
- .composer .note.err { color: var(--bc-danger); }
919
- .reccount { font-variant-numeric: tabular-nums; }
920
-
921
- /* Status carries the only colour in this list, so it is the thing the eye
922
- lands on — which is what someone opening this screen came to check. */
923
- .pill {
924
- flex: none; display: inline-flex; align-items: center; gap: 5px;
925
- font-size: 11px; font-weight: 650;
926
- padding: 3px 9px; border-radius: 999px;
927
- color: var(--bc-muted); background: var(--bc-subtle);
928
- }
929
- .pill::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
930
- .pill.open { color: #c2410c; background: color-mix(in srgb, #f59e0b 14%, var(--bc-bg)); }
931
- .pill.in_progress { color: #1d6fd1; background: color-mix(in srgb, #3b82f6 14%, var(--bc-bg)); }
932
- .pill.resolved { color: #0f8a5a; background: color-mix(in srgb, #10b981 14%, var(--bc-bg)); }
933
- .pill.closed { color: var(--bc-muted); }
934
- :host(.bc-dark) .pill.open { color: #fbbf24; }
935
- :host(.bc-dark) .pill.in_progress { color: #7cb8ff; }
936
- :host(.bc-dark) .pill.resolved { color: #4ade9a; }
937
- @media (prefers-color-scheme: dark) {
938
- :host(.bc-auto) .pill.open { color: #fbbf24; }
939
- :host(.bc-auto) .pill.in_progress { color: #7cb8ff; }
940
- :host(.bc-auto) .pill.resolved { color: #4ade9a; }
941
- }
942
-
943
- /* ---------- does the fix work ---------- */
944
- .fix-verification {
945
- flex: none; margin: 10px 12px 0; padding: 12px 14px;
946
- border-radius: var(--bc-r-lg); border: 1px solid color-mix(in srgb, var(--bc-ok) 35%, var(--bc-border));
947
- background: color-mix(in srgb, var(--bc-ok) 8%, var(--bc-bg));
948
- font-size: var(--bc-fs-sm); color: var(--bc-fg);
949
- animation: bc-rise 280ms var(--bc-ease) both;
950
- }
951
- .fix-verification p { margin: 0 0 8px; font-weight: 620; }
952
- .fix-verification textarea { min-height: 56px; margin-bottom: 8px; font-size: 13px; background: var(--bc-bg); }
953
- .fix-verification button {
954
- padding: 7px 13px; font: inherit; font-size: 12.5px; font-weight: 620; border-radius: 999px; cursor: pointer;
955
- border: 1px solid var(--bc-border); background: var(--bc-bg); color: var(--bc-fg);
956
- }
957
- .fix-verification button[data-result="CONFIRMED"] { background: var(--bc-ok); border-color: var(--bc-ok); color: #fff; }
958
- .fix-verification .feedback-status { margin: 6px 0 0; font-weight: 450; color: var(--bc-danger); font-size: var(--bc-fs-xs); }
959
- .fix-verification .feedback-status:empty { display: none; }
960
-
961
- /* A call in progress, in place of the form.
962
- ---------------------------------------------------------------------------
963
- Deliberately not styled as a chat. Nothing arrives here to read — the
964
- conversation is happening out loud — so this is a state, a line of
965
- reassurance about the report, and a way to hang up. */
966
- .call { display: flex; flex-direction: column; padding: 26px 0 0; animation: bc-fade-in 240ms ease both; }
967
- .call-head, .call-note { padding: 0 var(--bc-pad); }
968
- .call-head { display: flex; align-items: center; gap: 9px; margin-bottom: 8px; }
969
- .call-head strong { font-size: 15.5px; font-weight: 680; letter-spacing: -.01em; }
970
- .call-dot {
971
- width: 9px; height: 9px; border-radius: 50%; background: var(--bc-accent);
972
- animation: bc-call-pulse 1.6s ease-in-out infinite;
973
- }
974
- .call-dot.off { background: var(--bc-faint); animation: none; }
975
- @keyframes bc-call-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
976
- /* Said while it is still ringing, because that is when somebody is deciding
977
- whether it is worth waiting. */
978
- .call-note { margin: 0 0 18px; font-size: 13px; color: var(--bc-muted); line-height: 1.55; }
979
-
980
- /* ---------- the same call, on top of the report it is about ---------- */
981
- /* A bar rather than its own screen, unlike the one the composer opens. The
982
- thread underneath is exactly what both people are talking about, so taking it
983
- off screen would be taking away the only shared reference. */
984
- .livecall {
985
- flex: none;
986
- display: flex; align-items: center; gap: 9px;
987
- margin: 10px 12px 0; padding: 9px 11px;
988
- border: 1px solid var(--bc-accent-line); border-radius: var(--bc-r-md);
989
- background: var(--bc-accent-soft); font-size: var(--bc-fs-sm); color: var(--bc-fg);
990
- animation: bc-rise 240ms var(--bc-ease) both;
991
- }
992
- .livecall .who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
993
- .livecall .call-kept { font-size: 11px; opacity: .75; }
994
- .livecall .secs { font-variant-numeric: tabular-nums; color: var(--bc-muted); }
995
- .livecall .act {
996
- padding: 5px 11px; border-radius: 999px; border: 1px solid var(--bc-border);
997
- background: var(--bc-bg); color: var(--bc-fg);
998
- font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
999
- }
1000
- .livecall .act:hover { background: var(--bc-subtle); }
1001
- .livecall .act.danger { border-color: transparent; background: var(--bc-danger); color: #fff; }
1002
- .livecall.over { color: var(--bc-muted); background: var(--bc-subtle); border-color: var(--bc-border); }
1003
- .call-note code { font-size: 11px; }
1004
- @media (prefers-reduced-motion: reduce) { .call-dot { animation: none; } }
1005
-
1006
- /* div.done, not .done: the assistant's stepper marks finished steps li.done,
1007
- and this screen's padding was landing on every one of them. */
1008
- div.done { padding: 44px 26px 44px; text-align: center; animation: bc-fade-in 240ms ease both; }
1009
- .done .tick {
1010
- width: 56px; height: 56px; margin: 0 auto 16px; border-radius: 50%;
1011
- display: grid; place-items: center; font-size: 26px;
1012
- background: color-mix(in srgb, var(--bc-ok) 14%, transparent); color: var(--bc-ok);
1013
- box-shadow: 0 0 0 8px color-mix(in srgb, var(--bc-ok) 6%, transparent);
1014
- animation: bc-pop 420ms var(--bc-ease) both;
1015
- }
1016
- .done h3 { margin: 0 0 6px; font-size: 17px; font-weight: 680; letter-spacing: -.015em; }
1017
- .done p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--bc-muted); }
1018
- .done code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; padding: 1px 5px; border-radius: 5px; background: var(--bc-subtle); color: var(--bc-fg); }
1019
- /* The likely wait. Set apart from the reference line above it by a rule rather
1020
- than by weight: one is bookkeeping and the other is the answer to the
1021
- question the reporter actually has, and they should not read as one
1022
- paragraph. */
1023
- .done .eta {
1024
- margin: 14px auto 0;
1025
- padding-top: 12px;
1026
- border-top: 1px solid var(--bc-border);
1027
- max-width: 30ch;
1028
- line-height: 1.5;
1029
- }
1030
- .done .eta strong { color: var(--bc-fg); font-weight: 620; }
1031
- /* The same figure on the report the reporter came back to. Sits under the
1032
- status pill in the header block, muted — it is context, not the headline the
1033
- status is. */
1034
- .report-head .eta-line {
1035
- grid-column: 1 / -1;
1036
- font-size: var(--bc-fs-xs);
1037
- color: var(--bc-muted);
1038
- margin-top: 2px;
1039
- }
1040
-
1041
- /* ---------- recording bar ---------- */
1042
- .recbar {
1043
- position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%);
1044
- z-index: 2147483002;
1045
- display: flex; align-items: center; gap: 12px;
1046
- padding: 8px 8px 8px 16px; border-radius: 999px;
1047
- background: var(--bc-elevated); color: var(--bc-fg);
1048
- border: 1px solid var(--bc-border); box-shadow: var(--bc-shadow);
1049
- animation: bc-item-in 300ms var(--bc-ease) both;
1050
- }
1051
- .recbar .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--bc-danger); animation: bc-pulse 1.4s ease-in-out infinite; }
1052
- .recbar .time { font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 650; min-width: 38px; }
1053
- .recbar .hint { font-size: 12px; color: var(--bc-muted); }
1054
- .recbar button { height: 34px; padding: 0 14px; font-size: 12.5px; font-weight: 620; border-radius: 999px; cursor: pointer; }
1055
- .recbar .stop { background: var(--bc-danger); border: 1px solid var(--bc-danger); color: #fff; }
1056
- .recbar .stop:hover { filter: brightness(1.08); }
1057
-
1058
- /* ---------- phones ----------------------------------------------------------
1059
- This widget runs inside a shadow root on somebody else's site, so its own
1060
- stylesheet is the whole story: the host page cannot correct any of this, and
1061
- anything that goes wrong here goes wrong on their site, in front of their
1062
- visitor, at the moment that visitor is already annoyed enough to file a bug.
1063
- -------------------------------------------------------------------------- */
1064
-
1065
- /* 100vh on a phone is the viewport with the browser chrome hidden, so the
1066
- panel could be taller than the space it actually has and put "Send report"
1067
- under the address bar. */
1068
- @supports (height: 100dvh) {
1069
- .panel { max-height: calc(100dvh - var(--bc-dock-h, 84px) - 52px); }
1070
- }
209
+ .notice.tone-success { --tone: var(--bc-ok); }
210
+ .notice.tone-warning { --tone: var(--bc-warn); }
211
+ .notice.tone-critical { --tone: var(--bc-danger); }
212
+ /* The tone is the icon's colour and a wash behind it — enough to tell a delay
213
+ from a fix at a glance, without a coloured card shouting over the form. */
214
+ .notice-ico {
215
+ flex: none; display: grid; place-items: center; width: 22px; height: 22px; margin-top: -1px;
216
+ border-radius: 7px; font-size: 14px;
217
+ color: var(--tone); background: color-mix(in srgb, var(--tone) 13%, transparent);
218
+ }
219
+ .notice-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
220
+ .notice-text strong { display: block; font-weight: 600; letter-spacing: -.005em; }
221
+ .notice-text p { margin: 2px 0 0; color: var(--bc-muted); white-space: pre-line; }
222
+ .notice-cta {
223
+ display: inline-flex; align-items: center; gap: 4px; margin-top: 6px;
224
+ color: var(--bc-fg); font-weight: 600; text-decoration: underline;
225
+ text-decoration-color: color-mix(in srgb, var(--bc-fg) 30%, transparent); text-underline-offset: 3px;
226
+ }
227
+ .notice-cta:hover { text-decoration-color: currentColor; }
228
+ .notice-x {
229
+ flex: none; display: grid; place-items: center;
230
+ width: 24px; height: 24px; margin: -2px -2px 0 0; padding: 0;
231
+ border: 0; border-radius: 999px; background: transparent;
232
+ color: var(--bc-faint); cursor: pointer;
233
+ }
234
+ .notice-x:hover { background: color-mix(in srgb, var(--bc-fg) 7%, transparent); color: var(--bc-fg); }
235
+ .notice-x svg { font-size: 13px; }
236
+ /* Beside the button it floats over somebody else's page: opaque, lifted, and
237
+ never taller than a glance. The whole text is in the panel. */
238
+ .notice.at-launcher {
239
+ width: min(320px, calc(100vw - 40px));
240
+ background: var(--bc-elevated); border-color: transparent;
241
+ box-shadow: var(--bc-shadow);
242
+ padding: 13px 12px 13px 13px;
243
+ }
244
+ .notice.at-launcher .notice-text p {
245
+ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
246
+ }
247
+ /* In the panel they are a stack of short lines above the form, not a wall:
248
+ the body is held to two lines, and the whole of it was beside the button. */
249
+ .notice.at-panel { margin-bottom: 8px; padding: 9px 8px 9px 10px; }
250
+ .notice.at-panel .notice-text p {
251
+ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
252
+ }
253
+ .notice.at-panel .notice-cta { margin-top: 3px; }
1071
254
 
1072
255
  /* The dock sits in a corner of a page the widget does not own. On a notched
1073
256
  phone that corner is under the home indicator, and in landscape it is under
@@ -1079,355 +262,49 @@ div.done { padding: 44px 26px 44px; text-align: center; animation: bc-fade-in 24
1079
262
  padding-left: env(safe-area-inset-left, 0px);
1080
263
  }
1081
264
 
1082
- /* Short desktop windows: the chat height gives way to the space there is. */
1083
- @media (max-height: 820px) { .panel.is-chat { height: calc(100vh - var(--bc-dock-h, 84px) - 52px); } }
1084
-
1085
- /* A phone gets the whole screen: a sheet that slides up from the bottom, with
1086
- the notch and the home indicator kept clear. The dock steps aside while it
1087
- is open — the sheet has its own close button, and a launcher floating over
1088
- the send button would be in the way of the one thing that matters. */
265
+ /* The dock's own share of the phone and touch rules; the panel's sheet carries
266
+ the rest. */
1089
267
  @media (max-width: 480px) {
1090
- .scrim { background: rgba(10,12,16,.4); }
1091
- .panel, .scrim.at-right .panel, .scrim.at-left .panel, .scrim.at-bottom .panel, .scrim.at-top .panel {
1092
- inset: 0; width: 100%; max-width: none; height: 100%; max-height: none;
1093
- border-radius: 0; border: 0;
1094
- animation: bc-sheet-in 380ms var(--bc-ease) both;
1095
- }
1096
- @supports (height: 100dvh) { .panel { height: 100dvh; max-height: none; } }
1097
- .panel.is-chat { height: 100%; }
1098
- .scrim.closing .panel { animation: bc-sheet-out 200ms ease both; }
1099
- .head { padding-top: max(14px, env(safe-area-inset-top, 0px)); padding-left: max(var(--bc-pad), env(safe-area-inset-left, 0px)); padding-right: max(12px, env(safe-area-inset-right, 0px)); }
1100
- .foot, .powered { padding-bottom: max(10px, env(safe-area-inset-bottom, 0px)); }
1101
- .foot + .powered { padding-bottom: max(9px, env(safe-area-inset-bottom, 0px)); }
1102
- .foot:has(+ .powered) { padding-bottom: 12px; }
268
+ /* The panel is a full-screen sheet with its own close button, and a launcher
269
+ floating over its send button would be in the way. */
1103
270
  .dock.over { display: none; }
1104
- .recbar .hint { display: none; }
1105
- .msg-row, .typing-row { max-width: 92%; }
1106
271
  }
1107
-
1108
272
  @media (pointer: coarse) {
1109
- /* iOS zooms the whole page when a field it focuses is under 16px, and does
1110
- not zoom back out. On the host's site that is not the widget's page to
1111
- zoom — the visitor is left on a magnified checkout wondering what they
1112
- touched. !important because this is a property of the device, not a
1113
- preference about type. */
1114
- input, textarea, select { font-size: 16px !important; }
1115
-
1116
- /* 44px is the floor a thumb hits reliably. Every control in this form was
1117
- under it, on the one surface in the product designed for someone who is
1118
- not an operator and gets no second attempt. */
1119
- .seg button { min-height: 44px; }
1120
- .tool { min-height: 56px; }
1121
- button.primary, button.ghost { height: 44px; }
1122
- .composer .send, .composer .tool { width: 40px; height: 40px; min-height: 0; }
1123
- textarea { min-height: 104px; }
1124
- .composer textarea { min-height: 40px; }
1125
-
1126
- /* The theme switch and the close button are small glyphs whose boxes cannot
1127
- grow without moving the header text beside them, so each keeps its size
1128
- and gets an invisible target drawn around it instead. */
1129
- .themer button, .head .icon-btn, .thumb .drop, .brand-credit { position: relative; }
1130
- .thumb .drop { position: absolute; opacity: 1; }
1131
- .themer button::after, .head .icon-btn::after, .thumb .drop::after, .brand-credit::after {
1132
- content: '';
1133
- position: absolute;
1134
- inset: -8px;
1135
- }
1136
- /* The themer's three buttons sit side by side, so their expanders would
1137
- overlap each other without a gap to grow into. */
1138
- .themer { gap: 4px; }
273
+ .notice-x { width: 36px; height: 36px; margin: -9px -8px -6px 0; }
1139
274
  }
1140
-
1141
275
  /* Hover is a lie on a touchscreen: it latches on tap and stays. The launcher's
1142
276
  lift in particular was left applied after the panel closed. */
1143
277
  @media (hover: none) {
1144
278
  .launcher:hover { transform: none; filter: none; }
1145
- .tool:hover:not(:disabled), .themer button:hover { transform: none; }
1146
279
  }
1147
- `;function Xa(t){const e=document.createElement("div");e.setAttribute("data-bugcatch","widget");let n=Wa()||Ja(t.config.theme);e.className=qn(n);const o=e.attachShadow({mode:"open"}),i=document.createElement("style");i.textContent=Ya(t.config.accent||"#4F46E5"),o.appendChild(i);const r=Rn[t.config.widgetPosition]?t.config.widgetPosition:"bottom-right",s=document.createElement("div");s.className=`dock ${r.includes("right")?"at-right":"at-left"}`+(r.startsWith("top")?" at-top":""),s.style.cssText=Rn[r];const l=document.createElement("div");l.className="themer",l.setAttribute("role","group"),l.setAttribute("aria-label","Report widget theme"),l.innerHTML=On.map(m=>`<button type="button" data-theme="${m.value}" title="${m.label}" aria-label="${m.label}" aria-pressed="${m.value===n}">${m.icon}</button>`).join(""),l.addEventListener("click",m=>{const h=m.target.closest("button[data-theme]");!h||h.dataset.theme===n||(n=h.dataset.theme,e.className=qn(n),Fa(n),l.querySelectorAll("button").forEach(f=>{f.setAttribute("aria-pressed",String(f.dataset.theme===n))}))});const g=document.createElement("button");g.className="launcher",g.type="button",g.innerHTML=`<span class="ico" aria-hidden="true"><span class="when-closed">${B.chat}</span><span class="when-open">${B.chevronDown}</span></span><span>Report a bug</span>`,g.setAttribute("aria-haspopup","dialog"),g.setAttribute("aria-expanded","false");let b=null;return g.addEventListener("click",()=>{if(b){b.close();return}s.classList.add("over"),g.setAttribute("aria-expanded","true"),b=Za({root:o,host:e,dock:s,client:t,onClose:()=>{b=null,s.classList.remove("over"),g.setAttribute("aria-expanded","false")}})}),s.append(l,g),o.appendChild(s),document.body.appendChild(e),t.widgetControls={open:()=>{b||g.click()},close:()=>{b==null||b.close()},isOpen:()=>!!b},()=>{b==null||b.destroy(),e.remove(),t.widgetControls=null}}function Ja(t){return t==="dark"||t==="light"?t:"auto"}function qn(t){return t==="dark"?"bc-dark":t==="light"?"bc-light":"bc-auto"}function Za({root:t,host:e,dock:n,client:o,onClose:i}){var ko,So,$o,Co,To,Eo;const r=o.config,s=r.attachments!==!1,l=s&&Jo(),g=s&&Xo(),b=[];let m="MEDIUM",h=null,f=!1;const p=document.createElement("div");p.className=`scrim ${n.classList.contains("at-left")?"at-left":"at-right"} ${n.classList.contains("at-top")?"at-top":"at-bottom"}`,p.style.setProperty("--bc-dock-h",`${Math.round(n.getBoundingClientRect().height)}px`),p.innerHTML=`
1148
- <div class="panel" role="dialog" aria-modal="true" aria-labelledby="bc-h">
1149
- ${Kt({id:"bc-h",title:"Tell us what went wrong",sub:"Our assistant starts looking right away. We attach the page details for you."})}
1150
- <div class="body">
1151
- <div class="slot" role="alert"></div>
1152
- <div class="whoami" hidden></div>
1153
- <!-- The questions the assistant can answer from their account and
1154
- close by itself. Drawn only when the project offers any. -->
1155
- <div class="picks" hidden></div>
1156
- <div class="known" hidden></div>
1157
- <div class="compose">
1158
- <div class="field">
1159
- <label for="bc-title">What went wrong?</label>
1160
- <input id="bc-title" type="text" placeholder="e.g. The checkout button does nothing" autocomplete="off"
1161
- aria-describedby="bc-title-err" />
1162
- <p class="field-err" id="bc-title-err" hidden></p>
1163
- </div>
1164
- <div class="field">
1165
- <label for="bc-desc">Anything else that helps <span class="opt">(optional)</span></label>
1166
- <textarea id="bc-desc" placeholder="What you were doing, what you expected, what happened instead."></textarea>
1167
- </div>
1168
- <!-- Drawn only when this project asks for an identifier and the report
1169
- does not already carry one. Between the description and the
1170
- severity because that is where it belongs in the reading order:
1171
- it is a question about what they just typed. -->
1172
- <div class="refs" hidden></div>
1173
- <div class="field">
1174
- <label id="bc-prio-l">How much is it getting in your way?</label>
1175
- <div class="seg" role="group" aria-labelledby="bc-prio-l">
1176
- ${zn.map(a=>`
1177
- <button type="button" data-prio="${a.value}" aria-pressed="${a.value===m}">${a.label}</button>
1178
- `).join("")}
1179
- </div>
1180
- </div>
1181
- ${s?`
1182
- <div class="field">
1183
- <div class="field-head">
1184
- <label id="bc-att-l">Show us <span class="opt">(optional)</span></label>
1185
- <span class="count" aria-live="polite"></span>
1186
- </div>
1187
- <div class="tools" role="group" aria-labelledby="bc-att-l">
1188
- ${l?`<button class="tool" type="button" data-act="shot">${B.camera}Screenshot</button>`:""}
1189
- ${g?`<button class="tool" type="button" data-act="rec">${B.video}Record screen</button>`:""}
1190
- <button class="tool" type="button" data-act="file">${B.paperclip}Upload file</button>
1191
- </div>
1192
- <div class="thumbs"></div>
1193
- <input type="file" accept="${lt.join(",")}" multiple hidden />
1194
- </div>`:""}
1195
- </div>
1196
- </div>
1197
- <div class="foot">
1198
- <span class="note" aria-live="polite"></span>
1199
- ${o.hasReports()?'<button class="quiet" type="button" data-act="history">Your reports</button>':""}
1200
- <span class="spacer"></span>
1201
- <!-- Hidden until the API confirms this project can take a call, which
1202
- most cannot. Drawn here rather than as a second launcher because it
1203
- is the same errand as Send report — the difference is only whether
1204
- the reporter would rather say it than type it. -->
1205
- <button class="ghost" type="button" data-act="call" hidden>${B.phone}Talk to the team</button>
1206
- <button class="primary" type="button" data-act="send">Send report ${B.send}</button>
1207
- </div>
1208
- <div class="powered">Powered by
1209
- <a class="brand-credit" href="https://bugcatch.sphoro.com" target="_blank" rel="noopener noreferrer" aria-label="BugCatch">${Va}</a>
1210
- </div>
1211
- </div>`;const x=a=>p.querySelector(a),A=x("#bc-title"),z=x("#bc-desc"),S=x(".slot"),M=x(".thumbs"),I=p.querySelector("input[type=file]"),j=x('[data-act="send"]'),Q=x('[data-act="call"]'),U=x(".note"),P=x(".compose"),C=x(".whoami"),$=x(".refs"),_=x(".known"),Te=x(".picks"),kt=x('[data-act="history"]'),fe=document.createElement("div");fe.setAttribute("role","status"),fe.className="note",P.prepend(fe);const Ge=ra(r,(ko=$e(r.projectId))==null?void 0:ko.email);let He=r.drafts===!1,re=!1,be=Promise.resolve(),Le;function Ee(){if(clearTimeout(Le),!He||re||r.drafts===!1)return be;const a=A.value||z.value||b.length?{title:A.value,description:z.value,priority:m,items:b.map(({blob:c,mime:u,kind:d})=>({blob:c,mime:u,kind:d}))}:null;return be=be.catch(()=>{}).then(()=>ht(Ge,a)).then(()=>{fe.textContent=a?"Draft saved on this device":""}).catch(()=>{fe.textContent="Draft could not be saved. Keep this tab open."}),be}function De(){clearTimeout(Le),Le=setTimeout(Ee,250)}P.addEventListener("input",De),P.addEventListener("change",De);const et=()=>{Ee()};window.addEventListener("pagehide",et);function tt(){b.filter(a=>a.status==="failed").forEach(y)}window.addEventListener("online",tt),r.drafts!==!1&&(P.inert=!0,ia(Ge).then(a=>{Oe||!a||(fe.innerHTML='<span>You have an unfinished report on this device.</span> <button type="button" data-resume>Resume report</button> <button type="button" data-discard>Discard</button>',fe.querySelector("[data-resume]").onclick=()=>{A.value=a.title||"",z.value=a.description||"",m=zn.some(c=>c.value===a.priority)?a.priority:"MEDIUM",p.querySelectorAll("[data-prio]").forEach(c=>c.setAttribute("aria-pressed",String(c.dataset.prio===m)));for(const c of(a.items||[]).slice(0,r.maxAttachments??5))T(c);He=!0,Ee()},fe.querySelector("[data-discard]").onclick=()=>{He=!0,Ee()})}).catch(()=>{fe.textContent="Draft storage is unavailable. Keep this tab open."}).finally(()=>{P.inert=!1,fe.querySelector("[data-resume]")||(He=!0)}));const ze=document.activeElement,nt=document.documentElement.style,St=nt.overflow;nt.overflow="hidden";let Oe=!1,Qe=null;function Ne(){var a;Oe||(Ee(),Oe=!0,clearTimeout(Le),window.removeEventListener("pagehide",et),window.removeEventListener("online",tt),h==null||h.cancel(),Qe==null||Qe(),b.forEach(c=>c.previewUrl&&URL.revokeObjectURL(c.previewUrl)),nt.overflow=St,document.removeEventListener("keydown",$t,!0),p.remove(),(a=t.querySelector(".recbar"))==null||a.remove(),n.hidden=!1,e.style.opacity="",i==null||i())}function we(){var a;if(!Oe){p.classList.add("closing"),setTimeout(Ne,160);try{(a=ze==null?void 0:ze.focus)==null||a.call(ze)}catch{}}}function $t(a){if(a.key==="Escape"){if(a.stopPropagation(),h){ie({keep:!1});return}f||we();return}if(a.key!=="Tab"||h)return;const c=[...p.querySelectorAll("button:not(:disabled), input:not([hidden]), textarea, select, [href]")].filter(k=>k.offsetParent!==null||k===document.activeElement);if(!c.length)return;const u=c[0],d=c[c.length-1],w=p.getRootNode().activeElement;a.shiftKey&&w===u?(a.preventDefault(),d.focus()):!a.shiftKey&&w===d&&(a.preventDefault(),u.focus())}document.addEventListener("keydown",$t,!0),p.addEventListener("click",a=>{a.target===p&&!f&&we()}),x('[data-act="close"]').addEventListener("click",we),p.querySelectorAll("[data-prio]").forEach(a=>{a.addEventListener("click",()=>{m=a.dataset.prio,De(),p.querySelectorAll("[data-prio]").forEach(c=>{c.setAttribute("aria-pressed",String(c===a))})})});function J(a,c="err"){S.innerHTML=a?`<div class="msg ${c}">${c==="err"?B.alert:""}<span>${v(a)}</span></div>`:""}const Ct=x("#bc-title-err");function ot(a){Ct.hidden=!a,Ct.innerHTML=a?`${B.alert}<span>${v(a)}</span>`:"",A.classList.toggle("invalid",!!a),A.setAttribute("aria-invalid",String(!!a))}A.addEventListener("input",()=>{A.value.trim()&&ot("")});function me(){const a=b.some(d=>d.status==="uploading");j.disabled=f||a||b.some(d=>d.status==="failed"),U.textContent=a?"Waiting for the upload to finish…":b.some(d=>d.status==="failed")?"Retry or remove failed attachments before sending.":"";const c=r.maxAttachments??5;p.querySelectorAll(".tool").forEach(d=>{d.disabled=f||b.length>=c});const u=x(".field-head .count");u&&(u.textContent=b.length?`${b.length} of ${c}`:"")}function Tt(a){var k;const c=a.el,u=a.kind==="VIDEO"?"Video":"Shot",d=a.kind==="VIDEO"?`<video src="${a.previewUrl}" muted playsinline preload="metadata"></video>`:`<img src="${a.previewUrl}" alt="" />`;let w="";a.status==="uploading"?w=`<div class="veil"><div class="spinner"></div><div class="bar"><i style="width:${Math.round(a.progress*100)}%"></i></div></div>`:a.status==="failed"&&(w=`<div class="veil err"><button type="button" data-act="retry" aria-label="Retry upload">${B.retry}</button>Failed</div>`),c.innerHTML=`
1212
- ${d}
1213
- <span class="kind">${u} · ${qe(a.size)}</span>
1214
- <button class="drop" type="button" data-act="drop" aria-label="Remove ${u.toLowerCase()}">${B.close}</button>
1215
- ${w}`,c.querySelector('[data-act="drop"]').addEventListener("click",()=>Zt(a)),(k=c.querySelector('[data-act="retry"]'))==null||k.addEventListener("click",()=>y(a))}function Zt(a){const c=b.indexOf(a);c>=0&&b.splice(c,1),a.previewUrl&&URL.revokeObjectURL(a.previewUrl),a.el.remove(),Ee(),me()}async function y(a){a.status="uploading",a.progress=0,Tt(a),me();try{const{storageKey:c,mime:u}=await o.uploadAttachment(a.blob,a.mime,d=>{a.progress=d;const w=a.el.querySelector(".bar i");w&&(w.style.width=`${Math.round(d*100)}%`)});a.storageKey=c,a.mime=u,a.status="ready"}catch(c){a.status="failed",J(c.message.replace("[BugCatch] ",""))}Tt(a),me()}function T({blob:a,mime:c,kind:u}){const d=r.maxAttachmentBytes??26214400;if(a.size>d){J(`That file is ${qe(a.size)} — the limit is ${qe(d)}.`);return}const w=dt(c||a.type,lt);if(!w){J("Only PNG, JPEG, WebP, WebM and MP4 files can be attached to a report.");return}const k={blob:a,kind:u,mime:w,size:a.size,previewUrl:URL.createObjectURL(a),status:"uploading",progress:0,storageKey:null,el:document.createElement("div")};k.el.className="thumb",b.push(k),M.appendChild(k.el),J(""),Ee(),y(k)}async function D(a){e.style.opacity="0",e.style.pointerEvents="none",await new Promise(c=>requestAnimationFrame(()=>requestAnimationFrame(c)));try{return await a()}finally{e.style.opacity="",e.style.pointerEvents=""}}(So=p.querySelector('[data-act="shot"]'))==null||So.addEventListener("click",async()=>{try{const a=await D(Zo);T(a)}catch(a){(a==null?void 0:a.name)!=="NotAllowedError"&&J(`Screenshot failed: ${a.message}`)}}),($o=p.querySelector('[data-act="file"]'))==null||$o.addEventListener("click",()=>I.click()),I==null||I.addEventListener("change",()=>{const a=(r.maxAttachments??5)-b.length;[...I.files].slice(0,Math.max(a,0)).forEach(c=>{T({blob:c,mime:c.type,kind:c.type.startsWith("video/")?"VIDEO":"SCREENSHOT"})}),I.value=""}),(Co=p.querySelector('[data-act="rec"]'))==null||Co.addEventListener("click",async()=>{const a=r.maxRecordingMs??12e4;let c;try{h=await Go({maxMs:a,onTick:u=>{c&&(c.querySelector(".time").textContent=ut(u))},onAutoStop:()=>{h&&ie({keep:!0})}})}catch(u){(u==null?void 0:u.name)!=="NotAllowedError"&&J(`Recording failed: ${u.message}`);return}p.style.display="none",n.hidden=!0,c=document.createElement("div"),c.className="recbar",c.innerHTML=`
1216
- <span class="dot"></span>
1217
- <span class="time">0:00</span>
1218
- <span class="hint">Recording — up to ${ut(Math.round(a/1e3))}</span>
1219
- <button class="stop" type="button">Stop</button>
1220
- <button class="ghost" type="button">Discard</button>`,c.querySelector(".stop").addEventListener("click",()=>ie({keep:!0})),c.querySelector(".ghost").addEventListener("click",()=>ie({keep:!1})),t.appendChild(c),c.querySelector(".stop").focus()});async function ie({keep:a}){var u;const c=h;if(c){if(h=null,(u=t.querySelector(".recbar"))==null||u.remove(),p.style.display="",n.hidden=!1,!a){c.cancel(),A.focus();return}try{const d=await c.stop();d.blob.size?T(d):J("That recording came back empty — nothing was captured.")}catch(d){J(`Recording failed: ${d.message}`)}A.focus()}}let N={policy:"off",fields:[]},ke=!1,Z=null,ee="",ne="",he=[],Gt="ask",oo=!1,Ue=!1;o.referenceFields().then(a=>{N=a,io()}).catch(()=>{});function en(){return N.policy==="off"||!N.fields.length?{found:[],conflicts:[],loose:[]}:Ua(N.fields,[A.value,z.value])}function ao(a=en()){return a.found.length>0&&a.conflicts.length===0}function tn(){return!Z||!ee.trim()?[]:[{key:Z,value:ee.trim().slice(0,120)}]}const Ae=document.createElement("p");Ae.className="ref-found",Ae.setAttribute("aria-live","polite"),Ae.hidden=!0,z.closest(".field").after(Ae);let ro;function io(){const a=en();if(ao(a)){const c=a.found.slice(0,2).map(u=>`${v(u.label)} <b>${v(co(u.value))}</b>`).join(", ");Ae.innerHTML=`${B.check}<span>Found your ${c} — no need to add it again.</span>`,Ae.hidden=!1,ke&&!tn().length&&(ke=!1,Ie())}else Ae.hidden=!0,Ae.innerHTML=""}const so=()=>{clearTimeout(ro),ro=setTimeout(io,250)};A.addEventListener("input",so),z.addEventListener("input",so);function co(a){return a.length>20?`${a.slice(0,8)}…${a.slice(-4)}`:a}function Ar(a){const c=a.trim().toLowerCase();return he.find(u=>u.value.toLowerCase()===c)||null}function lo(a){ee=a.value,a.kinds.length===1?Z=a.kinds[0].key:a.kinds.some(c=>c.key===Z)||(Z=null),ne="",Ue=!1}function Ie(){var W,G;if(!ke){$.hidden=!0,$.innerHTML="";return}const a=N.fields.find(E=>E.key===Z)||null,c=N.policy!=="required",u=Ar(ee),d=u&&u.kinds.length>1?N.fields.filter(E=>u.kinds.some(te=>te.key===E.key)):N.fields,w=he.length>1&&he.every(E=>E.kinds.length===1&&E.kinds[0].key===he[0].kinds[0].key),k={confirm:he.length>1?["Which one is this about?","We found more than one ID in your message. Pick the one this problem is about."]:["Is this the right ID?","This could be more than one kind of ID. Tell us which it is."],suggest:["Is this your ID?","We spotted this in your message. Tell us what it is so we can find your record."],ask:c?["One more thing","So we can find your record — this helps us answer faster."]:["We need one detail","We need one of these to look this up."]}[Gt];$.hidden=!1;const q=d.length>6;$.innerHTML=`
1221
- <div class="ask-head">
1222
- <span class="avatar" aria-hidden="true">${B.sparkle}</span>
1223
- <div>
1224
- <strong>${v(k[0])}</strong>
1225
- <p class="why">${v(k[1])}</p>
1226
- </div>
1227
- </div>
1228
- ${he.length>1?`
1229
- <div class="chips cands" role="group" aria-label="IDs found in your message">
1230
- ${he.map((E,te)=>`
1231
- <button type="button" data-ref-cand="${te}" title="${v(E.value)}"
1232
- aria-pressed="${!!u&&u===E}">${v(co(E.value))}</button>
1233
- `).join("")}
1234
- </div>`:""}
1235
- ${d.length>1&&!q&&!w?`
1236
- <div class="chips" role="group" aria-label="Which detail you are giving">
1237
- ${d.map(E=>`
1238
- <button type="button" data-ref-kind="${v(E.key)}" aria-pressed="${!!a&&E.key===a.key}">${v(E.label)}</button>
1239
- `).join("")}
1240
- </div>`:""}
1241
- <div class="row">
1242
- ${q?`
1243
- <select class="ref-kind" aria-label="Which detail you are giving">
1244
- ${a?"":'<option value="" selected disabled>What is it?</option>'}
1245
- ${d.map(E=>`
1246
- <option value="${v(E.key)}" ${a&&E.key===a.key?"selected":""}>${v(E.label)}</option>
1247
- `).join("")}
1248
- </select>`:""}
1249
- <input class="ref-value${ne?" invalid":""}" type="${(a==null?void 0:a.inputMode)==="email"?"email":"text"}"
1250
- inputmode="${v((a==null?void 0:a.inputMode)==="numeric"||(a==null?void 0:a.inputMode)==="tel"?"numeric":"text")}"
1251
- value="${v(ee)}"
1252
- placeholder="${v(a?a.placeholder||a.label:"Your ID")}"
1253
- aria-label="${v(a?a.label:"Your ID")}" ${ne?'aria-invalid="true"':""} />
1254
- </div>
1255
- ${a!=null&&a.hint?`<p class="hint">${v(a.hint)}</p>`:""}
1256
- ${ne?`<p class="field-err" role="alert">${B.alert}<span>${v(ne)}</span></p>`:""}
1257
- ${c?`<button type="button" class="link" data-act="ref-skip">${Gt==="ask"?"I don't have this — send without it":"Not sure — send as it is"}</button>`:""}`,$.querySelectorAll("[data-ref-cand]").forEach(E=>{E.addEventListener("click",()=>{lo(he[Number(E.dataset.refCand)]),Ie()})}),$.querySelectorAll("[data-ref-kind]").forEach(E=>{E.addEventListener("click",()=>{Z=E.dataset.refKind,ne="",Ue=!1,Ie()})});const R=$.querySelector(".ref-value");R.addEventListener("input",()=>{ee=R.value,ne="",Ue=!1}),R.addEventListener("keydown",E=>{E.key==="Enter"&&(E.preventDefault(),j.click())}),(W=$.querySelector(".ref-kind"))==null||W.addEventListener("change",E=>{var te;Z=E.target.value,ne="",Ie(),(te=$.querySelector(".ref-value"))==null||te.focus()}),(G=$.querySelector('[data-act="ref-skip"]'))==null||G.addEventListener("click",()=>{oo=!0,ke=!1,Z=null,ee="",ne="",Ue=!1,Ie(),J("")}),((!a&&ee?$.querySelector("[data-ref-kind], .ref-kind"):null)||R).focus()}function Br(a){if(he=a.conflicts.length?a.conflicts:a.loose.slice(0,4),Gt=a.conflicts.length?"confirm":a.loose.length?"suggest":"ask",ke=!0,he.length)if(he.length===1)lo(he[0]);else{ee="";const[c]=he;Z=he.every(d=>d.kinds.length===1&&d.kinds[0].key===c.kinds[0].key)?c.kinds[0].key:null}else Z=Z||N.fields[0].key;Ie()}function jr(){if(N.policy==="off"||!N.fields.length||tn().length)return!1;const a=en();return ao(a)||oo&&N.policy!=="required"?!1:a}j.addEventListener("click",async()=>{const a=A.value;if(!a.trim()){ot("Add a few words about what went wrong — a short sentence is plenty."),A.focus();return}if(ke&&ee.trim()&&!Z){ne="Pick what this ID is.",Ie();return}const c=jr();if(c){ke?Ie():Br(c),J("");return}if(Z&&ee.trim()){const d=N.fields.find(w=>w.key===Z);if(!Ue&&d&&!jn(d,ee)){ne=`That does not look like a ${d.label.toLowerCase()}. Press Send again to use it anyway.`,Ue=!0,Ie();return}}f=!0,J(""),me();const u=j.innerHTML;j.innerHTML='<span class="spinner" aria-hidden="true"></span>Sending…';try{const d=await o.report({title:a,description:z.value,priority:m,references:tn(),issue:(se==null?void 0:se.key)||null,attachments:b.filter(w=>w.status==="ready").map(w=>({type:w.kind,storageKey:w.storageKey,mime:w.mime}))});if(d!=null&&d.cancelled){f=!1,j.innerHTML=u,me();return}re=!0,clearTimeout(Le),await be,r.drafts!==!1&&await ht(Ge,null).catch(()=>{}),mo(d)}catch(d){f=!1,j.innerHTML=u,J(d.message.replace("[BugCatch] ","")),me(),ce.mode!=="off"&&rn().catch(()=>{})}}),(To=p.querySelector('[data-act="history"]'))==null||To.addEventListener("click",wo);let Ke=[],Et="",nn=null,at=null;function uo(){r.knownIssues!==!1&&o.knownIssues().then(a=>{Ke=a,rt()}).catch(()=>{})}let po=null;function Rr(a){if(r.knownIssues===!1)return;clearTimeout(po),at==null||at.abort();const c=(a||"").trim();if(c.length<3){uo();return}po=setTimeout(()=>{at=new AbortController,o.matchKnownIssues(c,{signal:at.signal}).then(u=>{u.length&&(Ke=u,rt())}).catch(()=>{})},250)}function rt(){if(!Ke.length){_.hidden=!0,_.innerHTML="";return}_.hidden=!1,_.innerHTML=`
1258
- <p class="known-h">Already reported</p>
1259
- ${Ke.map(a=>`
1260
- <div class="issue ${a.joined?"is-joined":""}">
1261
- <span class="who">
1262
- <span class="t">${v(a.title)}</span>
1263
- <span class="meta">${v(zr(a))}</span>
1264
- </span>
1265
- <button class="join" type="button" data-join="${v(a.id)}"
1266
- ${a.joined?"disabled":""}>${a.joined?"Following":"Me too"}</button>
1267
- </div>
1268
- ${nn===a.id?`
1269
- <div class="ask">
1270
- <input type="email" class="join-email" placeholder="you@example.com"
1271
- aria-label="Your email, so we can tell you when this is fixed" />
1272
- <button class="ghost" type="button" data-join-confirm="${v(a.id)}">Follow</button>
1273
- </div>`:""}
1274
- `).join("")}
1275
- ${Et?`<p class="err">${v(Et)}</p>`:""}`}function zr(a){const c=a.status==="IN_PROGRESS"?"Being worked on now":"Not fixed yet",u=a.affectedCount===0?"":` · ${a.affectedCount} ${a.affectedCount===1?"person has":"people have"} said this is happening to them`;return`${c} · reported ${yt(a.since)}${u}`}_.addEventListener("click",async a=>{var k,q,R,V,W;const c=a.target.closest("[data-join]"),u=a.target.closest("[data-join-confirm]");if(!c&&!u)return;const d=(u||c).getAttribute(u?"data-join-confirm":"data-join");Et="";const w=((q=(k=_.querySelector(".join-email"))==null?void 0:k.value)==null?void 0:q.trim())||((R=o.signedInAs())==null?void 0:R.email)||((V=o.config.user)==null?void 0:V.email);if(!w){nn=d,rt(),(W=_.querySelector(".join-email"))==null||W.focus();return}try{const G=await o.meToo(d,{email:w});nn=null,Ke=Ke.map(E=>E.id===d?{...E,joined:!0,affectedCount:G.affectedCount??E.affectedCount}:E),rt()}catch(G){Et=G.message.replace("[BugCatch] ",""),rt()}}),uo(),A.addEventListener("input",()=>Rr(A.value));let It=[],se=null;function ho(){if(!It.length){Te.hidden=!0,Te.innerHTML="";return}Te.hidden=!1,Te.innerHTML=`
1276
- <p class="picks-h">Get an answer right away</p>
1277
- <div class="picks-grid" role="group" aria-label="Common questions">
1278
- ${It.map(a=>`
1279
- <button type="button" class="pick" data-pick="${v(a.key)}"
1280
- aria-pressed="${(se==null?void 0:se.key)===a.key}">
1281
- <span class="t">${v(a.title)}</span>
1282
- ${a.hint?`<span class="h">${v(a.hint)}</span>`:""}
1283
- </button>`).join("")}
1284
- </div>
1285
- ${se?`<p class="picks-note">${B.check}<span>Our assistant will check your account and reply here. Add anything else below, then send.</span></p>`:""}`}Te.addEventListener("click",a=>{const c=a.target.closest("[data-pick]");if(!c)return;const u=It.find(w=>w.key===c.dataset.pick);if(!u)return;const d=se&&A.value.trim()===se.title;(se==null?void 0:se.key)===u.key?(se=null,d&&(A.value="")):(se=u,(!A.value.trim()||d)&&(A.value=u.title)),A.dispatchEvent(new Event("input",{bubbles:!0})),ho(),se&&z.focus()}),r.agentIssues!==!1&&typeof o.agentIssues=="function"&&o.agentIssues().then(a=>{It=a,ho()}).catch(()=>{});let fo=!1;function go(){o.callKind().then(a=>{a&&(fo=!0,Q.hidden=on())}).catch(()=>{})}go();let ce={mode:o.lastLoginMode||"off",signedIn:null},le="idle",Pe=((Eo=o.config.user)==null?void 0:Eo.email)||"",_e="",de=!1,ve="";const on=()=>ce.mode==="required"&&!ce.signedIn;function Or(){const a=on();P.hidden=a,j.hidden=a,kt&&(kt.hidden=a),a?Q.hidden=!0:fo&&(Q.hidden=!1)}function Qr(){var c;if(ce.mode==="off"){C.hidden=!0,C.innerHTML="";return}C.hidden=!1,C.classList.toggle("asking",le!=="idle");const a=ve?`<div class="after err">${v(ve)}</div>`:"";if(le==="idle"){if(ce.signedIn){C.innerHTML=`<span class="who">Signed in as <strong>${v(ce.signedIn.email)}</strong></span><button type="button" class="link" data-act="signout">Sign out</button>`;return}const u=(c=o.config.user)==null?void 0:c.email;C.innerHTML=`<span class="who">${ce.mode==="required"?"Sign in to report a bug here.":u?`Reporting as <strong>${v(u)}</strong>.`:"Reporting anonymously — sign in and we can tell you what happens next."}</span><button type="button" class="link" data-act="signin">Sign in</button>`;return}if(le==="email"){C.innerHTML=`
1286
- <p class="why">Sign in with any email address — we'll send you a code.
1287
- It's how you hear back about this report${ce.mode==="required"?", and this project asks for it":""}.</p>
1288
- <div class="row">
1289
- <input type="email" data-fld="email" value="${v(Pe)}"
1290
- placeholder="you@example.com" autocomplete="email" spellcheck="false"
1291
- aria-label="Your email address" ${de?"disabled":""} />
1292
- <button type="button" class="primary" data-act="code" ${de?"disabled":""}>
1293
- ${de?"Sending…":"Send code"}
1294
- </button>
1295
- </div>
1296
- ${a}
1297
- ${ce.mode==="optional"?'<div class="after"><button type="button" class="link" data-act="skip">Not now</button></div>':""}`;return}C.innerHTML=`
1298
- <p class="why">We sent a code to <strong>${v(Pe)}</strong>. It expires in ten minutes.</p>
1299
- <div class="row">
1300
- <input type="text" class="code" data-fld="code" value="${v(_e)}"
1301
- placeholder="123456" inputmode="numeric" autocomplete="one-time-code"
1302
- maxlength="8" aria-label="The code we emailed you" ${de?"disabled":""} />
1303
- <button type="button" class="primary" data-act="confirm" ${de?"disabled":""}>
1304
- ${de?"Checking…":"Sign in"}
1305
- </button>
1306
- </div>
1307
- ${a}
1308
- <div class="after">
1309
- <button type="button" class="link" data-act="resend" ${de?"disabled":""}>Send it again</button>
1310
- <button type="button" class="link" data-act="signin-back" ${de?"disabled":""}>Use another address</button>
1311
- </div>`}function ye({focus:a=!1}={}){var u;if(Qr(),Or(),!a||de)return;const c=C.querySelector("[data-fld]");c&&(c.focus(),(u=c.setSelectionRange)==null||u.call(c,c.value.length,c.value.length))}C.addEventListener("input",a=>{const c=a.target.closest("[data-fld]");c&&(c.dataset.fld==="email"?Pe=c.value:_e=c.value)}),C.addEventListener("keydown",a=>{a.key!=="Enter"||!a.target.closest("[data-fld]")||(a.preventDefault(),le==="email"?an():bo())});async function an({resent:a=!1}={}){const c=Pe.trim();if(!c){ve="Enter the address to send the code to.",ye({focus:!0});return}de=!0,ve="",ye();try{await o.signIn(c),Pe=c,a||(_e=""),le="code"}catch(u){ve=u.message}finally{de=!1,ye({focus:!0})}}async function bo(){const a=_e.trim();if(!a){ve="Enter the code we emailed you.",ye({focus:!0});return}de=!0,ve="",ye();try{const c=await o.confirmSignIn(Pe.trim(),a);ce.signedIn=c,le="idle",_e="",J(""),go()}catch(c){ve=c.message}finally{de=!1,ye({focus:le!=="idle"})}}C.addEventListener("click",async a=>{const c=a.target.closest("button[data-act]");if(!c)return;const u=c.dataset.act;if(u==="signin"){le="email",ve="",ye({focus:!0});return}if(u==="skip"){le="idle",ve="",ye();return}if(u==="signin-back"){le="email",ve="",_e="",ye({focus:!0});return}if(u==="code"){an();return}if(u==="resend"){an({resent:!0});return}if(u==="confirm"){bo();return}if(u==="signout"){de=!0,ye();try{await o.signOut()}finally{ce.signedIn=null,de=!1,le=ce.mode==="required"?"email":"idle",ye({focus:le!=="idle"})}}});async function rn({announce:a=!1}={}){const c=await o.loginState();ce={mode:c.mode,signedIn:c.signedIn},o.lastLoginMode=c.mode,on()&&le==="idle"&&(le="email"),ce.signedIn&&(le="idle"),a&&c.signedOutReason&&J(c.signedOutReason,"info"),ye()}rn({announce:!0}).catch(()=>{}),Q.addEventListener("click",async()=>{f=!0,J(""),me(),Q.disabled=!0,Q.innerHTML='<span class="spinner"></span>Ringing…';try{const a=await o.startCall();qr(a)}catch(a){f=!1,Q.disabled=!1,Q.innerHTML=`${B.phone}Talk to the team`,me(),a.filed?mo(a.filed,{callFailed:a.reason||"The call could not be started."}):(J(a.message.replace("[BugCatch] ","")),ce.mode!=="off"&&rn().catch(()=>{}))}});function qr(a){const c=p.querySelector(".panel"),u=a!=null&&a.bugId?String(a.bugId).slice(0,8):null;c.innerHTML=`
1312
- <div class="call">
1313
- <div class="call-head">
1314
- <span class="call-dot" aria-hidden="true"></span>
1315
- <strong class="call-state">Ringing…</strong>
1316
- </div>
1317
- <p class="call-note">
1318
- We're ringing the team now. Someone will pick up if they're free — your report${u?` <code>${v(u)}</code>`:""} is already filed either way, so nothing is lost if nobody answers.
1319
- </p>
1320
- <div class="foot">
1321
- <span class="spacer"></span>
1322
- <button class="primary" type="button" data-act="hangup">Cancel</button>
1323
- </div>
1324
- </div>`;const d=c.querySelector(".call-state"),w=c.querySelector(".call-dot"),k={ringing:"Ringing the team…",connecting:"Someone answered — connecting…",connected:"Connected",missed:"Nobody was free",ended:"Call ended",failed:"The call dropped"},q=["ended","failed","missed"],R=o.on("call",({state:V})=>{if(d.textContent=k[V]||V,w.classList.toggle("off",q.includes(V)),q.includes(V)&&(c.querySelector('[data-act="hangup"]').textContent="Close",V==="missed")){const W=c.querySelector(".call-note");W&&(W.textContent="Nobody was free to pick up just now. Your report is filed and the team will reply in the chat.")}});c.querySelector('[data-act="hangup"]').addEventListener("click",()=>{R(),o.endCall(),we()})}function mo(a,{callFailed:c=null}={}){if(a!=null&&a.id&&!c){sn(a.id,{fresh:!0}).then(u=>{u||yo(a,{callFailed:c})});return}yo(a,{callFailed:c})}function yo(a,{callFailed:c=null}={}){var k;const u=a!=null&&a.id?String(a.id).slice(0,8):null,d=(k=a==null?void 0:a.estimate)!=null&&k.label?String(a.estimate.label):null,w=c?`<p class="eta">${v(c)} Your report is filed, and the team will reply in the chat.</p>`:"";p.querySelector(".panel").innerHTML=`
1325
- <div class="done">
1326
- <div class="tick">${B.check}</div>
1327
- <h3>Thanks — that's on its way.</h3>
1328
- <p>${u?`Filed as <code>${v(u)}</code>.`:"The team has what they need to reproduce it."}</p>
1329
- ${w}
1330
- ${d?`<p class="eta">Reports like this usually take <strong>${v(d)}</strong>. We'll let you know when it moves.</p>`:""}
1331
- </div>`,setTimeout(we,w?6e3:d?4e3:2e3)}async function wo(){const a=p.querySelector(".panel");clearTimeout(it),Me=null,a.classList.remove("is-chat"),a.innerHTML=`
1332
- ${Kt({title:"Your reports",sub:"Everything you've reported, and where each one got to."})}
1333
- <div class="history" aria-busy="true">${nr(3)}</div>`,a.querySelector('[data-act="close"]').addEventListener("click",we);let c=[];try{({reports:c}=await o.myReports())}catch{const d=a.querySelector(".history");d&&(d.innerHTML=Pt("Couldn't load your reports just now.",B.alert));return}const u=a.querySelector(".history");if(u){u.removeAttribute("aria-busy"),u.innerHTML=c.length?c.map((d,w)=>`
1334
- <button class="row" type="button" data-report="${v(d.id)}" style="animation-delay:${Math.min(w,8)*30}ms">
1335
- <span class="row-ico" aria-hidden="true">${B.chat}</span>
1336
- <span class="who">
1337
- <span class="ttl">${v(d.title)}</span>
1338
- <span class="sub">${v(d.reference)} · ${v(yt(d.createdAt))}</span>
1339
- </span>
1340
- <span class="pill ${v(String(d.status).toLowerCase())}">${v(Vt(d.status))}</span>
1341
- <span class="chev" aria-hidden="true">${B.chevronRight}</span>
1342
- </button>`).join(""):Pt("You haven't reported anything yet.",B.inbox);for(const d of u.querySelectorAll("[data-report]"))d.addEventListener("click",()=>sn(d.dataset.report))}}async function sn(a,{fresh:c=!1}={}){var R;const u=p.querySelector(".panel");clearTimeout(it),u.classList.add("is-chat"),u.innerHTML=`
1343
- ${Kt({back:!0,title:c?"Looking into it":"Your report",sub:c?"Our assistant is on it — it may ask you a question here.":"Everything that has happened, and a way to answer."})}
1344
- <div class="history chatting" aria-busy="true"><div class="thread">${or()}</div></div>`,u.querySelector('[data-act="close"]').addEventListener("click",we),u.querySelector('[data-act="back"]').addEventListener("click",wo);let d=null;try{d=await o.myReport(a)}catch{d=null}const w=u.querySelector(".history");if(!w||!u.querySelector('[data-act="back"]'))return!1;if(!d)return c?(u.classList.remove("is-chat"),!1):(w.removeAttribute("aria-busy"),w.innerHTML=Pt("Couldn't load that report just now.",B.alert),!1);Me=a,o.markReportRead(a);const k=Hn(d.timeline);w.classList.add("chatting"),w.removeAttribute("aria-busy"),w.innerHTML=`
1345
- <div class="report-head">
1346
- <span class="ttl">${v(d.title)}</span>
1347
- <span class="sub">${v(d.reference)} · ${v(d.project||"")}</span>
1348
- <span class="pill ${v(String(d.status).toLowerCase())}">${v(Vt(d.status))}</span>
1349
- ${(R=d.estimate)!=null&&R.label?`<span class="eta-line">Usually takes ${v(String(d.estimate.label))}</span>`:""}
1350
- </div>
1351
- <div class="thread" role="log" aria-live="polite" aria-label="Conversation">${k}</div>`;const q=w.querySelector(".thread");return _t.set(q,k),Xe(q),xo(w,d),d.canReply&&Hr(w,q,a),vo(w,d,a),Xe(q),!0}let it=null;function vo(a,c,u){var F,oe,Se;clearTimeout(it);const d=c.assistant,w=a.querySelector(".thread");let k=a.querySelector(".assistant");if(!d){k==null||k.remove(),(F=w==null?void 0:w.querySelector(".typing-row"))==null||F.remove();return}const q=(k==null?void 0:k.dataset.phase)||null;k||(k=document.createElement("div"),k.setAttribute("role","status"),k.setAttribute("aria-live","polite"),(oe=a.querySelector(".report-head"))==null||oe.after(k)),k.className=`assistant ${v(d.phase)}`,k.dataset.phase=d.phase;const R=d.steps||[],V=R.filter(ae=>ae.state==="done").length,W=R.some(ae=>ae.state==="active")?.5:0,G=R.length?Math.round((V+W)/R.length*100):0,E=`
1352
- <div class="as-body${q&&q!==d.phase?" entering":""}">
1353
- <div class="as-top">
1354
- <span class="as-orb" aria-hidden="true">${d.phase==="analyzing"?'<span class="as-spin"></span>':d.phase==="calling"?B.phone:d.phase==="resolved"?B.check:B.sparkle}</span>
1355
- <div class="as-text">
1356
- <div class="as-title">${v(d.title)}</div>
1357
- <div class="as-detail">${v(d.detail)}</div>
1358
- </div>
1359
- </div>
1360
- </div>
1361
- ${R.length?`
1362
- <div class="as-progress" role="progressbar" aria-label="Progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${G}"><i style="width:${G}%"></i></div>
1363
- <ol class="as-steps">${R.map(ae=>`<li class="${v(ae.state)}"${ae.state==="active"?' aria-current="step"':""}><span class="dot"></span><span class="lbl">${v(ae.label)}</span></li>`).join("")}</ol>`:""}`,te=JSON.stringify([d.phase,d.title,d.detail,R]);if(k.dataset.key!==te){const ae=k.querySelector(".as-progress i"),ue=ae==null?void 0:ae.style.width;k.innerHTML=E,k.dataset.key=te;const Ve=k.querySelector(".as-progress i");Ve&&ue&&(Ve.style.width=ue,requestAnimationFrame(()=>{Ve.style.width=`${G}%`}))}if(w){const ae=Un(w),ue=w.querySelector(".typing-row");d.phase!=="analyzing"?ue==null||ue.remove():(!ue||ue!==w.lastElementChild)&&(ue==null||ue.remove(),w.insertAdjacentHTML("beforeend",tr()),ae&&Xe(w))}d.phase==="analyzing"?it=setTimeout(()=>st(u),3e3):d.phase==="calling"?it=setTimeout(()=>st(u),1e4):d.phase==="needs_info"&&q!=="needs_info"&&((Se=a.querySelector(".composer textarea"))==null||Se.focus())}function xo(a,c){var d,w;if((d=a.querySelector(".fix-verification"))==null||d.remove(),!c.resolvedAt||!["RESOLVED","CLOSED"].includes(c.status))return;const u=document.createElement("div");u.className="fix-verification",c.verification?u.textContent=c.verification==="CONFIRMED"?"You confirmed that this fix works.":"Your feedback was recorded.":(u.innerHTML='<p>Does the fix work for you?</p><textarea maxlength="4000" aria-label="Fix feedback" placeholder="What happens when you try again? (optional)"></textarea><button type="button" data-result="CONFIRMED">It works now</button> <button type="button" data-result="STILL_HAPPENING">Still happening</button><p class="feedback-status" role="status"></p>',u.querySelectorAll("[data-result]").forEach(k=>{k.onclick=async()=>{u.querySelectorAll("button").forEach(q=>{q.disabled=!0});try{await o.verifyFix(c.id,{result:k.dataset.result,resolvedAt:c.resolvedAt,note:u.querySelector("textarea").value}),await sn(c.id)}catch(q){u.querySelector(".feedback-status").textContent=q.message,u.querySelectorAll("button").forEach(R=>{R.disabled=!1})}}})),(w=a.querySelector(".report-head"))==null||w.after(u)}async function st(a){const c=p.querySelector(".history.chatting"),u=c==null?void 0:c.querySelector(".thread");if(!u||Me!==a)return;let d=null;try{d=await o.myReport(a)}catch{}if(!d||Me!==a)return;xo(c,d);const w=Un(u),k=Hn(d.timeline);_t.get(u)!==k&&(u.innerHTML=k,_t.set(u,k)),vo(c,d,a),w&&Xe(u);const q=c.querySelector(".report-head .pill");q&&(q.className=`pill ${String(d.status).toLowerCase()}`,q.textContent=Vt(d.status))}function Hr(a,c,u){var Ve;const d=document.createElement("div");d.className="composer",d.innerHTML=`
1364
- <div class="staged" hidden></div>
1365
- <div class="row">
1366
- <div class="tools">
1367
- ${r.attachments===!1?"":`
1368
- <button class="tool" type="button" data-act="attach" title="Attach a file" aria-label="Attach a file">${B.paperclip}</button>
1369
- <button class="tool" type="button" data-act="voice" title="Record a voice note" aria-label="Record a voice note">${B.mic}</button>`}
1370
- <button class="tool" type="button" data-act="livecall" title="Call the team" aria-label="Call the team" hidden>${B.phone}</button>
1371
- </div>
1372
- <textarea rows="1" placeholder="Write a reply…" aria-label="Write a reply"></textarea>
1373
- <button class="send" type="button" data-act="send" title="Send" aria-label="Send" disabled>${B.send}</button>
1374
- </div>
1375
- <div class="note" hidden></div>
1376
- <input type="file" hidden multiple accept="${wn.join(",")}">`,a.appendChild(d);const w=d.querySelector(".staged"),k=d.querySelector("textarea"),q=d.querySelector('[data-act="send"]'),R=d.querySelector('[data-act="voice"]'),V=d.querySelector('[data-act="livecall"]'),W=d.querySelector("input[type=file]"),G=d.querySelector(".note"),E=[];let te=null,F=!1;function oe(O,K=""){G.hidden=!O,G.className=`note ${K}`,G.textContent=O||""}function Se(){const O=E.filter(K=>K.storageKey);q.disabled=F||!k.value.trim()&&!O.length,w.hidden=!E.length,w.innerHTML=E.map((K,xe)=>`
1377
- <span class="chip ${K.storageKey?"":"uploading"}">
1378
- <span class="nm">${v(K.name)}</span>
1379
- <span class="sz">${K.storageKey?qe(K.size):"sending…"}</span>
1380
- <button type="button" data-drop="${xe}" aria-label="Remove ${v(K.name)}">${B.close}</button>
1381
- </span>`).join("");for(const K of w.querySelectorAll("[data-drop]"))K.addEventListener("click",()=>{E.splice(Number(K.dataset.drop),1),Se()})}async function ae(O,K,xe){const Y={name:xe,size:O.size,storageKey:null};E.push(Y),Se();try{const{storageKey:cn,mime:Nr}=await o.uploadAttachment(O,K);Y.storageKey=cn,Y.mime=Nr}catch(cn){E.splice(E.indexOf(Y),1),oe(cn.message||"That file could not be sent.","err")}Se()}k.addEventListener("input",()=>{k.style.height="auto",k.style.height=`${Math.min(k.scrollHeight,120)}px`,Se()}),k.addEventListener("keydown",O=>{O.key==="Enter"&&!O.shiftKey&&!O.isComposing&&(O.preventDefault(),ue())}),(Ve=d.querySelector('[data-act="attach"]'))==null||Ve.addEventListener("click",()=>W.click()),W.addEventListener("change",()=>{oe("");for(const O of W.files){if(O.size>o.config.maxAttachmentBytes){oe(`${O.name} is larger than ${qe(o.config.maxAttachmentBytes)}.`,"err");continue}ae(O,O.type,O.name)}W.value=""}),R&&!Yo()&&(R.disabled=!0,R.title="This browser cannot record audio"),R==null||R.addEventListener("click",async()=>{if(te){const O=te;te=null,R.classList.remove("recording"),R.innerHTML=B.mic,oe("");const{blob:K,mime:xe}=await O.stop();K.size&&ae(K,xe,"Voice note");return}try{oe(""),te=await ea({maxMs:o.config.maxRecordingMs,onTick:O=>oe(`Recording — ${ut(O)}. Tap the mic to stop.`),onAutoStop:()=>R.click()}),R.classList.add("recording"),R.innerHTML=B.stopSquare}catch{te=null,oe("The microphone is not available — check the browser permission.","err")}}),o.canCallTeam(u).then(O=>{O&&(V.hidden=!1)}).catch(()=>{}),V.addEventListener("click",async()=>{V.disabled=!0,oe("");try{const O=await o.startLiveCall(u);Dr(a,c,O)}catch(O){oe(O.message.replace("[BugCatch] ",""),"err")}finally{V.disabled=!1}}),q.addEventListener("click",ue),c.addEventListener("click",O=>{var xe,Y;const K=(Y=(xe=O.target).closest)==null?void 0:Y.call(xe,"[data-quick]");!K||F||(k.value=K.dataset.quick,ue())});async function ue(){var xe;const O=k.value.trim(),K=E.filter(Y=>Y.storageKey);if(!(F||!O&&!K.length)){F=!0,Se(),oe(""),c.insertAdjacentHTML("beforeend",Dn({from:"reporter",body:O,at:new Date().toISOString(),attachments:K.map(Y=>({type:"FILE",name:Y.name,size:Y.size}))},"pending")),Xe(c,{smooth:!0});try{await Sn({apiBase:o.config.apiBase,bugId:u,body:O,attachments:K.map(Y=>({storageKey:Y.storageKey,mime:Y.mime,name:Y.name,size:Y.size}))}),k.value="",k.style.height="auto",E.length=0,await st(u)}catch(Y){(xe=c.querySelector(".msg-row.pending"))==null||xe.classList.replace("pending","failed"),oe(Y.message||"That could not be sent — try again.","err")}finally{F=!1,Se()}}}Se()}function Dr(a,c,u){const d=document.createElement("div");d.className="livecall",d.innerHTML=`
1382
- <span class="call-dot" aria-hidden="true"></span>
1383
- <span class="who" role="status" aria-live="polite">Calling the team…</span>
1384
- <span class="secs" hidden></span>
1385
- ${u!=null&&u.recorded?'<span class="call-kept">This call is recorded.</span>':""}
1386
- <button class="act" type="button" data-act="mute" hidden>Mute</button>
1387
- <button class="act danger" type="button" data-act="hangup">Cancel</button>`,a.insertBefore(d,c);const w=d.querySelector(".who"),k=d.querySelector(".secs"),q=d.querySelector(".call-dot"),R=d.querySelector('[data-act="mute"]'),V=d.querySelector('[data-act="hangup"]');let W=null;const G=()=>{W&&(clearInterval(W),W=null)},E=F=>{G(),q.classList.add("off"),d.classList.add("over"),w.textContent=F,k.hidden=!0,R.hidden=!0,V.textContent="Dismiss",V.classList.remove("danger")},te=o.on("call",({state:F})=>{if(F==="ringing"&&(w.textContent="Calling the team…"),F==="connecting"&&(w.textContent="Someone answered — connecting…"),F==="connected"){w.textContent="You're on a call with the team",R.hidden=!1,k.hidden=!1;const oe=Date.now();G(),W=setInterval(()=>{k.textContent=ut(Math.floor((Date.now()-oe)/1e3))},1e3),k.textContent="0s"}F==="missed"&&E("Nobody was free. Send a message instead and the team will pick it up."),F==="ended"&&E("Call ended."),F==="failed"&&E("The call dropped.")});R.addEventListener("click",()=>{const F=u.setMuted(!u.isMuted());R.textContent=F?"Unmute":"Mute",w.textContent=F?"You're muted":"You're on a call with the team"}),V.addEventListener("click",()=>{te(),G(),o.endCall(),d.remove(),st(Me)})}let Me=null;return Qe=o.on("update",a=>{!Me||(a==null?void 0:a.bugId)!==Me||(o.markReportRead(Me),st(Me))}),t.appendChild(p),me(),A.focus(),{destroy:Ne,close:we}}function Hn(t){const e=t||[];if(!e.length)return"";let n=null,o=null;return e.map(i=>{var m,h,f,p;const r=Ye(i.at),s=r&&r!==n?`<div class="day-sep" role="separator">${v(rr(i.at))}</div>`:"";if(s&&(n=r,o=null),!i.from)return o=null,s+er(i);const l={from:i.from,author:(m=i.reply)==null?void 0:m.author,automated:(h=i.reply)==null?void 0:h.automated,body:(f=i.reply)==null?void 0:f.body,at:i.at,attachments:(p=i.reply)==null?void 0:p.attachments},g=Ga(l),b=o&&o.who===g&&Math.abs(new Date(i.at).getTime()-new Date(o.at).getTime())<5*6e4;return o={who:g,at:i.at},s+Dn(l,b?"cont":"")}).join("")}function Ga(t){return t.from==="reporter"?"me":t.automated?"assistant":`team:${t.author||""}`}function er(t){const e=Nn(t.at);return`
1388
- <div class="sys k-${v(t.kind)}">
1389
- <span class="dot" aria-hidden="true"></span>${v(t.summary||"")}${e?`
1390
- <span class="t">· ${v(e)}</span>`:""}
1391
- </div>`}function Dn(t,e=""){const n=t.from==="reporter",o=t.body||"",i=t.attachments||[],r=!n&&!!t.automated,s=o?`<span class="msg-text">${v(o)}</span>`:"",l=i.length?`<div class="att">${i.map(ir).join("")}</div>`:"";let g="",b="";if(r)g='<span class="msg-who auto">Assistant <span class="tag">AI</span></span>',b=`<span class="av bot" aria-hidden="true">${B.sparkle}</span>`;else if(!n){const h=t.author||"Support team";g=`<span class="msg-who">${v(h)}</span>`,b=`<span class="av" aria-hidden="true">${v(ar(h))}</span>`}e.includes("cont")&&(g="");const m=r&&/it goes to the same person/i.test(o)?`<div class="quick" role="group" aria-label="Answer">
1392
- <button type="button" data-quick="Yes, this is the problem">${B.check}That's right</button>
1393
- <button type="button" data-quick="No, that is not the problem">That's not it</button>
1394
- </div>`:r&&Pa.test(o)?`<div class="quick" role="group" aria-label="Did this answer your question?">
1395
- <button type="button" data-quick="Yes, that answers it">${B.check}Yes, that answers it</button>
1396
- <button type="button" data-quick="No, I still need help">No, I still need help</button>
1397
- </div>`:"";return`
1398
- <div class="msg-row ${n?"mine":"theirs"} ${r?"auto":""} ${e}">
1399
- ${b}
1400
- ${g}
1401
- <div class="bubble ${o?"":"silent"}">${s}${l}</div>
1402
- ${m}
1403
- <span class="msg-when">${v(Nn(t.at)||yt(t.at))}</span>
1404
- </div>`}function tr(){return`
1405
- <div class="typing-row">
1406
- <span class="av bot" aria-hidden="true">${B.sparkle}</span>
1407
- <span class="msg-who auto">Assistant <span class="tag">AI</span></span>
1408
- <div class="bubble typing" role="img" aria-label="The assistant is working on it"><i></i><i></i><i></i></div>
1409
- </div>`}function Kt({title:t,sub:e,id:n="",back:o=!1}){return`
1410
- <div class="head">
1411
- ${o?`<button class="icon-btn back" type="button" data-act="back" aria-label="Back to your reports">${B.back}</button>`:""}
1412
- <div class="mark" aria-hidden="true">${B.sparkle}</div>
1413
- <div class="titles">
1414
- <h2${n?` id="${n}"`:""}>${v(t)}</h2>
1415
- <p>${v(e)}</p>
1416
- </div>
1417
- <button class="icon-btn" type="button" data-act="close" aria-label="Close">${B.close}</button>
1418
- </div>`}function Pt(t,e){return`<div class="empty"><span class="empty-ico" aria-hidden="true">${e}</span>${v(t)}</div>`}function nr(t){return Array.from({length:t},()=>`
1419
- <div class="row" aria-hidden="true">
1420
- <span class="row-ico"></span>
1421
- <span class="who"><span class="skel" style="width:70%"></span><span class="skel" style="width:40%;height:9px"></span></span>
1422
- </div>`).join("")+'<span class="sr-only">Loading…</span>'}function or(){return`
1423
- <div aria-hidden="true" style="display:flex;flex-direction:column;gap:10px;padding-top:6px">
1424
- <span class="skel" style="width:42%;height:34px;border-radius:16px;align-self:center"></span>
1425
- <span class="skel" style="width:66%;height:52px;border-radius:18px"></span>
1426
- <span class="skel" style="width:48%;height:38px;border-radius:18px;align-self:flex-end"></span>
1427
- </div>
1428
- <span class="sr-only">Loading…</span>`}const _t=new WeakMap;function ar(t){const e=String(t).trim().split(/\s+/).filter(Boolean);return e.length?(e[0][0]+(e.length>1?e[e.length-1][0]:"")).toUpperCase():"?"}function Ye(t){const e=new Date(t);return Number.isFinite(e.getTime())?`${e.getFullYear()}-${e.getMonth()}-${e.getDate()}`:null}function rr(t){const e=new Date(t),n=new Date,o=new Date(n.getFullYear(),n.getMonth(),n.getDate()-1);if(Ye(e)===Ye(n))return"Today";if(Ye(e)===Ye(o))return"Yesterday";try{return e.toLocaleDateString(void 0,{weekday:"short",day:"numeric",month:"short",...e.getFullYear()!==n.getFullYear()?{year:"numeric"}:{}})}catch{return yt(t)}}function Nn(t){const e=new Date(t);if(!Number.isFinite(e.getTime()))return"";try{return e.toLocaleTimeString(void 0,{hour:"numeric",minute:"2-digit"})}catch{return""}}function ir(t){const e=v(t.name||"attachment");if(!t.url)return`<span class="file"><span class="nm">${e}</span></span>`;const n=v(t.url);return t.type==="SCREENSHOT"?`<a href="${n}" target="_blank" rel="noopener noreferrer"><img src="${n}" alt="${e}" loading="lazy"></a>`:t.type==="AUDIO"?`<audio controls preload="metadata" src="${n}"></audio>`:t.type==="VIDEO"?`<video controls preload="metadata" src="${n}"></video>`:`
1429
- <a class="file" href="${n}" target="_blank" rel="noopener noreferrer" download>
1430
- <span class="nm">${e}</span>
1431
- ${t.size?`<span class="sz">${v(qe(t.size))}</span>`:""}
1432
- </a>`}function Un(t){return t.scrollHeight-t.scrollTop-t.clientHeight<40}function Xe(t,{smooth:e=!1}={}){const n=typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches;e&&!n&&t.scrollTo?t.scrollTo({top:t.scrollHeight,behavior:"smooth"}):t.scrollTop=t.scrollHeight}function Vt(t){return{OPEN:"Open",IN_PROGRESS:"In progress",RESOLVED:"Fixed",CLOSED:"Closed"}[t]||String(t||"").toLowerCase()}function yt(t){const e=new Date(t).getTime();if(!Number.isFinite(e))return"";const n=Math.floor((Date.now()-e)/864e5);if(n<=0)return"today";if(n===1)return"yesterday";if(n<30)return`${n} days ago`;const o=Math.floor(n/30);return o===1?"a month ago":`${o} months ago`}function v(t){return String(t).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e])}const Kn=8e3;function sr(t=Kn){const e=window.AudioContext||window.webkitAudioContext;try{return new e({sampleRate:t})}catch{return new e}}async function cr(t){if(t.state==="suspended")try{await t.resume()}catch{}}function lr(t,e,n){const o=e/n,i=new Float32Array(Math.floor(t.length/o));for(let r=0;r<i.length;r++){const s=Math.floor(r*o),l=Math.min(Math.floor((r+1)*o),t.length);let g=0;for(let b=s;b<l;b++)g+=t[b];i[r]=l>s?g/(l-s):0}return i}const dr=132,Pn=32635;function ur(t){const e=new Uint8Array(t.length);for(let n=0;n<t.length;n++){const o=Math.max(-1,Math.min(1,t[n]));let i=Math.round(o*32767);const r=i<0?128:0;r&&(i=-i),i>Pn&&(i=Pn),i+=dr;let s=7;for(let g=16384;!(i&g)&&s>0;s--,g>>=1);const l=i>>s+3&15;e[n]=~(r|s<<4|l)&255}return e}const pr=512;function hr(t,e,n,o=Kn){const i=t.createMediaStreamSource(e),r=t.createScriptProcessor(pr,1,1);r.onaudioprocess=l=>{const g=l.inputBuffer.getChannelData(0),b=t.sampleRate===o?g:lr(g,t.sampleRate,o);n(ur(b))},i.connect(r);const s=t.createGain();return s.gain.value=0,r.connect(s),s.connect(t.destination),{stop(){r.onaudioprocess=null;try{i.disconnect()}catch{}try{r.disconnect()}catch{}try{s.disconnect()}catch{}}}}const fr="voiceai.realtime.v1",gr=[{urls:"stun:stun.l.google.com:19302"}];function Wt(){return navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:1}})}function Ft(){var t;return typeof window<"u"&&typeof window.RTCPeerConnection=="function"&&!!((t=navigator.mediaDevices)!=null&&t.getUserMedia)}async function _n({realtimeUrl:t,stream:e,ringsUntil:n,onState:o,onAudioBlocked:i,onMicrophone:r}){const s=(y,T)=>{try{o==null||o(y,T)}catch{}},l=y=>{try{i==null||i(y)}catch{}},g=y=>{try{r==null||r(y)}catch{}};if(!Ft()){const y=new Error("[BugCatch] this browser cannot make live calls.");throw s("failed",{reason:"unsupported"}),y}let b=e;if(!b)try{b=await Wt()}catch(y){throw s("failed",{reason:"microphone",error:String((y==null?void 0:y.name)||y)}),y}let m;try{m=await br(t)}catch(y){throw Wn(b),s("failed",{reason:"connect"}),y}let h=null,f=null,p=!1,x=null,A=null,z=null,S=!1,M=null,I=null,j=!1;const Q=[];let U=!1,P=!1,C=!1,$=null,_=null,Te=0;const kt=2e3,fe=5e3,Ge=3e4,He=4,re=(y,T)=>{if(!p){p=!0,x&&(clearTimeout(x),x=null),Qe(),I==null||I(),I=null,Ee(),M==null||M(),M=null;try{h==null||h.close()}catch{}Wn(b),f&&(f.srcObject=null,f.remove(),f=null);try{m.readyState===WebSocket.OPEN&&m.close(1e3,"call over")}catch{}s(y,T)}},be=y=>{m.readyState===WebSocket.OPEN&&m.send(JSON.stringify(y))},Le=async()=>{if(j||z||p||!A||!A.audio&&!A.transcript)return;j=!0;const y=A.encoding||"mulaw";if(y!=="mulaw"){Vn(`this call is kept as ${y}, which this SDK cannot produce.`);return}const T=Number(A.sample_rate)||8e3;try{const D=sr(T);if(await cr(D),p){try{D.close()}catch{}return}const ie=hr(D,b,N=>{m.readyState===WebSocket.OPEN&&m.send(N)},T);z={context:D,tap:ie}}catch(D){Vn(`could not send the copy this call is keeping: ${String((D==null?void 0:D.message)||D)}`)}};function Ee(){if(j=!1,!z)return;const{context:y,tap:T}=z;z=null;try{T.stop()}catch{}try{y.close()}catch{}}function De(){var T;if(!f||p)return;let y;try{y=(T=f.play)==null?void 0:T.call(f)}catch{tt();return}if(!y||typeof y.then!="function"){et();return}y.then(et,tt)}function et(){M==null||M(),M=null,S&&(S=!1,l(!1))}function tt(){p||(S||(S=!0,l(!0)),ze())}function ze(){if(M||typeof window>"u"||typeof window.addEventListener!="function")return;const y=()=>De(),T=["pointerdown","keydown","touchstart"];for(const D of T)window.addEventListener(D,y,{capture:!0,passive:!0});M=()=>{for(const D of T)try{window.removeEventListener(D,y,{capture:!0})}catch{}}}const nt=y=>{h=new RTCPeerConnection({iceServers:y!=null&&y.length?y:gr});for(const T of b.getAudioTracks())h.addTrack(T,b);I=$t(),h.onicecandidate=T=>{T.candidate&&be({type:"signal",signal:{kind:"candidate",payload:T.candidate.toJSON()}})},h.ontrack=T=>{var ie;const[D]=T.streams;D&&(f||(f=document.createElement("audio"),f.autoplay=!0,f.playsInline=!0,(ie=f.setAttribute)==null||ie.call(f,"playsinline",""),f.style.cssText="position:fixed;width:0;height:0;opacity:0;pointer-events:none",document.body.appendChild(f)),f.srcObject=D,De())},h.onconnectionstatechange=St,h.oniceconnectionstatechange=St};function St(){if(!h||p)return;const y=h.connectionState||h.iceConnectionState;if(y==="connected"||y==="completed"){P=!0,Te=0,Qe(),C=!1,s("connected");return}if(y==="disconnected"){if(!P)return;Oe(kt);return}if(y==="failed"){if(!P){re("failed",{reason:"ice"});return}Oe(0)}}function Oe(y){p||(C||(C=!0,s("reconnecting"),_=setTimeout(()=>re("failed",{reason:"network"}),Ge)),!$&&($=setTimeout(Ne,y)))}function Qe(){$&&(clearTimeout($),$=null),_&&(clearTimeout(_),_=null)}async function Ne(){if($=null,p||!h)return;const y=h.connectionState||h.iceConnectionState;if(!(y==="connected"||y==="completed")){if(!U){be({type:"signal",signal:{kind:"restart",payload:{}}}),$=setTimeout(Ne,fe);return}Te>=He||(Te+=1,await we(),$=setTimeout(Ne,fe))}}async function we(){if(!(p||!h))try{const y=await h.createOffer({iceRestart:!0});await h.setLocalDescription(y),be({type:"signal",signal:{kind:"offer",payload:h.localDescription.toJSON()}})}catch{}}function $t(){let T=null,D=!1;const ie=N=>{if(T&&(clearTimeout(T),T=null),N){if(!D)return;D=!1,g(!0);return}D||(T=setTimeout(()=>{T=null,!p&&(D=!0,g(!1))},600))};for(const N of b.getAudioTracks())N.onmute=()=>ie(!1),N.onunmute=()=>ie(!0),N.onended=()=>{T&&(clearTimeout(T),T=null),!D&&(D=!0,g(!1))};return()=>{T&&(clearTimeout(T),T=null);for(const N of b.getAudioTracks())N.onmute=null,N.onunmute=null,N.onended=null}}const J=async()=>{try{const y=await h.createOffer();await h.setLocalDescription(y),be({type:"signal",signal:{kind:"offer",payload:h.localDescription.toJSON()}})}catch{re("failed",{reason:"negotiation"})}},Ct=async y=>{try{await h.setRemoteDescription(new RTCSessionDescription(y));const T=await h.createAnswer();await h.setLocalDescription(T),be({type:"signal",signal:{kind:"answer",payload:h.localDescription.toJSON()}}),await ot()}catch{re("failed",{reason:"negotiation"})}},ot=async()=>{for(;Q.length;){const y=Q.shift();try{await h.addIceCandidate(new RTCIceCandidate(y))}catch{}}};let me=Promise.resolve();m.onmessage=y=>{me=me.then(()=>Tt(y)).catch(()=>{})};const Tt=async y=>{var D,ie,N,ke,Z;if(typeof y.data!="string")return;let T;try{T=JSON.parse(y.data)}catch{return}switch(T.type){case"session.created":{if(nt((D=T.data)==null?void 0:D.ice_servers),A=((ie=T.data)==null?void 0:ie.capture)||null,(N=T.data)!=null&&N.peer_present){s("connecting"),Le();break}s("ringing"),Zt();break}case"peer.joined":{x&&(clearTimeout(x),x=null),s("connecting"),U=!!((ke=T.data)!=null&&ke.should_offer),U&&await J(),Le();break}case"signal":{if(!h)break;const{kind:ee,payload:ne}=T.data||{};if(ee==="offer")await Ct(ne);else if(ee==="answer")try{await h.setRemoteDescription(new RTCSessionDescription(ne)),await ot()}catch{re("failed",{reason:"negotiation"})}else if(ee==="candidate")if(h.remoteDescription)try{await h.addIceCandidate(new RTCIceCandidate(ne))}catch{}else Q.push(ne);else ee==="restart"&&U&&await we();break}case"peer.left":re("ended",{reason:"peer_left"});break;case"error":re("failed",{reason:"server",detail:(Z=T.error)==null?void 0:Z.detail});break;case"session.ended":re("ended",{reason:"server"});break}};m.onclose=()=>re("ended",{reason:"disconnected"}),m.onerror=()=>re("failed",{reason:"socket"});function Zt(){if(!n)return;const y=new Date(n).getTime()-Date.now();Number.isFinite(y)&&(x=setTimeout(()=>re("missed"),Math.max(0,y)))}return{hangUp(){be({type:"session.end"}),re("ended",{reason:"hung_up"})},setMuted(y){for(const T of b.getAudioTracks())T.enabled=!y;return y},isMuted(){return b.getAudioTracks().every(y=>!y.enabled)}}}function br(t){return new Promise((e,n)=>{let o;try{o=new WebSocket(t,fr)}catch(i){n(i);return}o.onopen=()=>{o.onopen=null,o.onerror=null,e(o)},o.onerror=()=>{o.onopen=null,o.onerror=null,n(new Error("[BugCatch] could not connect the call."))}})}function Vn(t){try{console.warn(`[BugCatch] ${t}`)}catch{}}function Wn(t){var e;for(const n of((e=t==null?void 0:t.getTracks)==null?void 0:e.call(t))??[])try{n.stop()}catch{}}const wt={cycleMs:3e3,peak:.045,bursts:[{at:0,seconds:.4,hz:[440,660]},{at:.55,seconds:.4,hz:[440,660]}]};let Fn=!1;if(typeof window<"u"&&window.addEventListener)for(const t of["pointerdown","keydown","touchstart"])window.addEventListener(t,()=>{Fn=!0},{once:!0,capture:!0,passive:!0});function mr(){var t;try{return!!((t=navigator.userActivation)!=null&&t.hasBeenActive)}catch{return!1}}function yr(){if(!Fn&&!mr())return()=>{};const t=typeof window<"u"&&(window.AudioContext||window.webkitAudioContext);if(!t)return()=>{};let e;try{e=new t}catch{return()=>{}}let n=!1,o=null;const i=[],r=({at:g,seconds:b,hz:m})=>{const h=e.currentTime+g,f=e.createGain();f.connect(e.destination),f.gain.setValueAtTime(0,h),f.gain.linearRampToValueAtTime(wt.peak,h+.02),f.gain.setValueAtTime(wt.peak,h+b-.06),f.gain.linearRampToValueAtTime(0,h+b);for(const p of m){const x=e.createOscillator();x.type="sine",x.frequency.value=p,x.connect(f),x.start(h),x.stop(h+b+.02),i.push(x)}},s=()=>{if(!n)try{wt.bursts.forEach(r)}catch{}},l=()=>{n||(s(),o=setInterval(s,wt.cycleMs))};return e.state==="running"?l():e.resume().then(l).catch(()=>{}),()=>{n=!0,o&&clearInterval(o);for(const g of i)try{g.stop()}catch{}try{e.close()}catch{}}}function wr(t){const e=Date.parse(t||"");return Number.isNaN(e)?0:Math.max(0,Math.round((e-Date.now())/1e3))}const Yn=["flex:1","padding:9px 12px","border:0","border-radius:8px","font:600 13px/1.4 inherit","cursor:pointer"].join(";");function vr({call:t,accent:e="#5b6ef5",position:n="bottom-right",onAnswer:o,onDecline:i,onExpire:r}){if(typeof document>"u"||!document.body)return()=>{};const[s,l]=String(n).split("-"),g=document.createElement("div");g.setAttribute("role","alertdialog"),g.setAttribute("aria-live","assertive"),g.setAttribute("aria-label",`${t.from||"Support"} is calling about ${t.reference}`),g.style.cssText=["position:fixed",`${s==="top"?"top":"bottom"}:20px`,`${l==="left"?"left":"right"}:20px`,"z-index:2147483647","width:300px","max-width:calc(100vw - 40px)","padding:14px","border-radius:12px","background:#1b1e26","color:#eceef2",'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"box-shadow:0 18px 44px rgba(0,0,0,0.5)",`border:1px solid ${e}`,"opacity:0","transform:translateY(8px)","transition:opacity .2s ease,transform .2s ease"].join(";");const b=document.createElement("div");b.style.cssText="font-weight:600;font-size:14px;margin-bottom:2px",b.textContent=`${t.from||"Support"} is calling`;const m=document.createElement("div");m.style.cssText="color:#a4abba;margin-bottom:10px",m.textContent=t.project?`About your report ${t.reference} · ${t.project}`:`About your report ${t.reference}`;const h=document.createElement("div");h.style.cssText="color:#7d8598;font-size:12px;margin-bottom:10px";const f=document.createElement("button");f.type="button",f.textContent="Answer",f.style.cssText=`${Yn};background:${e};color:#fff`;const p=document.createElement("button");p.type="button",p.textContent="Not now",p.style.cssText=`${Yn};background:#2b2f3a;color:#c8cedb`;const x=document.createElement("div");if(x.style.cssText="display:flex;gap:8px",x.append(f,p),g.append(b,m,h),t.recorded){const j=document.createElement("div");j.style.cssText="color:#7d8598;font-size:12px;margin-bottom:10px",j.textContent="This call will be recorded.",g.append(j)}g.append(x),document.body.appendChild(g);const A=yr();let z=!1,S=null;const M=()=>{z||(z=!0,S&&clearInterval(S),A(),g.style.opacity="0",g.style.transform="translateY(8px)",setTimeout(()=>g.remove(),250))},I=()=>{const j=wr(t.ringsUntil);if(h.textContent=j?`Ringing — ${j}s`:"The call has stopped ringing.",!j){M();try{r==null||r()}catch{}}};return I(),S=setInterval(I,1e3),f.addEventListener("click",()=>{M();try{o==null||o()}catch{}}),p.addEventListener("click",()=>{M();try{i==null||i()}catch{}}),requestAnimationFrame(()=>{g.style.opacity="1",g.style.transform="translateY(0)"}),M}const vt=120,xr=500,xt=30,kr=1e3,Yt={loanId:["loanId","loanNumber"],applicationId:["applicationId"],customerNumber:["customerNumber","customerId"]},Sr=/[\u0000-\u001f\u007f]/;function $r(t){if(typeof t=="number")return Number.isFinite(t)?String(t):null;if(typeof t!="string")return null;const e=t.trim();return!e||e.length>vt||Sr.test(e)?null:e}function Ce(t){return t===null?"null":Array.isArray(t)?"an array":typeof t=="object"?"an object":`a ${typeof t}`}function Xt(t,e,n,o,i){for(const r of n){if(!(r in t)||t[r]===void 0||t[r]===null||t[r]==="")continue;const s=$r(t[r]);if(s)return s;const l=t[r];i(typeof l=="string"&&l.trim().length>vt?`${o}.${r} is longer than ${vt} characters and was dropped — pass the identifier itself, not a sentence or a URL.`:`${o}.${r} should be a string or number, got ${Ce(l)} — it was dropped.`)}return null}function Xn(t){if(t===null||typeof t=="boolean")return{ok:!0,value:t};if(typeof t=="number")return Number.isFinite(t)?{ok:!0,value:t}:{ok:!1};if(typeof t=="string")return{ok:!0,value:t.slice(0,xr)};if(typeof t=="object")try{const e=JSON.stringify(t);if(e&&e.length<=kr)return{ok:!0,value:JSON.parse(e)}}catch{}return{ok:!1}}const Jn=["email","name","phone"],Cr=new Set(["id","userId",...Jn,...Object.values(Yt).flat()]);function Zn(t,e=()=>{}){if(t==null)return null;if(typeof t!="object"||Array.isArray(t))return e(`identify() takes an object like { id, email, name } or null, got ${Ce(t)} — ignored.`),null;const n={};try{const o=Xt(t,"id",["id","userId"],"identify()",e);o&&(n.id=o);for(const r of Jn){const s=t[r];if(s==null||s==="")continue;if(r==="phone"&&typeof s=="number"&&Number.isFinite(s)){n.phone=String(s);continue}if(typeof s!="string"){e(`identify().${r} should be a string, got ${Ce(s)} — it was dropped.`);continue}const l=s.trim().slice(0,r==="name"?200:vt);l&&(r==="email"&&!l.includes("@")&&e(`identify().email "${l}" does not look like an email address — reports cannot be threaded or followed up without one.`),n[r]=l)}for(const[r,s]of Object.entries(Yt)){const l=Xt(t,r,s,"identify()",e);l&&(n[r]=l)}let i=0;for(const[r,s]of Object.entries(t)){if(Cr.has(r)||s===void 0||typeof s=="function")continue;if(i>=xt){e(`identify() was given more than ${xt} extra fields; the rest were dropped.`);break}const l=Xn(s);if(!l.ok){e(`identify().${r} could not be sent (${Ce(s)} too large or not serialisable) — dropped.`);continue}n[r]=l.value,i+=1}}catch{}return!n.id&&!n.email&&e("identify() was called without an id or email — reports will not be attributed to anyone, and nothing can be looked up by user."),n}function Gn(t,e,n=()=>{}){if(e===null)return{};const o={...t||{}};if(e===void 0)return o;if(typeof e!="object"||Array.isArray(e))return n(`setContext() takes an object like { loanId, screen } or null, got ${Ce(e)} — ignored.`),o;try{const i=new Map;for(const[r,s]of Object.entries(Yt))for(const l of s)i.set(l,r);for(const[r,s]of Object.entries(e)){const l=i.get(r);if(s==null){delete o[l||r];continue}if(l){const b=Xt({[r]:s},l,[r],"setContext()",n);b?o[l]=b:delete o[l];continue}if(typeof s=="function")continue;if(!(r in o)&&Object.keys(o).length>=xt){n(`setContext() holds at most ${xt} keys; "${r}" was dropped.`);continue}const g=Xn(s);if(!g.ok){n(`setContext().${r} could not be sent (${Ce(s)} too large or not serialisable) — dropped.`);continue}o[r]=g.value}}catch{}return o}const eo=["apiBase","apiKey","projectId","consoleLimit","networkLimit","captureConsole","captureNetwork","captureErrors","widget","widgetPosition","theme","accent","attachments","maxAttachments","maxAttachmentBytes","maxRecordingMs","beforeSend","user","context","updates","updateToast","onUpdate","voice","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError","debug","drafts","knownIssues","agentIssues","release","environment","buildId"],Tr=["beforeSend","onUpdate","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError"];function Er(t){const e=t.toLowerCase().replace(/[-_]/g,"");return eo.find(n=>n.toLowerCase()===e)||null}const to=/^(your[_-]|<|xxx|todo|changeme)/i,Ir=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Mr(t,{pageHost:e=""}={}){const n=[],o=[];if(!t||typeof t!="object")return n.push("init() takes an options object: BugCatch.init({ apiBase, apiKey, projectId })."),{errors:n,warnings:o};const{apiKey:i,projectId:r,apiBase:s}=t;i?typeof i!="string"?n.push(`apiKey must be a string, got ${Ce(i)}.`):to.test(i)?n.push(`apiKey is still the placeholder "${i}" — paste the key you saved when you created it.`):Ir.test(i)?o.push("apiKey looks like a project id — did you swap apiKey and projectId?"):i.startsWith("bc_")||o.push('apiKey does not look like a BugCatch key (they start with "bc_live_") — reports will be refused with HTTP 401.'):n.push("apiKey is required — create one under Workspace Settings → API Keys."),r?typeof r!="string"?n.push(`projectId must be a string, got ${Ce(r)}.`):to.test(r)?n.push(`projectId is still the placeholder "${r}".`):r.startsWith("bc_")?o.push("projectId looks like an API key — did you swap apiKey and projectId?"):/\s/.test(r)&&o.push("projectId contains whitespace — check it was copied cleanly."):n.push("projectId is required — copy it from Project Settings → Web SDK."),s==null?!e||/^(localhost|127\.|0\.0\.0\.0|\[::1\])/.test(e)||o.push("apiBase is not set, so reports go to http://localhost:3000 — set apiBase to your BugCatch API URL."):(typeof s!="string"||!/^https?:\/\//.test(s))&&o.push(`apiBase should be an absolute http(s) URL, got ${JSON.stringify(s)}.`);for(const l of Object.keys(t)){if(eo.includes(l))continue;const g=Er(l);o.push(g?`unknown option "${l}" — did you mean "${g}"?`:`unknown option "${l}" is ignored.`)}for(const l of Tr){const g=t[l];g!=null&&typeof g!="function"&&o.push(`${l} should be a function, got ${Ce(g)} — it will not be called.`)}return{errors:n,warnings:o}}function Lr(t){return typeof t=="string"?t.replace(/\/+$/,""):t}const Je={name:"@sphoro/bugcatch-web-sdk",version:"0.17.0"},Ze={apiBase:"http://localhost:3000",apiKey:null,projectId:null,consoleLimit:100,networkLimit:50,captureConsole:!0,captureNetwork:!0,captureErrors:!0,widget:!0,widgetPosition:"bottom-right",theme:"auto",accent:"#4F46E5",attachments:!0,maxAttachments:5,maxAttachmentBytes:25*1024*1024,maxRecordingMs:12e4,beforeSend:null,user:null,updates:!0,updateToast:!0,onUpdate:null,voice:!0,onCallState:null,onIncomingCall:null,onReportSubmitted:null,onAssistantPhase:null,onError:null,context:null,debug:!1};class no{constructor(){Mo(this,L);this.config={...Ze},this.started=!1,this.teardown=[],this.consoleBuffer=ct(Ze.consoleLimit),this.networkBuffer=ct(Ze.networkLimit),this.stopWatching=null,this.notifier=null,this.call=null,this.callStarting=!1,this.ringing=null,this.listeners=new Map,this.context={},this.warned=new Set,this.phases=new Map}warn(e){this.warned.has(e)||(this.warned.add(e),console.warn(`[BugCatch] ${e}`))}debugLog(...e){this.config.debug&&console.info("[BugCatch]",...e)}init(e={}){if(this.started)return console.warn("[BugCatch] init() called twice — ignoring the second call."),this;const{errors:n,warnings:o}=Mr(e,{pageHost:typeof location<"u"?location.hostname:""});if(n.length)throw new Error(`[BugCatch] ${n[0]}`);for(const r of o)this.warn(r);const i=this.config.user;return this.config={...Ze,...e,apiBase:Lr(e.apiBase??Ze.apiBase)},this.config.user=e.user!==void 0?Zn(e.user,r=>this.warn(r.replace("identify()","init({ user })"))):i||null,e.context&&this.setContext(e.context),this.consoleBuffer=ct(this.config.consoleLimit),this.networkBuffer=ct(this.config.networkLimit),this.config.captureConsole&&this.teardown.push(Ko(this.consoleBuffer)),this.config.captureNetwork&&this.teardown.push(Po(this.networkBuffer)),this.config.captureErrors&&this.teardown.push(_o(this.consoleBuffer)),fa(this.config.projectId),this.config.widget&&this.teardown.push(Xa(this)),this.config.updates&&ge()&&(this.startWatching(),queueMicrotask(()=>H(this,L,Be).call(this))),this.started=!0,H(this,L,Lo).call(this),this.debugLog("initialised",{version:Je.version,apiBase:this.config.apiBase,projectId:this.config.projectId,user:this.config.user,context:this.context}),this}startWatching(){if(this.stopWatching)return this;this.stopWatching=Ea({apiBase:this.config.apiBase,onUpdate:n=>{if(n!=null&&n.incomingCall){H(this,L,Ro).call(this,n);return}if(n!=null&&n.callWithdrawn){H(this,L,Bt).call(this,n.callWithdrawn.callId);return}const o=ba(n);typeof this.config.onUpdate=="function"&&this.config.onUpdate(o),this.emit("update",o),H(this,L,Be).call(this)}}),this.teardown.push(()=>{var n;return(n=this.stopWatching)==null?void 0:n.call(this)}),this.teardown.push(()=>H(this,L,Bt).call(this));const e=n=>{(n.key===bt||n.key===null)&&H(this,L,Be).call(this)};return window.addEventListener("storage",e),this.teardown.push(()=>window.removeEventListener("storage",e)),this}on(e,n){if(typeof n!="function")return()=>{};const o=this.listeners.get(e)||new Set;return o.add(n),this.listeners.set(e,o),()=>this.off(e,n)}off(e,n){var o;return(o=this.listeners.get(e))==null||o.delete(n),this}emit(e,n){for(const o of this.listeners.get(e)||[])try{o(n)}catch(i){console.error(`[BugCatch] a '${e}' listener threw:`,i)}}unreadUpdates(){return Fe()}markUpdateRead(e){return ma(e),H(this,L,Be).call(this),this}markReportRead(e){return ya(e),H(this,L,Be).call(this),this}markUpdatesRead(){return kn(),H(this,L,Be).call(this),this}async verifyFix(e,{result:n,resolvedAt:o,note:i}){const r=n==="STILL_HAPPENING"?await H(this,L,At).call(this,{}):{};return qa({apiBase:this.config.apiBase,bugId:e,result:n,resolvedAt:o,note:i,metadata:r.metadata,logs:r.logs})}async myReport(e){const n=await xa({apiBase:this.config.apiBase,bugId:e});return H(this,L,Ao).call(this,e,n==null?void 0:n.assistant),n}replyToReport(e,{body:n,attachments:o}={}){return Sn({apiBase:this.config.apiBase,bugId:e,body:n,attachments:o})}async canCallNow(){return!!await this.callKind()}async callKind(){return(await this.callKinds()).live?"live":null}async callKinds(){var n;const e={agent:!1,live:!1};return!this.config.voice||!((n=this.config.user)!=null&&n.email)?e:this.kinds!==void 0?this.kinds:(this.kinds=await ka({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.kinds)}canCall(e){return this.config.voice?$n({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}async startCall(e){var n;if(!this.config.voice)throw new Error("[BugCatch] calling is switched off.");if(this.call||this.callStarting)throw new Error("[BugCatch] a call is already running.");this.callStarting=!0;try{if(!await this.callKind())throw new Error((n=this.config.user)!=null&&n.email?"[BugCatch] calling is not available on this project.":"[BugCatch] a call needs to know who is calling — call identify() with an email first.");return await H(this,L,jo).call(this,e)}finally{this.callStarting=!1}}startLiveCall(e){return this.startCall(e)}canCallTeam(e){return this.config.voice?$n({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}endCall(){var e;return(e=this.call)==null||e.hangUp(),this.call=null,this}myReports(){return va({apiBase:this.config.apiBase})}hasReports(){return!!ge()}forgetReports(){var e;return(e=this.stopWatching)==null||e.call(this),this.stopWatching=null,this.endCall(),Re(),H(this,L,Be).call(this),this}identify(e){const n=Zn(e,o=>this.warn(o));return e!=null&&!n?this:(this.config.user=n,this.debugLog("identify",n),this)}setContext(e){return this.context=Gn(this.context,e,n=>this.warn(n)),this.debugLog("context",this.context),this}getContext(){return{...this.context}}open(){return this.started?this.widgetControls?(this.widgetControls.open(),this):(this.warn("open() needs the built-in widget; it was turned off with widget: false."),this):(this.warn("open() was called before init() — nothing to open yet."),this)}close(){var e;return(e=this.widgetControls)==null||e.close(),this}isOpen(){var e;return!!((e=this.widgetControls)!=null&&e.isOpen())}async uploadAttachment(e,n,o){if(!this.started)throw new Error("[BugCatch] call init() first.");return ta({apiBase:this.config.apiBase,apiKey:this.config.apiKey,blob:e,mime:n||(e==null?void 0:e.type),onProgress:o})}async report({title:e,description:n="",priority:o="MEDIUM",metadata:i={},attachments:r=[],references:s=[],context:l=null,issue:g=null}={}){var z,S,M,I,j,Q,U,P;if(!this.started)throw new Error("[BugCatch] call init() first.");if(typeof e!="string"||!e.trim())throw new Error("[BugCatch] a title is required.");["LOW","MEDIUM","HIGH","CRITICAL"].includes(String(o).toUpperCase())||(this.warn(`priority "${o}" is not one of LOW, MEDIUM, HIGH, CRITICAL — sent as MEDIUM.`),o="MEDIUM");const b=(r||[]).filter(C=>(C==null?void 0:C.storageKey)&&dt(C.mime,lt)).map(C=>({type:C.type==="VIDEO"?"VIDEO":"SCREENSHOT",storageKey:C.storageKey,mime:dt(C.mime,lt)})),m=(Array.isArray(s)?s:[]).filter(C=>C&&typeof C.key=="string"&&C.key&&(typeof C.value=="string"||typeof C.value=="number")).map(C=>({key:C.key.slice(0,64),value:String(C.value).trim().slice(0,120)})).filter(C=>C.value);Array.isArray(s)&&m.length<s.length&&this.warn("report({ references }) takes [{ key, value }] with string values; malformed entries were dropped.");let h=await H(this,L,At).call(this,{title:e.trim(),description:typeof n=="string"?n:String(n??""),priority:String(o).toUpperCase(),metadata:i,media:b,references:m,context:l,issue:typeof g=="string"&&g?g.slice(0,32):null});const f=H(this,L,dn).call(this,h);if(f===!1)return{cancelled:!0};h=f,this.debugLog("sending report",{title:h.title,user:(z=h.metadata)==null?void 0:z.user,context:(S=h.metadata)==null?void 0:S.context,references:h.references,console:(j=(I=(M=h.logs)==null?void 0:M.console)==null?void 0:I.entries)==null?void 0:j.length,network:(P=(U=(Q=h.logs)==null?void 0:Q.network)==null?void 0:U.entries)==null?void 0:P.length});const p=$e(this.config.projectId);let x;try{x=await fetch(`${this.config.apiBase}/ingest/bugs`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey,...p!=null&&p.token?{"X-BugCatch-Session":p.token}:{}},body:JSON.stringify(h)})}catch(C){const $=new Error(`[BugCatch] could not reach ${this.config.apiBase} — check apiBase, and that this site's origin is allowed by the API.`);throw $.cause=C,H(this,L,Lt).call(this,"report",$),$}if(x.status===401&&p&&Qt(this.config.projectId),!x.ok){let C="";try{const _=await x.json();C=Array.isArray(_.message)?_.message.join(", "):_.message||""}catch{}x.status===401&&!p&&(C||(C="the apiKey was not accepted")),x.status===403&&(C||(C="the apiKey is not allowed to report to this projectId"));const $=new Error(`[BugCatch] report failed (HTTP ${x.status})${C?": "+C:""}`);throw $.status=x.status,H(this,L,Lt).call(this,"report",$),$}const A=await x.json();return H(this,L,ln).call(this,A,{title:h.title??e.trim(),via:"form"}),A.watchToken&&(Dt(A.watchToken),this.config.updates&&this.startWatching()),A}loginState(){var e;return la({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:(e=$e(this.config.projectId))==null?void 0:e.token})}referenceFields(){return this.refFields?this.refFields:(this.refFields=Ka({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.refFields)}agentIssues(){return this.agentIssueList?this.agentIssueList:(this.agentIssueList=_a({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.agentIssueList)}signedInAs(){const e=$e(this.config.projectId);return e?{email:e.email,name:e.name}:null}signIn(e,{name:n}={}){var o;return da({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,name:n??((o=this.config.user)==null?void 0:o.name)??null})}async confirmSignIn(e,n){var i;const o=await ua({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,code:n});return(i=this.config.user)!=null&&i.email||(this.config.user={email:o.email,name:o.name||void 0}),this.emit("signin",{email:o.email,name:o.name}),{email:o.email,name:o.name}}async signOut(){var o;const e=$e(this.config.projectId),n=await pa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:e==null?void 0:e.token});return e!=null&&e.email&&((o=this.config.user)==null?void 0:o.email)===e.email&&(this.config.user=null),this.emit("signout",{email:(e==null?void 0:e.email)||null}),n}knownIssues(){var e;return Ra({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,sessionToken:(e=$e(this.config.projectId))==null?void 0:e.token})}matchKnownIssues(e,{signal:n}={}){var o;return za({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,q:e||"",sessionToken:(o=$e(this.config.projectId))==null?void 0:o.token,signal:n})}async meToo(e,{email:n,name:o}={}){var s,l;const i=$e(this.config.projectId),r=await bn();return Oa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,bugId:e,reporterEmail:n||((s=this.config.user)==null?void 0:s.email),reporterName:o||((l=this.config.user)==null?void 0:l.name),metadata:{...r,user:this.config.user||void 0,...Object.keys(this.context).length?{context:this.context}:{},sdk:Je},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},sessionToken:i==null?void 0:i.token})}snapshot(){return{user:this.config.user||null,context:{...this.context},console:this.consoleBuffer.all(),network:this.networkBuffer.all(),environment:mn()}}clear(){this.consoleBuffer.clear(),this.networkBuffer.clear()}destroy(){this.endCall(),this.teardown.forEach(e=>{try{e()}catch{}}),this.teardown=[],this.notifier=null,this.started=!1}}L=new WeakSet,Mt=function(e,n){const o=this.config[e];if(typeof o=="function")try{o(n)}catch(i){console.error(`[BugCatch] your ${e} callback threw:`,i)}},Lo=function(){if(typeof window>"u"||typeof fetch!="function")return;const e=`bugcatch:hello:${this.config.projectId}`,n=new Date().toISOString().slice(0,10);try{if(window.localStorage.getItem(e)===n)return}catch{}const o=setTimeout(()=>{try{window.localStorage.setItem(e,n)}catch{}fetch(`${this.config.apiBase}/ingest/hello`,{method:"POST",keepalive:!0,headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey},body:JSON.stringify({projectId:this.config.projectId,version:Je.version,identify:!!this.config.user,context:!!(this.context&&Object.keys(this.context).length),widget:!!this.config.widget,environment:this.config.environment||null,release:this.config.release||null})}).catch(()=>{})},5e3);this.teardown.push(()=>clearTimeout(o))},Be=function(){const e=Fe();if(this.emit("unread",{count:e.length,updates:e}),!!this.config.updateToast){if(!this.notifier){if(!e.length)return;this.notifier=ja({accent:this.config.accent,position:this.config.widgetPosition,onRead:n=>this.markUpdateRead(n),onReadAll:()=>this.markUpdatesRead(),onCall:this.config.voice?n=>{this.markUpdateRead(n.id),this.startCall(n.bugId)}:null}),this.teardown.push(()=>{var n;return(n=this.notifier)==null?void 0:n.destroy()})}this.notifier.render(e)}},Ao=function(e,n){const o=n==null?void 0:n.phase;if(!e||typeof o!="string"||this.phases.get(e)===o)return;this.phases.set(e,o);const i={bugId:e,phase:o,title:n.title??null,detail:n.detail??null};this.debugLog("assistant",i),H(this,L,Mt).call(this,"onAssistantPhase",i),this.emit("assistant",i)},ln=function(e,{title:n,via:o}){if(!(e!=null&&e.id))return;const i={bugId:e.id,title:n??null,via:o,result:e};this.debugLog("report accepted",i),H(this,L,Mt).call(this,"onReportSubmitted",i),this.emit("report",i)},Lt=function(e,n){H(this,L,Mt).call(this,"onError",{action:e,error:n}),this.emit("error",{action:e,error:n})},At=async function({title:e,description:n="",priority:o="MEDIUM",metadata:i={},media:r=[],references:s=[],context:l=null,issue:g=null}){var m,h;const b=l?Gn(this.context,l,f=>this.warn(f.replace("setContext()","report({ context })"))):this.context;return{projectId:this.config.projectId,...e?{title:e}:{},description:n,priority:o,url:window.location.href,reporterEmail:(m=this.config.user)==null?void 0:m.email,reporterName:(h=this.config.user)==null?void 0:h.name,metadata:{...await bn(),release:this.config.release,environment:this.config.environment,buildId:this.config.buildId,...i,user:this.config.user||void 0,...Object.keys(b).length?{context:b}:{},sdk:Je},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},...r.length?{attachments:r}:{},...s.length?{references:s}:{},...g?{issue:g}:{}}},dn=function(e){if(typeof this.config.beforeSend!="function")return e;let n;try{n=this.config.beforeSend(e)}catch(o){return this.warn(`beforeSend threw, so nothing was sent: ${(o==null?void 0:o.message)||o}`),!1}return n===!1?!1:n&&typeof n=="object"?n:e},Bo=async function(){const e=$e(this.config.projectId),n=H(this,L,dn).call(this,await H(this,L,At).call(this,{}));if(n===!1)return this.debugLog("call cancelled by beforeSend"),{cancelled:!0};let o;try{o=await Sa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,token:e==null?void 0:e.token,payload:n})}catch(i){throw i.status===401&&e&&Qt(this.config.projectId),H(this,L,Lt).call(this,"call",i),i}return H(this,L,ln).call(this,o,{title:null,via:"call"}),o.watchToken&&(Dt(o.watchToken),this.config.updates&&this.startWatching()),o},jo=async function(e){if(!Ft())throw new Error("[BugCatch] this browser cannot make live calls.");const n=await Wt();let o,i=null;try{if(e)o=await $a({apiBase:this.config.apiBase,bugId:e});else if(i=await H(this,L,Bo).call(this),e=i.id,o=i.live_call,!(o!=null&&o.realtime_url)){const s=new Error(`[BugCatch] the report was filed, but the team could not be rung${i.call_error?`: ${i.call_error}`:"."}`);throw s.filed=i,s.reason=i.call_error||null,s}}catch(s){for(const l of n.getTracks())l.stop();throw s}const r=await _n({realtimeUrl:o.realtime_url,stream:n,ringsUntil:o.rings_until,onState:(s,l)=>{(s==="ended"||s==="failed"||s==="missed")&&(this.call=null),s==="missed"&&Ca({apiBase:this.config.apiBase,bugId:e,callId:o.call_id}),typeof this.config.onCallState=="function"&&this.config.onCallState(s,{bugId:e,live:!0,...l}),this.emit("call",{bugId:e,live:!0,state:s,...l})}});return this.call=r,Object.assign(r,{bugId:e,callId:o.call_id,live:!0,filed:i,recorded:!!o.recorded})},Ro=function(e){var i;const n=e.incomingCall;if(!this.config.voice||!Ft()||((i=this.ringing)==null?void 0:i.callId)===n.callId||this.call||this.callStarting)return;H(this,L,Bt).call(this);const o=vr({call:n,accent:this.config.accent,position:this.config.widgetPosition,onAnswer:()=>{H(this,L,zo).call(this,e.bugId,n)},onDecline:()=>{this.ringing=null,Cn({apiBase:this.config.apiBase,bugId:e.bugId,callId:n.callId}),this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"declined"})},onExpire:()=>{this.ringing=null,this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"missed"})}});if(this.ringing={callId:n.callId,dismiss:o},typeof this.config.onIncomingCall=="function")try{this.config.onIncomingCall({bugId:e.bugId,...n})}catch{}this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"ringing"})},Bt=function(e){if(this.ringing&&!(e&&this.ringing.callId!==e)){try{this.ringing.dismiss()}catch{}this.ringing=null}},zo=async function(e,n){if(this.ringing=null,this.call||this.callStarting)return;this.callStarting=!0;let o;try{o=await Wt()}catch(r){this.callStarting=!1,Cn({apiBase:this.config.apiBase,bugId:e,callId:n.callId}),this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r});return}let i;try{i=await Ta({apiBase:this.config.apiBase,bugId:e,callId:n.callId})}catch(r){for(const s of o.getTracks())s.stop();this.callStarting=!1,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"missed",error:r});return}try{const r=await _n({realtimeUrl:i.realtime_url,stream:o,onState:(s,l)=>{(s==="ended"||s==="failed"||s==="missed")&&(this.call=null),typeof this.config.onCallState=="function"&&this.config.onCallState(s,{bugId:e,live:!0,incoming:!0,...l}),this.emit("call",{bugId:e,live:!0,incoming:!0,state:s,...l})}});this.call=r,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"answered"})}catch(r){for(const s of o.getTracks())s.stop();this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r})}finally{this.callStarting=!1}};const Jt=new no;return Jt.Client=no,Jt.version=Je.version,Jt});
280
+
281
+ /* ---------- while the panel's code is on its way ----------
282
+ Usually it is already here — it is fetched once the page is idle, and again
283
+ as a pointer reaches the button. On a slow first press the launcher says it
284
+ heard, rather than looking as though it did nothing. */
285
+ .launcher[aria-busy="true"] { cursor: progress; }
286
+ .launcher[aria-busy="true"] .ico { animation: bc-busy 700ms ease-in-out infinite alternate; }
287
+ @keyframes bc-busy { to { opacity: .4; } }
288
+
289
+ /* ---------- the panel's code would not load ----------
290
+ Said above the launcher, in the form's own error style, with the retry: the
291
+ network dropped, or a Content-Security-Policy refused the file. */
292
+ .loadfail {
293
+ margin: 0; max-width: min(320px, calc(100vw - 40px));
294
+ align-items: center; box-shadow: var(--bc-shadow-sm);
295
+ }
296
+ .loadfail > span { flex: 1; }
297
+ .loadfail button {
298
+ flex: none; padding: 2px 4px; border: 0; border-radius: 6px;
299
+ background: none; color: inherit; font: inherit; font-weight: 650; cursor: pointer;
300
+ }
301
+ .loadfail button[data-act="retry"] { text-decoration: underline; text-underline-offset: 2px; }
302
+ .loadfail button svg { font-size: 13px; margin: 0; }
303
+ `;function Jt(t){const e=document.createElement("div");e.setAttribute("data-bugcatch","widget");let n=Xt()||Yt(t.config.theme);e.className=qe(n);const o=e.attachShadow({mode:"open"}),s=document.createElement("style");s.textContent=Wt(t.config.accent||null),o.appendChild(s);const i=ze[t.config.widgetPosition]?t.config.widgetPosition:"bottom-right",r=document.createElement("div");r.className=`dock ${i.includes("right")?"at-right":"at-left"}`+(i.startsWith("top")?" at-top":""),r.style.cssText=ze[i];const a={current:()=>n,label:()=>H.find(p=>p.value===n).label,cycle(){const p=H[(H.findIndex(b=>b.value===n)+1)%H.length];return n=p.value,e.className=qe(n),qt(n),p}},c=document.createElement("button");c.className="launcher",c.type="button",c.innerHTML=`<span class="ico" aria-hidden="true"><span class="when-closed"><span class="orb"></span></span><span class="when-open">${Ot}</span></span><span>Report a bug</span>`,c.setAttribute("aria-haspopup","dialog"),c.setAttribute("aria-expanded","false");let l=null;c.addEventListener("click",()=>{if(l){l.close();return}l=h()});function h(){j(),f();let p=null,b=!1;const I=()=>{b=!0,c.removeAttribute("aria-busy"),l=null},g={close:()=>p?p.close():I(),destroy:()=>p?p.destroy():I()};return c.setAttribute("aria-busy","true"),_e(t.config.chunkBase).then(({openPanel:S})=>{b||(c.removeAttribute("aria-busy"),r.classList.add("over"),c.setAttribute("aria-expanded","true"),p=S({root:o,host:e,dock:r,client:t,preloadCall:y,themeControls:a,onClose:()=>{l=null,r.classList.remove("over"),c.setAttribute("aria-expanded","false")}}))},()=>{b||(I(),M())}),g}const u=()=>{_e(t.config.chunkBase).catch(()=>{})};for(const p of["pointerenter","touchstart","focus"])c.addEventListener(p,u,{once:!0,passive:!0});const B=We(u);function y(){J(t.config.chunkBase).catch(()=>{})}let w=null;function M(){j(),w=document.createElement("div"),w.className="msg err loadfail",w.setAttribute("role","alert"),w.innerHTML=`${je}<span>The report form didn't load. Check your connection and try again.</span><button type="button" data-act="retry">Try again</button><button type="button" data-act="dismiss" aria-label="Dismiss">${Oe}</button>`,w.querySelector('[data-act="retry"]').addEventListener("click",()=>{c.focus(),l||(l=h())}),w.querySelector('[data-act="dismiss"]').addEventListener("click",()=>{c.focus(),j()}),r.prepend(w)}function j(){w==null||w.remove(),w=null}let A=null;function D(p){f(),A=zt(p,{where:"launcher",onDismiss:b=>{t.dismissAnnouncement(b),c.focus(),f()}}),w?w.after(A):r.prepend(A)}function f(){A==null||A.remove(),A=null}const k=t.config.announcements===!1?()=>{}:We(()=>{t.announcements().then(p=>{const b=p.find(I=>I.placement==="launcher");b&&!l&&e.isConnected&&D(b)}).catch(()=>{})});return r.append(c),o.appendChild(r),document.body.appendChild(e),t.widgetControls={open:()=>{l||c.click()},close:()=>{l==null||l.close()},isOpen:()=>!!l},()=>{B(),k(),l==null||l.destroy(),e.remove(),t.widgetControls=null}}function Yt(t){return t==="dark"||t==="light"?t:"auto"}function qe(t){return t==="dark"?"bc-dark":t==="light"?"bc-light":"bc-auto"}function We(t){let e=()=>{};const n=()=>{if(typeof requestIdleCallback=="function"){const o=requestIdleCallback(t,{timeout:5e3});e=()=>cancelIdleCallback(o)}else{const o=setTimeout(t,1500);e=()=>clearTimeout(o)}};return document.readyState==="complete"?n():(window.addEventListener("load",n,{once:!0}),e=()=>window.removeEventListener("load",n)),()=>e()}const Vt="Too many requests are being sent from here at the moment. Please try again in a minute.";function _(t){return t||Vt}const Zt=typeof window<"u"&&window.fetch?window.fetch.bind(window):null,Gt=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.open:null,Qt=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.send:null,en=["image/png","image/jpeg","image/webp","image/gif","video/webm","video/mp4","audio/webm","audio/ogg","audio/mpeg","audio/mp4","audio/wav","application/pdf","text/plain","text/csv","application/json","application/zip","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],Je=["image/png","image/jpeg","image/webp","video/webm","video/mp4"];function de(t,e=en){const n=String(t||"").split(";")[0].trim().toLowerCase();return e.includes(n)?n:null}async function tn({apiBase:t,apiKey:e,blob:n,mime:o,onProgress:s}){var l;const i=de(o||n.type);if(!i)throw new Error(`[BugCatch] ${o||n.type||"that file"} can't be attached.`);const r=await Zt(`${t}/ingest/presign`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":e},body:JSON.stringify({mime:i})});if(r.status===429){let h="";try{h=((l=await r.json())==null?void 0:l.message)||""}catch{}throw new Error(`[BugCatch] ${_(typeof h=="string"?h:"")}`)}if(!r.ok)throw new Error(`[BugCatch] could not start the upload (HTTP ${r.status}).`);const{key:a,uploadUrl:c}=await r.json();return await nn(c,n,i,s),{storageKey:a,mime:i}}function nn(t,e,n,o){return new Promise((s,i)=>{const r=new XMLHttpRequest;Gt.call(r,"PUT",t,!0),r.setRequestHeader("Content-Type",n),r.upload.onprogress=a=>{a.lengthComputable&&(o==null||o(a.loaded/a.total))},r.onload=()=>{r.status>=200&&r.status<300?s():i(new Error(`[BugCatch] upload failed (HTTP ${r.status}).`))},r.onerror=()=>i(new Error("[BugCatch] the browser blocked the upload to storage. If this is a new environment, the media bucket is probably missing its CORS policy.")),r.onabort=()=>i(new Error("[BugCatch] upload cancelled.")),Qt.call(r,e)})}const Y="bugcatch.watch",Ye=[2e3,5e3,15e3,3e4,6e4],on=5e3;let z=null;function rn(t){z=t||null}const he=()=>z?`${Y}:${z}`:Y;function V(){return z?`?projectId=${encodeURIComponent(z)}`:""}function C(){try{const t=window.localStorage;return(t==null?void 0:t.getItem(he()))||(t==null?void 0:t.getItem(Y))||null}catch{return null}}function fe(t){var e;if(t)try{(e=window.localStorage)==null||e.setItem(he(),t)}catch{}}function N(){var t,e;try{(t=window.localStorage)==null||t.removeItem(he()),(e=window.localStorage)==null||e.removeItem(Y)}catch{}Ve()}const Z="bugcatch.unread";function sn(t){return[(t==null?void 0:t.bugId)||"report",(t==null?void 0:t.kind)||"status",(t==null?void 0:t.at)||""].join("|")}function F(){var t;try{const e=(t=window.localStorage)==null?void 0:t.getItem(Z),n=e?JSON.parse(e):[];return Array.isArray(n)?n.filter(o=>o&&typeof o.id=="string"):[]}catch{return[]}}function G(t){var e,n;try{t.length?(e=window.localStorage)==null||e.setItem(Z,JSON.stringify(t)):(n=window.localStorage)==null||n.removeItem(Z)}catch{}}function an(t){const e={...t,id:sn(t)},n=F().filter(o=>o.id!==e.id);return n.push(e),G(n.slice(-20)),e}function cn(t){const e=F().filter(n=>n.id!==t);return G(e),e}function ln(t){const e=F().filter(n=>n.bugId!==t);return G(e),e}function Ve(){return G([]),[]}function un(t,e){let n=t,o=n.indexOf(`
304
+
305
+ `);for(;o!==-1;){const s=n.slice(0,o);n=n.slice(o+2);const i=s.split(`
306
+ `).filter(r=>r.startsWith("data:")).map(r=>r.slice(5).trim()).join(`
307
+ `);if(i)try{e(JSON.parse(i))}catch{}o=n.indexOf(`
308
+
309
+ `)}return n}async function dn({apiBase:t}){const e=C();if(!e)return{reporter:null,reports:[]};const n=await fetch(`${t}/ingest/watch/reports${V()}`,{headers:{Authorization:`Bearer ${e}`}});if(n.status===401)return N(),{reporter:null,reports:[]};if(!n.ok)throw new Error(`[BugCatch] could not load your reports (HTTP ${n.status})`);return n.json()}async function hn({apiBase:t,bugId:e}){const n=C();if(!n)return null;const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}${V()}`,{headers:{Authorization:`Bearer ${n}`}});if(o.status===401)return N(),null;if(o.status===404)return null;if(!o.ok)throw new Error(`[BugCatch] could not load that report (HTTP ${o.status})`);return o.json()}async function fn({apiBase:t,bugId:e,body:n,attachments:o}){const s=C();if(!s)throw new Error("[BugCatch] nothing filed from this browser yet.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/messages`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({...n?{body:n}:{},...o!=null&&o.length?{attachments:o}:{}})});if(i.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!i.ok){let r="";try{const a=await i.json();r=Array.isArray(a.message)?a.message.join(", "):a.message||""}catch{}throw i.status===429?new Error(_(r)):new Error(r||`[BugCatch] could not send that (HTTP ${i.status}).`)}return i.json()}async function pn({apiBase:t,apiKey:e,projectId:n}){const o={agent:!1,live:!1};try{const s=await fetch(`${t}/ingest/calls?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!s.ok)return o;const i=await s.json();return i!=null&&i.available?i.kinds&&typeof i.kinds=="object"?{agent:!1,live:!!i.kinds.live}:{agent:!1,live:i.kind==="live"}:o}catch{return o}}async function gn({apiBase:t,apiKey:e,token:n,payload:o,watchToken:s}){const i=await fetch(`${t}/ingest/calls`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":e,...n?{"X-BugCatch-Session":n}:{}},body:JSON.stringify(s?{...o,watchToken:s}:o)});if(!i.ok){let r="";try{const c=await i.json();r=Array.isArray(c.message)?c.message.join(", "):c.message||""}catch{}const a=new Error(i.status===429?_(r):r||`[BugCatch] the call could not be started (HTTP ${i.status}).`);throw a.status=i.status,a}return i.json()}async function Ze({apiBase:t,bugId:e}){const n=C();if(!n)return!1;try{const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)return!1;const s=await o.json();return!!(s!=null&&s.offered)}catch{return!1}}async function mn({apiBase:t,bugId:e}){const n=C();if(!n)throw new Error("[BugCatch] nothing filed from this browser yet.");const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{method:"POST",headers:{Authorization:`Bearer ${n}`}});if(o.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!o.ok){let s="";try{const i=await o.json();s=Array.isArray(i.message)?i.message.join(", "):i.message||""}catch{}throw new Error(s||`[BugCatch] the call could not be started (HTTP ${o.status}).`)}return o.json()}async function wn({apiBase:t,bugId:e,callId:n}){const o=C();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/give-up`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}async function bn({apiBase:t,bugId:e,callId:n}){const o=C();if(!o)throw new Error("[BugCatch] nothing filed from this browser yet.");const s=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/answer`,{method:"POST",headers:{Authorization:`Bearer ${o}`}});if(s.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(s.status===409)throw new Error("[BugCatch] that call is no longer ringing.");if(!s.ok){let i="";try{const r=await s.json();i=Array.isArray(r.message)?r.message.join(", "):r.message||""}catch{}throw new Error(i||`[BugCatch] the call could not be answered (HTTP ${s.status}).`)}return s.json()}async function Ge({apiBase:t,bugId:e,callId:n}){const o=C();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/decline`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}function yn({apiBase:t,onUpdate:e}){let n=!0,o=0,s=null;async function i(){const r=C();if(!r)throw new Error("nothing filed from this browser yet");s=new AbortController;const a=await fetch(`${t}/ingest/watch/stream${V()}`,{headers:{Authorization:`Bearer ${r}`,Accept:"text/event-stream"},signal:s.signal});if(a.status===401)throw n=!1,N(),new Error("subscription no longer valid");if(!a.ok||!a.body)throw new Error(`stream failed (${a.status})`);const c=Date.now(),l=a.body.getReader(),h=new TextDecoder;let u="";try{for(;;){const{value:B,done:y}=await l.read();if(y)break;u+=h.decode(B,{stream:!0}),u=un(u,w=>{if(w.type!=="ping")try{e(w)}catch{}})}}finally{Date.now()-c>=on&&(o=0)}}return(async()=>{for(;n;){try{await i()}catch(a){if((a==null?void 0:a.name)==="AbortError")return}if(!n)return;const r=Ye[Math.min(o,Ye.length-1)];o+=1,await new Promise(a=>setTimeout(a,r))}})(),()=>{n=!1,s==null||s.abort(),s=null}}const xn={OPEN:"is open",IN_PROGRESS:"is being worked on",RESOLVED:"has been fixed",CLOSED:"has been closed"},kn={status:null,severity:"#f2a54a",assignment:"#79a8ff",reply:"#4fd19a"},T={bg:"#111113",fg:"#f3f2ef",muted:"#b3b0aa",faint:"#8f8c86",ring:"rgba(255,255,255,.09)",brand:"#a59cff"};function Qe(t){return t!=null&&t.summary?t.summary:`Your report ${xn[t==null?void 0:t.status]||"was updated"}`}function vn(t){var e;return(t==null?void 0:t.kind)==="reply"&&((e=t.reply)!=null&&e.body)?`${t.reply.author?`${t.reply.author}: `:""}${t.reply.body}`:(t==null?void 0:t.title)||""}function Cn(t){const e=Date.parse(t||"");if(!Number.isFinite(e))return"";const n=Math.round((Date.now()-e)/6e4);if(n<1)return"just now";if(n<60)return`${n}m ago`;const o=Math.round(n/60);if(o<24)return`${o}h ago`;const s=Math.round(o/24);return s===1?"yesterday":`${s}d ago`}function In(){var t;try{const e=document.querySelector('[data-bugcatch="widget"]'),n=(t=e==null?void 0:e.shadowRoot)==null?void 0:t.querySelector(".dock"),o=(n==null?void 0:n.getBoundingClientRect().height)||0;return o?Math.round(o)+12:0}catch{return 0}}function Sn({accent:t=null,position:e="bottom-right",onRead:n=null,onReadAll:o=null,onCall:s=null}={}){const[i,r]=String(e).split("-"),a=i==="top",c=r==="left",l=new Map;let h=null,u=null;function B(){return h!=null&&h.isConnected||(h=document.createElement("div"),h.setAttribute("data-bugcatch","updates"),h.style.cssText=["position:fixed",`${a?"top":"bottom"}:${20+In()}px`,`${c?"left":"right"}:20px`,"z-index:2147482998","display:flex","flex-direction:column","gap:10px",`align-items:${c?"flex-start":"flex-end"}`,"width:min(320px,calc(100vw - 40px))","pointer-events:none"].join(";"),document.body.appendChild(h)),h}function y(f){if(!f._bcShown){f.remove();return}f.style.opacity="0",f.style.transform=`translateY(${a?-8:8}px)`,setTimeout(()=>f.remove(),250)}const w=t||ue,M=t?He(t):De;function j(f,k){const p=document.createElement("button");return p.type="button",p.textContent=f,p.style.cssText=["padding:6px 12px","border:0",`box-shadow:${k?"none":`inset 0 0 0 1px ${T.ring}`}`,"border-radius:999px",`background:${k?w:"rgba(255,255,255,.06)"}`,`color:${k?M:T.fg}`,"font:560 12px/1.4 inherit","cursor:pointer","white-space:nowrap"].join(";"),p}function A(f){var P;const k=document.createElement("div");k.setAttribute("role","status"),k.style.cssText=["box-sizing:border-box","width:100%","padding:13px 14px","border-radius:14px",`background:${T.bg}`,`color:${T.fg}`,'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${T.ring},0 20px 44px -12px rgba(0,0,0,.55),0 4px 12px rgba(0,0,0,.25)`,"opacity:0",`transform:translateY(${a?-8:8}px)`,"transition:opacity .2s ease,transform .2s ease","pointer-events:auto"].join(";");const p=document.createElement("div");p.style.cssText="display:flex;align-items:baseline;gap:8px;font-weight:600;margin-bottom:2px";const b=document.createElement("span");b.setAttribute("aria-hidden","true"),b.style.cssText=`flex:none;width:7px;height:7px;border-radius:50%;transform:translateY(-1px);background:${kn[f==null?void 0:f.kind]||t||T.brand}`;const I=document.createElement("span");I.textContent=Qe(f),p.append(b,I);const g=document.createElement("div");g.style.cssText=(f==null?void 0:f.kind)==="reply"?`color:${T.muted};padding-left:15px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden`:`color:${T.muted};padding-left:15px`,g.textContent=vn(f);const S=document.createElement("div");S.style.cssText="display:flex;align-items:center;gap:8px;margin-top:10px;padding-left:15px;flex-wrap:wrap";const O=document.createElement("span");O.style.cssText=`color:${T.faint};font-size:11px;margin-right:auto`,O.textContent=Cn(f==null?void 0:f.at);const x=j("Mark as read",!1);if(x.setAttribute("aria-label",`Mark as read: ${Qe(f)}`),x.addEventListener("click",()=>n==null?void 0:n(f.id)),S.append(O,x),(P=f==null?void 0:f.call)!=null&&P.offered&&typeof s=="function"){const ft=j("Talk it through",!0);ft.addEventListener("click",()=>s(f)),S.insertBefore(ft,x)}return k.append(p,g,S),k}function D(f){const k=document.createElement("div");k.style.cssText=["box-sizing:border-box","width:100%","display:flex","align-items:center","gap:8px","padding:8px 10px 8px 14px","border-radius:999px",`background:${T.bg}`,`color:${T.muted}`,'font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${T.ring},0 12px 30px -10px rgba(0,0,0,.5)`,"pointer-events:auto"].join(";");const p=document.createElement("span");p.style.cssText="margin-right:auto",p.textContent=`${f} more update${f===1?"":"s"}`;const b=j("Mark all as read",!1);return b.style.cssText+=";padding:4px 9px",b.addEventListener("click",()=>o==null?void 0:o()),k.append(p,b),k}return{render(f){const k=Array.isArray(f)?f:[];if(!k.length){for(const x of l.values())y(x);l.clear(),u==null||u.remove(),u=null,h==null||h.remove(),h=null;return}const p=k.slice(-3),b=new Set(p.map(x=>x.id)),I=B();for(const[x,P]of l)b.has(x)||(y(P),l.delete(x));const g=[];for(const x of p){if(l.has(x.id))continue;const P=A(x);l.set(x.id,P),g.push(P)}const S=a?[...p].reverse():p;for(const x of S)I.appendChild(l.get(x.id));const O=k.length-p.length;O>0?(u==null||u.remove(),u=D(O),a?I.appendChild(u):I.insertBefore(u,I.firstChild)):(u==null||u.remove(),u=null),requestAnimationFrame(()=>{for(const x of g)x.isConnected&&(x._bcShown=!0,x.style.opacity="1",x.style.transform="translateY(0)")})},destroy(){l.clear(),u=null,h==null||h.remove(),h=null}}}async function $n({apiBase:t,apiKey:e,projectId:n,sessionToken:o}){try{const s=await fetch(`${t}/ingest/known-issues?projectId=${encodeURIComponent(n)}`,{headers:pe(e,o)});if(!s.ok)return[];const i=await s.json();return Array.isArray(i==null?void 0:i.issues)?i.issues:[]}catch{return[]}}async function Bn({apiBase:t,apiKey:e,projectId:n,q:o,sessionToken:s,signal:i}){try{const r=`${t}/ingest/known-issues/match?projectId=${encodeURIComponent(n)}&q=${encodeURIComponent(o)}`,a=await fetch(r,{headers:pe(e,s),signal:i});if(!a.ok)return[];const c=await a.json();return Array.isArray(c==null?void 0:c.issues)?c.issues:[]}catch{return[]}}async function En({apiBase:t,apiKey:e,projectId:n,bugId:o,reporterEmail:s,reporterName:i,metadata:r,logs:a,sessionToken:c}){const l=C(),h=await fetch(`${t}/ingest/known-issues/${encodeURIComponent(o)}/join`,{method:"POST",headers:pe(e,c),body:JSON.stringify({projectId:n,reporterEmail:s,reporterName:i,metadata:r,logs:a,...l?{watchToken:l}:{}})});if(!h.ok)throw new Error(await Tn(h));const u=await h.json();return u!=null&&u.watchToken&&fe(u.watchToken),u}function pe(t,e){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function Tn(t){let e="";try{const n=await t.json();e=Array.isArray(n.message)?n.message.join(", "):n.message||""}catch{}return t.status===429?_(e):e||`Could not add you to this issue (HTTP ${t.status})`}async function An({apiBase:t,bugId:e,score:n,comment:o}){const s=C();if(!s)throw new Error("This browser cannot access that report.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/rating${V()}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({score:n,...o?{comment:o}:{}})}),r=await i.json().catch(()=>({}));if(!i.ok){const a=Array.isArray(r.message)?r.message.join(", "):r.message;throw new Error(a||"Could not send your rating. Please try again.")}return r}async function jn({apiBase:t,bugId:e,...n}){const o=C();if(!o)throw new Error("This browser cannot access that report.");const s=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/verification`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify(n)}),i=await s.json().catch(()=>({}));if(!s.ok)throw new Error(i.message||"Could not record feedback. Please refresh and try again.");return i}const On="bugcatch.session.",ge=t=>`${On}${t}`,me=new Map;function R(t){var e;if(!t)return null;try{const n=(e=window.localStorage)==null?void 0:e.getItem(ge(t));if(n){const o=JSON.parse(n);if(o!=null&&o.token)return o}}catch{}return me.get(t)||null}function Ln(t,e){var o;if(!t||!(e!=null&&e.token))return;const n={token:e.token,email:e.email||null,name:e.name||null};me.set(t,n);try{(o=window.localStorage)==null||o.setItem(ge(t),JSON.stringify(n))}catch{}}function we(t){var e;if(t){me.delete(t);try{(e=window.localStorage)==null||e.removeItem(ge(t))}catch{}}}function Q({apiKey:t,token:e}){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function et(t){let e="";try{const n=await t.json();e=Array.isArray(n.message)?n.message.join(", "):n.message||""}catch{}return t.status===429?_(e):e||`Request failed (HTTP ${t.status})`}async function Rn({apiBase:t,apiKey:e,projectId:n,token:o}){const s={mode:"off",signedIn:null,signedOutReason:null};try{const i=await fetch(`${t}/ingest/login?projectId=${encodeURIComponent(n)}`,{headers:Q({apiKey:e,token:o})});if(!i.ok)return s;const r=await i.json();return{mode:r.mode==="optional"||r.mode==="required"?r.mode:"off",signedIn:r.signedIn||null,signedOutReason:r.signedOutReason||null}}catch{return s}}async function Mn({apiBase:t,apiKey:e,projectId:n,email:o,name:s}){const i=await fetch(`${t}/ingest/login/start`,{method:"POST",headers:Q({apiKey:e}),body:JSON.stringify({projectId:n,email:o,...s?{name:s}:{}})});if(!i.ok)throw new Error(await et(i));return i.json()}async function Kn({apiBase:t,apiKey:e,projectId:n,email:o,code:s}){const i=await fetch(`${t}/ingest/login`,{method:"POST",headers:Q({apiKey:e}),body:JSON.stringify({projectId:n,email:o,code:s})});if(!i.ok)throw new Error(await et(i));const r=await i.json(),a={token:r.sessionToken,email:r.email,name:r.name};return Ln(n,a),a}async function Un({apiBase:t,apiKey:e,projectId:n,token:o}){if(we(n),!o)return{signedOut:!0};try{await fetch(`${t}/ingest/logout`,{method:"POST",headers:Q({apiKey:e,token:o}),body:JSON.stringify({projectId:n})})}catch{}return{signedOut:!0}}let tt=!1;if(typeof window<"u"&&window.addEventListener)for(const t of["pointerdown","keydown","touchstart"])window.addEventListener(t,()=>{tt=!0},{once:!0,capture:!0,passive:!0});function Nn(){return tt}async function Pn({apiBase:t,apiKey:e,projectId:n}){const o={policy:"off",fields:[]};try{const s=await fetch(`${t}/ingest/reference-fields?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!s.ok)return o;const i=await s.json(),r=i.policy==="optional"||i.policy==="required"?i.policy:"off",a=Array.isArray(i.fields)?i.fields:[];return a.length?{policy:r,fields:a}:o}catch{return o}}async function _n({apiBase:t,apiKey:e,projectId:n}){try{const o=await fetch(`${t}/ingest/agent-issues?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!o.ok)return[];const s=await o.json();return(Array.isArray(s==null?void 0:s.issues)?s.issues:[]).filter(i=>i&&typeof i.key=="string"&&typeof i.title=="string").map(i=>({key:i.key,title:i.title,hint:typeof i.hint=="string"?i.hint:""}))}catch{return[]}}const Dn=5*6e4,nt="bugcatch:announcements:seen",Hn=40,zn=["info","success","warning","critical"],ot=t=>`bugcatch:announcements:${t}`;async function Fn({apiBase:t,apiKey:e,projectId:n,fresh:o=!1}){var s;if(!o){const i=Zn(n);if(i)return i}try{const i=await fetch(`${t}/ingest/announcements?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}}),r=i.ok?Vn((s=await i.json())==null?void 0:s.announcements):[];return Gn(n,r),r}catch{return[]}}function Xn(t,{on:e=Jn(),seen:n=rt()}={}){return(t||[]).filter(o=>Yn(o,e)&&!(o.dismissible&&n.includes(it(o))))}function qn(t){if(!(!(t!=null&&t.id)||!t.dismissible))try{const e=it(t),n=[...rt().filter(o=>o!==e),e];localStorage.setItem(nt,JSON.stringify(n.slice(-Hn)))}catch{}}function Wn(t){return typeof t!="string"||!t||t.startsWith("//")?null:t.startsWith("/")||/^(https?:\/\/|mailto:)./i.test(t)?t:null}function Jn(t=new Date){const e=n=>String(n).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}`}function Yn(t,e){return!(t.startsOn&&e<t.startsOn||t.endsOn&&e>t.endsOn)}function it(t){return`${t.id}#${t.revision||0}`}function Vn(t){return(Array.isArray(t)?t:[]).filter(e=>e&&typeof e.id=="string"&&typeof e.title=="string"&&e.title.trim()).map(e=>{const n=e.placement==="launcher"?"launcher":"panel",o=Wn(e.ctaHref);return{id:e.id,revision:Number.isInteger(e.revision)?e.revision:0,title:e.title,body:typeof e.body=="string"?e.body:"",tone:zn.includes(e.tone)?e.tone:"info",ctaLabel:o&&typeof e.ctaLabel=="string"?e.ctaLabel:"",ctaHref:o&&typeof e.ctaLabel=="string"?o:"",placement:n,dismissible:n==="launcher"||e.dismissible!==!1,startsOn:typeof e.startsOn=="string"?e.startsOn:"",endsOn:typeof e.endsOn=="string"?e.endsOn:""}})}function rt(){try{const t=JSON.parse(localStorage.getItem(nt)||"[]");return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}catch{return[]}}function Zn(t){try{const e=JSON.parse(localStorage.getItem(ot(t))||"null");if(!e||!Array.isArray(e.list)||typeof e.at!="number")return null;const n=Date.now()-e.at;return n>=0&&n<Dn?e.list:null}catch{return null}}function Gn(t,e){try{localStorage.setItem(ot(t),JSON.stringify({at:Date.now(),list:e}))}catch{}}const ee=120,Qn=500,te=30,eo=1e3,be={loanId:["loanId","loanNumber"],applicationId:["applicationId"],customerNumber:["customerNumber","customerId"]},to=/[\u0000-\u001f\u007f]/;function no(t){if(typeof t=="number")return Number.isFinite(t)?String(t):null;if(typeof t!="string")return null;const e=t.trim();return!e||e.length>ee||to.test(e)?null:e}function L(t){return t===null?"null":Array.isArray(t)?"an array":typeof t=="object"?"an object":`a ${typeof t}`}function ye(t,e,n,o,s){for(const i of n){if(!(i in t)||t[i]===void 0||t[i]===null||t[i]==="")continue;const r=no(t[i]);if(r)return r;const a=t[i];s(typeof a=="string"&&a.trim().length>ee?`${o}.${i} is longer than ${ee} characters and was dropped — pass the identifier itself, not a sentence or a URL.`:`${o}.${i} should be a string or number, got ${L(a)} — it was dropped.`)}return null}function st(t){if(t===null||typeof t=="boolean")return{ok:!0,value:t};if(typeof t=="number")return Number.isFinite(t)?{ok:!0,value:t}:{ok:!1};if(typeof t=="string")return{ok:!0,value:t.slice(0,Qn)};if(typeof t=="object")try{const e=JSON.stringify(t);if(e&&e.length<=eo)return{ok:!0,value:JSON.parse(e)}}catch{}return{ok:!1}}const at=["email","name","phone"],oo=new Set(["id","userId",...at,...Object.values(be).flat()]);function ct(t,e=()=>{}){if(t==null)return null;if(typeof t!="object"||Array.isArray(t))return e(`identify() takes an object like { id, email, name } or null, got ${L(t)} — ignored.`),null;const n={};try{const o=ye(t,"id",["id","userId"],"identify()",e);o&&(n.id=o);for(const i of at){const r=t[i];if(r==null||r==="")continue;if(i==="phone"&&typeof r=="number"&&Number.isFinite(r)){n.phone=String(r);continue}if(typeof r!="string"){e(`identify().${i} should be a string, got ${L(r)} — it was dropped.`);continue}const a=r.trim().slice(0,i==="name"?200:ee);a&&(i==="email"&&!a.includes("@")&&e(`identify().email "${a}" does not look like an email address — reports cannot be threaded or followed up without one.`),n[i]=a)}for(const[i,r]of Object.entries(be)){const a=ye(t,i,r,"identify()",e);a&&(n[i]=a)}let s=0;for(const[i,r]of Object.entries(t)){if(oo.has(i)||r===void 0||typeof r=="function")continue;if(s>=te){e(`identify() was given more than ${te} extra fields; the rest were dropped.`);break}const a=st(r);if(!a.ok){e(`identify().${i} could not be sent (${L(r)} too large or not serialisable) — dropped.`);continue}n[i]=a.value,s+=1}}catch{}return!n.id&&!n.email&&e("identify() was called without an id or email — reports will not be attributed to anyone, and nothing can be looked up by user."),n}function lt(t,e,n=()=>{}){if(e===null)return{};const o={...t||{}};if(e===void 0)return o;if(typeof e!="object"||Array.isArray(e))return n(`setContext() takes an object like { loanId, screen } or null, got ${L(e)} — ignored.`),o;try{const s=new Map;for(const[i,r]of Object.entries(be))for(const a of r)s.set(a,i);for(const[i,r]of Object.entries(e)){const a=s.get(i);if(r==null){delete o[a||i];continue}if(a){const l=ye({[i]:r},a,[i],"setContext()",n);l?o[a]=l:delete o[a];continue}if(typeof r=="function")continue;if(!(i in o)&&Object.keys(o).length>=te){n(`setContext() holds at most ${te} keys; "${i}" was dropped.`);continue}const c=st(r);if(!c.ok){n(`setContext().${i} could not be sent (${L(r)} too large or not serialisable) — dropped.`);continue}o[i]=c.value}}catch{}return o}const ut=["apiBase","apiKey","projectId","consoleLimit","networkLimit","captureConsole","captureNetwork","captureErrors","widget","widgetPosition","theme","accent","attachments","maxAttachments","maxAttachmentBytes","maxRecordingMs","beforeSend","user","context","updates","updateToast","onUpdate","voice","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError","debug","drafts","knownIssues","agentIssues","release","environment","buildId","chunkBase","announcements"],io=["beforeSend","onUpdate","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError"];function ro(t){const e=t.toLowerCase().replace(/[-_]/g,"");return ut.find(n=>n.toLowerCase()===e)||null}const dt=/^(your[_-]|<|xxx|todo|changeme)/i,so=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function ao(t,{pageHost:e=""}={}){const n=[],o=[];if(!t||typeof t!="object")return n.push("init() takes an options object: BugCatch.init({ apiBase, apiKey, projectId })."),{errors:n,warnings:o};const{apiKey:s,projectId:i,apiBase:r}=t;s?typeof s!="string"?n.push(`apiKey must be a string, got ${L(s)}.`):dt.test(s)?n.push(`apiKey is still the placeholder "${s}" — paste the key you saved when you created it.`):so.test(s)?o.push("apiKey looks like a project id — did you swap apiKey and projectId?"):s.startsWith("bc_")||o.push('apiKey does not look like a BugCatch key (they start with "bc_live_") — reports will be refused with HTTP 401.'):n.push("apiKey is required — create one under Workspace Settings → API Keys."),i?typeof i!="string"?n.push(`projectId must be a string, got ${L(i)}.`):dt.test(i)?n.push(`projectId is still the placeholder "${i}".`):i.startsWith("bc_")?o.push("projectId looks like an API key — did you swap apiKey and projectId?"):/\s/.test(i)&&o.push("projectId contains whitespace — check it was copied cleanly."):n.push("projectId is required — copy it from Project Settings → Web SDK."),r==null?!e||/^(localhost|127\.|0\.0\.0\.0|\[::1\])/.test(e)||o.push("apiBase is not set, so reports go to http://localhost:3000 — set apiBase to your BugCatch API URL."):(typeof r!="string"||!/^https?:\/\//.test(r))&&o.push(`apiBase should be an absolute http(s) URL, got ${JSON.stringify(r)}.`);for(const a of Object.keys(t)){if(ut.includes(a))continue;const c=ro(a);o.push(c?`unknown option "${a}" — did you mean "${c}"?`:`unknown option "${a}" is ignored.`)}for(const a of io){const c=t[a];c!=null&&typeof c!="function"&&o.push(`${a} should be a function, got ${L(c)} — it will not be called.`)}return{errors:n,warnings:o}}function co(t){return typeof t=="string"?t.replace(/\/+$/,""):t}const X={name:"@sphoro/bugcatch-web-sdk",version:"0.19.0"},q={apiBase:"http://localhost:3000",apiKey:null,projectId:null,consoleLimit:100,networkLimit:50,captureConsole:!0,captureNetwork:!0,captureErrors:!0,widget:!0,widgetPosition:"bottom-right",theme:"auto",accent:null,announcements:!0,attachments:!0,maxAttachments:5,maxAttachmentBytes:25*1024*1024,maxRecordingMs:12e4,beforeSend:null,user:null,updates:!0,updateToast:!0,onUpdate:null,voice:!0,onCallState:null,onIncomingCall:null,onReportSubmitted:null,onAssistantPhase:null,onError:null,context:null,chunkBase:null,debug:!1};function lo(t){const e=C();return e?{...t,watchToken:e}:t}class ht{constructor(){gt(this,d);this.config={...q},this.started=!1,this.teardown=[],this.consoleBuffer=W(q.consoleLimit),this.networkBuffer=W(q.networkLimit),this.stopWatching=null,this.notifier=null,this.call=null,this.callStarting=!1,this.ringing=null,this.listeners=new Map,this.context={},this.warned=new Set,this.phases=new Map}warn(e){this.warned.has(e)||(this.warned.add(e),console.warn(`[BugCatch] ${e}`))}debugLog(...e){this.config.debug&&console.info("[BugCatch]",...e)}init(e={}){if(this.started)return console.warn("[BugCatch] init() called twice — ignoring the second call."),this;const{errors:n,warnings:o}=ao(e,{pageHost:typeof location<"u"?location.hostname:""});if(n.length)throw new Error(`[BugCatch] ${n[0]}`);for(const i of o)this.warn(i);const s=this.config.user;return this.config={...q,...e,apiBase:co(e.apiBase??q.apiBase)},this.config.user=e.user!==void 0?ct(e.user,i=>this.warn(i.replace("identify()","init({ user })"))):s||null,e.context&&this.setContext(e.context),this.consoleBuffer=W(this.config.consoleLimit),this.networkBuffer=W(this.config.networkLimit),this.config.captureConsole&&this.teardown.push(Tt(this.consoleBuffer)),this.config.captureNetwork&&this.teardown.push(At(this.networkBuffer)),this.config.captureErrors&&this.teardown.push(jt(this.consoleBuffer)),rn(this.config.projectId),this.config.widget&&this.teardown.push(Jt(this)),this.config.updates&&C()&&(this.startWatching(),queueMicrotask(()=>m(this,d,K).call(this))),this.started=!0,m(this,d,mt).call(this),this.debugLog("initialised",{version:X.version,apiBase:this.config.apiBase,projectId:this.config.projectId,user:this.config.user,context:this.context}),this}startWatching(){if(this.stopWatching)return this;this.stopWatching=yn({apiBase:this.config.apiBase,onUpdate:n=>{if(n!=null&&n.incomingCall){m(this,d,xt).call(this,n);return}if(n!=null&&n.callWithdrawn){m(this,d,re).call(this,n.callWithdrawn.callId);return}const o=an(n);typeof this.config.onUpdate=="function"&&this.config.onUpdate(o),this.emit("update",o),m(this,d,K).call(this)}}),this.teardown.push(()=>{var n;return(n=this.stopWatching)==null?void 0:n.call(this)}),this.teardown.push(()=>m(this,d,re).call(this));const e=n=>{(n.key===Z||n.key===null)&&m(this,d,K).call(this)};return window.addEventListener("storage",e),this.teardown.push(()=>window.removeEventListener("storage",e)),this}on(e,n){if(typeof n!="function")return()=>{};const o=this.listeners.get(e)||new Set;return o.add(n),this.listeners.set(e,o),()=>this.off(e,n)}off(e,n){var o;return(o=this.listeners.get(e))==null||o.delete(n),this}emit(e,n){for(const o of this.listeners.get(e)||[])try{o(n)}catch(s){console.error(`[BugCatch] a '${e}' listener threw:`,s)}}unreadUpdates(){return F()}markUpdateRead(e){return cn(e),m(this,d,K).call(this),this}markReportRead(e){return ln(e),m(this,d,K).call(this),this}markUpdatesRead(){return Ve(),m(this,d,K).call(this),this}async verifyFix(e,{result:n,resolvedAt:o,note:s}){const i=n==="STILL_HAPPENING"?await m(this,d,ie).call(this,{}):{};return jn({apiBase:this.config.apiBase,bugId:e,result:n,resolvedAt:o,note:s,metadata:i.metadata,logs:i.logs})}async rate(e,{score:n,comment:o}={}){return An({apiBase:this.config.apiBase,bugId:e,score:n,comment:o})}async myReport(e){const n=await hn({apiBase:this.config.apiBase,bugId:e});return m(this,d,wt).call(this,e,n==null?void 0:n.assistant),n}replyToReport(e,{body:n,attachments:o}={}){return fn({apiBase:this.config.apiBase,bugId:e,body:n,attachments:o})}async canCallNow(){return!!await this.callKind()}async callKind(){return(await this.callKinds()).live?"live":null}async callKinds(){var n;const e={agent:!1,live:!1};return!this.config.voice||!((n=this.config.user)!=null&&n.email)?e:this.kinds!==void 0?this.kinds:(this.kinds=await pn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.kinds)}canCall(e){return this.config.voice?Ze({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}async startCall(e){var n;if(!this.config.voice)throw new Error("[BugCatch] calling is switched off.");if(this.call||this.callStarting)throw new Error("[BugCatch] a call is already running.");this.callStarting=!0;try{if(!await this.callKind())throw new Error((n=this.config.user)!=null&&n.email?"[BugCatch] calling is not available on this project.":"[BugCatch] a call needs to know who is calling — call identify() with an email first.");return await m(this,d,yt).call(this,e)}finally{this.callStarting=!1}}startLiveCall(e){return this.startCall(e)}canCallTeam(e){return this.config.voice?Ze({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}endCall(){var e;return(e=this.call)==null||e.hangUp(),this.call=null,this}myReports(){return dn({apiBase:this.config.apiBase})}hasReports(){return!!C()}forgetReports(){var e;return(e=this.stopWatching)==null||e.call(this),this.stopWatching=null,this.endCall(),N(),m(this,d,K).call(this),this}identify(e){const n=ct(e,o=>this.warn(o));return e!=null&&!n?this:(this.config.user=n,this.debugLog("identify",n),this)}setContext(e){return this.context=lt(this.context,e,n=>this.warn(n)),this.debugLog("context",this.context),this}getContext(){return{...this.context}}open(){return this.started?this.widgetControls?(this.widgetControls.open(),this):(this.warn("open() needs the built-in widget; it was turned off with widget: false."),this):(this.warn("open() was called before init() — nothing to open yet."),this)}close(){var e;return(e=this.widgetControls)==null||e.close(),this}isOpen(){var e;return!!((e=this.widgetControls)!=null&&e.isOpen())}async uploadAttachment(e,n,o){if(!this.started)throw new Error("[BugCatch] call init() first.");return tn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,blob:e,mime:n||(e==null?void 0:e.type),onProgress:o})}async report({title:e,description:n="",priority:o="MEDIUM",metadata:s={},attachments:i=[],references:r=[],context:a=null,issue:c=null}={}){var j,A,D,f,k,p,b,I;if(!this.started)throw new Error("[BugCatch] call init() first.");if(typeof e!="string"||!e.trim())throw new Error("[BugCatch] a title is required.");["LOW","MEDIUM","HIGH","CRITICAL"].includes(String(o).toUpperCase())||(this.warn(`priority "${o}" is not one of LOW, MEDIUM, HIGH, CRITICAL — sent as MEDIUM.`),o="MEDIUM");const l=(i||[]).filter(g=>(g==null?void 0:g.storageKey)&&de(g.mime,Je)).map(g=>({type:g.type==="VIDEO"?"VIDEO":"SCREENSHOT",storageKey:g.storageKey,mime:de(g.mime,Je)})),h=(Array.isArray(r)?r:[]).filter(g=>g&&typeof g.key=="string"&&g.key&&(typeof g.value=="string"||typeof g.value=="number")).map(g=>({key:g.key.slice(0,64),value:String(g.value).trim().slice(0,120)})).filter(g=>g.value);Array.isArray(r)&&h.length<r.length&&this.warn("report({ references }) takes [{ key, value }] with string values; malformed entries were dropped.");let u=await m(this,d,ie).call(this,{title:e.trim(),description:typeof n=="string"?n:String(n??""),priority:String(o).toUpperCase(),metadata:s,media:l,references:h,context:a,issue:typeof c=="string"&&c?c.slice(0,32):null});const B=m(this,d,ve).call(this,u);if(B===!1)return{cancelled:!0};u=B,this.debugLog("sending report",{title:u.title,user:(j=u.metadata)==null?void 0:j.user,context:(A=u.metadata)==null?void 0:A.context,references:u.references,console:(k=(f=(D=u.logs)==null?void 0:D.console)==null?void 0:f.entries)==null?void 0:k.length,network:(I=(b=(p=u.logs)==null?void 0:p.network)==null?void 0:b.entries)==null?void 0:I.length});const y=R(this.config.projectId);let w;try{w=await fetch(`${this.config.apiBase}/ingest/bugs`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey,...y!=null&&y.token?{"X-BugCatch-Session":y.token}:{}},body:JSON.stringify(lo(u))})}catch(g){const S=new Error(`[BugCatch] could not reach ${this.config.apiBase} — check apiBase, and that this site's origin is allowed by the API.`);throw S.cause=g,m(this,d,oe).call(this,"report",S),S}if(w.status===401&&y&&we(this.config.projectId),!w.ok){let g="";try{const O=await w.json();g=Array.isArray(O.message)?O.message.join(", "):O.message||""}catch{}w.status===401&&!y&&(g||(g="the apiKey was not accepted")),w.status===403&&(g||(g="the apiKey is not allowed to report to this projectId"));const S=w.status===429?new Error(`[BugCatch] ${_(g)}`):new Error(`[BugCatch] report failed (HTTP ${w.status})${g?": "+g:""}`);throw S.status=w.status,m(this,d,oe).call(this,"report",S),S}const M=await w.json();return m(this,d,ke).call(this,M,{title:u.title??e.trim(),via:"form"}),M.watchToken&&(fe(M.watchToken),this.config.updates&&this.startWatching()),M}loginState(){var e;return Rn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:(e=R(this.config.projectId))==null?void 0:e.token})}referenceFields(){return this.refFields?this.refFields:(this.refFields=Pn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.refFields)}agentIssues(){return this.agentIssueList?this.agentIssueList:(this.agentIssueList=_n({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.agentIssueList)}async announcements(){const e=await Fn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId});return Xn(e)}dismissAnnouncement(e){qn(e)}signedInAs(){const e=R(this.config.projectId);return e?{email:e.email,name:e.name}:null}signIn(e,{name:n}={}){var o;return Mn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,name:n??((o=this.config.user)==null?void 0:o.name)??null})}async confirmSignIn(e,n){var s;const o=await Kn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,code:n});return(s=this.config.user)!=null&&s.email||(this.config.user={email:o.email,name:o.name||void 0}),this.emit("signin",{email:o.email,name:o.name}),{email:o.email,name:o.name}}async signOut(){var o;const e=R(this.config.projectId),n=await Un({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:e==null?void 0:e.token});return e!=null&&e.email&&((o=this.config.user)==null?void 0:o.email)===e.email&&(this.config.user=null),this.emit("signout",{email:(e==null?void 0:e.email)||null}),n}knownIssues(){var e;return $n({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,sessionToken:(e=R(this.config.projectId))==null?void 0:e.token})}matchKnownIssues(e,{signal:n}={}){var o;return Bn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,q:e||"",sessionToken:(o=R(this.config.projectId))==null?void 0:o.token,signal:n})}async meToo(e,{email:n,name:o}={}){var r,a;const s=R(this.config.projectId),i=await Ee();return En({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,bugId:e,reporterEmail:n||((r=this.config.user)==null?void 0:r.email),reporterName:o||((a=this.config.user)==null?void 0:a.name),metadata:{...i,user:this.config.user||void 0,...Object.keys(this.context).length?{context:this.context}:{},sdk:X},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},sessionToken:s==null?void 0:s.token})}snapshot(){return{user:this.config.user||null,context:{...this.context},console:this.consoleBuffer.all(),network:this.networkBuffer.all(),environment:Te()}}clear(){this.consoleBuffer.clear(),this.networkBuffer.clear()}destroy(){this.endCall(),this.teardown.forEach(e=>{try{e()}catch{}}),this.teardown=[],this.notifier=null,this.started=!1}}d=new WeakSet,ne=function(e,n){const o=this.config[e];if(typeof o=="function")try{o(n)}catch(s){console.error(`[BugCatch] your ${e} callback threw:`,s)}},mt=function(){if(typeof window>"u"||typeof fetch!="function")return;const e=`bugcatch:hello:${this.config.projectId}`,n=new Date().toISOString().slice(0,10);try{if(window.localStorage.getItem(e)===n)return}catch{}const o=setTimeout(()=>{try{window.localStorage.setItem(e,n)}catch{}fetch(`${this.config.apiBase}/ingest/hello`,{method:"POST",keepalive:!0,headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey},body:JSON.stringify({projectId:this.config.projectId,version:X.version,identify:!!this.config.user,context:!!(this.context&&Object.keys(this.context).length),widget:!!this.config.widget,environment:this.config.environment||null,release:this.config.release||null})}).catch(()=>{})},5e3);this.teardown.push(()=>clearTimeout(o))},K=function(){const e=F();if(this.emit("unread",{count:e.length,updates:e}),!!this.config.updateToast){if(!this.notifier){if(!e.length)return;this.notifier=Sn({accent:this.config.accent,position:this.config.widgetPosition,onRead:n=>this.markUpdateRead(n),onReadAll:()=>this.markUpdatesRead(),onCall:this.config.voice?n=>{this.markUpdateRead(n.id),this.startCall(n.bugId)}:null}),this.teardown.push(()=>{var n;return(n=this.notifier)==null?void 0:n.destroy()})}this.notifier.render(e)}},wt=function(e,n){const o=n==null?void 0:n.phase;if(!e||typeof o!="string"||this.phases.get(e)===o)return;this.phases.set(e,o);const s={bugId:e,phase:o,title:n.title??null,detail:n.detail??null};this.debugLog("assistant",s),m(this,d,ne).call(this,"onAssistantPhase",s),this.emit("assistant",s)},ke=function(e,{title:n,via:o}){if(!(e!=null&&e.id))return;const s={bugId:e.id,title:n??null,via:o,result:e};this.debugLog("report accepted",s),m(this,d,ne).call(this,"onReportSubmitted",s),this.emit("report",s)},oe=function(e,n){m(this,d,ne).call(this,"onError",{action:e,error:n}),this.emit("error",{action:e,error:n})},ie=async function({title:e,description:n="",priority:o="MEDIUM",metadata:s={},media:i=[],references:r=[],context:a=null,issue:c=null}){var h,u;const l=a?lt(this.context,a,B=>this.warn(B.replace("setContext()","report({ context })"))):this.context;return{projectId:this.config.projectId,...e?{title:e}:{},description:n,priority:o,url:window.location.href,reporterEmail:(h=this.config.user)==null?void 0:h.email,reporterName:(u=this.config.user)==null?void 0:u.name,metadata:{...await Ee(),release:this.config.release,environment:this.config.environment,buildId:this.config.buildId,...s,user:this.config.user||void 0,...Object.keys(l).length?{context:l}:{},sdk:X},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},...i.length?{attachments:i}:{},...r.length?{references:r}:{},...c?{issue:c}:{}}},ve=function(e){if(typeof this.config.beforeSend!="function")return e;let n;try{n=this.config.beforeSend(e)}catch(o){return this.warn(`beforeSend threw, so nothing was sent: ${(o==null?void 0:o.message)||o}`),!1}return n===!1?!1:n&&typeof n=="object"?n:e},bt=async function(){const e=R(this.config.projectId),n=m(this,d,ve).call(this,await m(this,d,ie).call(this,{}));if(n===!1)return this.debugLog("call cancelled by beforeSend"),{cancelled:!0};let o;try{o=await gn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,token:e==null?void 0:e.token,payload:n,watchToken:C()})}catch(s){throw s.status===401&&e&&we(this.config.projectId),m(this,d,oe).call(this,"call",s),s}return m(this,d,ke).call(this,o,{title:null,via:"call"}),o.watchToken&&(fe(o.watchToken),this.config.updates&&this.startWatching()),o},yt=async function(e){const{joinLiveCall:n,liveCallSupported:o,openMicrophone:s}=await J(this.config.chunkBase);if(!o())throw new Error("[BugCatch] this browser cannot make live calls.");const i=await s();let r,a=null;try{if(e)r=await mn({apiBase:this.config.apiBase,bugId:e});else if(a=await m(this,d,bt).call(this),e=a.id,r=a.live_call,!(r!=null&&r.realtime_url)){const l=new Error(`[BugCatch] the report was filed, but the team could not be rung${a.call_error?`: ${a.call_error}`:"."}`);throw l.filed=a,l.reason=a.call_error||null,l}}catch(l){for(const h of i.getTracks())h.stop();throw l}const c=await n({realtimeUrl:r.realtime_url,stream:i,ringsUntil:r.rings_until,onState:(l,h)=>{(l==="ended"||l==="failed"||l==="missed")&&(this.call=null),l==="missed"&&wn({apiBase:this.config.apiBase,bugId:e,callId:r.call_id}),typeof this.config.onCallState=="function"&&this.config.onCallState(l,{bugId:e,live:!0,...h}),this.emit("call",{bugId:e,live:!0,state:l,...h})}});return this.call=c,Object.assign(c,{bugId:e,callId:r.call_id,live:!0,filed:a,recorded:!!r.recorded})},xt=function(e){var s;const n=e.incomingCall;if(!this.config.voice||((s=this.ringing)==null?void 0:s.callId)===n.callId||this.call||this.callStarting)return;m(this,d,re).call(this);const o={callId:n.callId,dismiss:()=>{}};this.ringing=o,J(this.config.chunkBase).then(({liveCallSupported:i,showIncomingCall:r})=>{if(this.ringing!==o||(this.ringing=null,!i())||this.call||this.callStarting)return;const a=r({call:n,accent:this.config.accent,position:this.config.widgetPosition,touched:Nn,onAnswer:()=>{m(this,d,kt).call(this,e.bugId,n)},onDecline:()=>{this.ringing=null,Ge({apiBase:this.config.apiBase,bugId:e.bugId,callId:n.callId}),this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"declined"})},onExpire:()=>{this.ringing=null,this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"missed"})}});if(this.ringing={callId:n.callId,dismiss:a},typeof this.config.onIncomingCall=="function")try{this.config.onIncomingCall({bugId:e.bugId,...n})}catch{}this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"ringing"})},()=>{this.ringing===o&&(this.ringing=null)})},re=function(e){if(this.ringing&&!(e&&this.ringing.callId!==e)){try{this.ringing.dismiss()}catch{}this.ringing=null}},kt=async function(e,n){if(this.ringing=null,this.call||this.callStarting)return;this.callStarting=!0;let o,s;try{const r=await J(this.config.chunkBase);s=r.joinLiveCall,o=await r.openMicrophone()}catch(r){this.callStarting=!1,Ge({apiBase:this.config.apiBase,bugId:e,callId:n.callId}),this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r});return}let i;try{i=await bn({apiBase:this.config.apiBase,bugId:e,callId:n.callId})}catch(r){for(const a of o.getTracks())a.stop();this.callStarting=!1,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"missed",error:r});return}try{const r=await s({realtimeUrl:i.realtime_url,stream:o,onState:(a,c)=>{(a==="ended"||a==="failed"||a==="missed")&&(this.call=null),typeof this.config.onCallState=="function"&&this.config.onCallState(a,{bugId:e,live:!0,incoming:!0,...c}),this.emit("call",{bugId:e,live:!0,incoming:!0,state:a,...c})}});this.call=r,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"answered"})}catch(r){for(const a of o.getTracks())a.stop();this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r})}finally{this.callStarting=!1}};const xe=new ht;return xe.Client=ht,xe.version=X.version,xe});
1433
310
  //# sourceMappingURL=bugcatch.umd.js.map