@sphoro/bugcatch-web-sdk 0.14.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +390 -115
- package/dist/bugcatch.es.js +2798 -1797
- package/dist/bugcatch.es.js.map +1 -1
- package/dist/bugcatch.umd.js +804 -450
- package/dist/bugcatch.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/identity.js +277 -0
- package/src/index.d.ts +145 -7
- package/src/index.js +326 -36
- package/src/references.js +155 -3
- package/src/updates.js +36 -6
- package/src/widget.js +1170 -456
package/dist/bugcatch.umd.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
(function(N,te){typeof exports=="object"&&typeof module<"u"?module.exports=te():typeof define=="function"&&define.amd?define(te):(N=typeof globalThis<"u"?globalThis:N||self,N.BugCatch=te())})(this,function(){"use strict";var jn=N=>{throw TypeError(N)};var Io=(N,te,we)=>te.has(N)||jn("Cannot "+we);var zn=(N,te,we)=>te.has(N)?jn("Cannot add the same private member more than once"):te instanceof WeakSet?te.add(N):te.set(N,we);var _=(N,te,we)=>(Io(N,te,"access private method"),we);var B,ye,lt,On,Hn,qn,dt,Dn;const N="[REDACTED]",te=new Set(["auth","bearer","session","sid","phpsessid","otp","pass","passwd","pwd","sig","codeverifier"]),we=["token","secret","password","passwd","apikey","signature","credential","sessionid","privatekey","accesskey","authorization"],Nn=new Set(["key","code","state","nonce","salt","hash"]),Rt=[/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],Un=/\bBearer\s+[\w.~+/=-]{8,}/gi,Vn=/^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#@]+)@/;function Qt(t){try{return decodeURIComponent(t.replace(/\+/g," "))}catch{return t}}function Kn(t){return Qt(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function Pn(t){return t.length>=20?!0:Rt.some(e=>(e.lastIndex=0,e.test(t)))}function _n(t,e){const n=Kn(t);if(!n)return!1;if(te.has(n))return!0;for(const a of we)if(n.includes(a))return!0;return Nn.has(n)?Pn(Qt(e)):!1}function ut(t){return t.split("&").map(e=>{const n=e.indexOf("=");if(n===-1)return e;const a=e.slice(0,n),i=e.slice(n+1);return i&&_n(a,i)?`${a}=${N}`:e}).join("&")}function jt(t){let e=t;for(const n of Rt)n.lastIndex=0,e=e.replace(n,N);return e}function zt(t){if(t==null)return t;let e=String(t);if(!e)return e;e=e.replace(Vn,`$1${N}@`);const n=e.indexOf("#");let a=n===-1?e:e.slice(0,n),i=n===-1?"":e.slice(n);const r=a.indexOf("?");if(r!==-1&&(a=a.slice(0,r+1)+ut(a.slice(r+1))),i.length>1){const s=i.slice(1),l=s.indexOf("?");l!==-1?i=`#${s.slice(0,l+1)}${ut(s.slice(l+1))}`:s.includes("=")&&(i=`#${ut(s)}`)}return jt(a+i)}function Fn(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=>zt(n)),e=e.replace(Un,`Bearer ${N}`),jt(e))}function Pe(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 Ot=["log","info","warn","error","debug"];function Wn(t,{levels:e=["warn","error","log","info"]}={}){const n={};for(const a of Ot)n[a]=console[a],e.includes(a)&&(console[a]=(...i)=>{try{t.push({level:a,text:i.map(Dt).join(" ").slice(0,2e3),at:new Date().toISOString()})}catch{}n[a].apply(console,i)});return()=>{for(const a of Ot)console[a]=n[a]}}function Yn(t,{slowMs:e=2e3}={}){const n=window.fetch,a=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send;return n&&(window.fetch=async function(...s){var y,h,b;const l=performance.now(),p=typeof s[0]=="string"?s[0]:(y=s[0])==null?void 0:y.url,f=((h=s[1])==null?void 0:h.method)||(typeof s[0]=="object"?(b=s[0])==null?void 0:b.method:"GET")||"GET";try{const g=await n.apply(this,s);return pt(t,{url:p,method:f,status:g.status,ms:Math.round(performance.now()-l),slowMs:e}),g}catch(g){throw pt(t,{url:p,method:f,status:0,error:String((g==null?void 0:g.message)||g),ms:Math.round(performance.now()-l),slowMs:e}),g}}),XMLHttpRequest.prototype.open=function(s,l,...p){return this.__bc={method:s,url:l,started:0},a.call(this,s,l,...p)},XMLHttpRequest.prototype.send=function(...s){return this.__bc&&(this.__bc.started=performance.now(),this.addEventListener("loadend",()=>{pt(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=a,XMLHttpRequest.prototype.send=i}}function pt(t,{url:e,method:n,status:a,ms:i,error:r,slowMs:s}){try{t.push({url:zt(String(e)).slice(0,500),method:n,status:a,ms:i,error:r===void 0?void 0:Fn(r),failed:a===0||a>=400,slow:i>=s,at:new Date().toISOString()})}catch{}}function Xn(t){const e=a=>{const i=String(a.message||"Error"),r=a.filename?` (${a.filename}:${a.lineno}:${a.colno})`:"";t.push({level:"error",text:(/^uncaught/i.test(i)?i:`Uncaught ${i}`)+r,at:new Date().toISOString()})},n=a=>{t.push({level:"error",text:`Unhandled rejection: ${Dt(a.reason)}`,at:new Date().toISOString()})};return window.addEventListener("error",e),window.addEventListener("unhandledrejection",n),()=>{window.removeEventListener("error",e),window.removeEventListener("unhandledrejection",n)}}async function Ht(){const t=qt();try{if(navigator.getBattery){const e=await navigator.getBattery();t.battery=Math.round(e.level*100),t.charging=e.charging}}catch{}return t}function qt(){var e,n,a,i,r,s,l,p,f;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:(a=window.screen)==null?void 0:a.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:(p=window.matchMedia)!=null&&p.call(window,"(prefers-color-scheme: dark)").matches?"dark":"light",prefersReducedMotion:((f=window.matchMedia)==null?void 0:f.call(window,"(prefers-reduced-motion: reduce)").matches)||!1}}function Dt(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 Zn=typeof window<"u"&&window.fetch?window.fetch.bind(window):null,Jn=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.open:null,Gn=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.send:null,Nt=["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"],_e=["image/png","image/jpeg","image/webp","video/webm","video/mp4"],ea=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getUserMedia&&typeof MediaRecorder<"u")},ta=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getDisplayMedia&&typeof MediaRecorder<"u")},na=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getDisplayMedia)};function Fe(t,e=Nt){const n=String(t||"").split(";")[0].trim().toLowerCase();return e.includes(n)?n:null}async function aa(){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:a,height:i}=e.getSettings(),r=document.createElement("canvas");r.width=a||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 oa({maxMs:t=12e4,onTick:e,onAutoStop:n}={}){const a=await navigator.mediaDevices.getDisplayMedia({video:{frameRate:15},audio:!1}),i=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"].find(g=>MediaRecorder.isTypeSupported(g))||"video/webm",r=new MediaRecorder(a,{mimeType:i,videoBitsPerSecond:25e5}),s=[];r.ondataavailable=g=>{g.data.size&&s.push(g.data)},r.start(1e3);const l=Date.now(),p=setInterval(()=>e==null?void 0:e(Math.floor((Date.now()-l)/1e3)),500),f=()=>{clearInterval(p),clearTimeout(b),a.getTracks().forEach(g=>g.stop())},y=new Promise(g=>{r.onstop=()=>{f(),g(new Blob(s,{type:i}))}}),h=()=>{r.state!=="inactive"&&r.stop(),n==null||n()};a.getVideoTracks()[0].addEventListener("ended",h);const b=setTimeout(h,t);return{mime:i,maxMs:t,async stop(){return clearTimeout(b),r.state!=="inactive"&&r.stop(),{blob:await y,mime:i,kind:"VIDEO"}},cancel(){f(),r.state!=="inactive"&&r.stop()}}}async function ra({maxMs:t=12e4,onTick:e,onAutoStop:n}={}){const a=await navigator.mediaDevices.getUserMedia({audio:!0}),i=["audio/webm;codecs=opus","audio/webm","audio/mp4","audio/ogg"].find(g=>MediaRecorder.isTypeSupported(g))||"",r=new MediaRecorder(a,i?{mimeType:i}:void 0),s=[];r.ondataavailable=g=>{g.data.size&&s.push(g.data)},r.start(1e3);const l=Date.now(),p=setInterval(()=>e==null?void 0:e(Math.floor((Date.now()-l)/1e3)),500),f=()=>{clearInterval(p),clearTimeout(b),a.getTracks().forEach(g=>g.stop())},y=new Promise(g=>{r.onstop=()=>{f(),g(new Blob(s,{type:r.mimeType||i}))}}),b=setTimeout(()=>{r.state!=="inactive"&&r.stop(),n==null||n()},t);return{mime:r.mimeType||i,maxMs:t,async stop(){clearTimeout(b),r.state!=="inactive"&&r.stop();const g=await y;return{blob:g,mime:g.type||r.mimeType||i,kind:"AUDIO"}},cancel(){f(),r.state!=="inactive"&&r.stop()}}}async function ia({apiBase:t,apiKey:e,blob:n,mime:a,onProgress:i}){const r=Fe(a||n.type);if(!r)throw new Error(`[BugCatch] ${a||n.type||"that file"} can't be attached.`);const s=await Zn(`${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:p}=await s.json();return await sa(p,n,r,i),{storageKey:l,mime:r}}function sa(t,e,n,a){return new Promise((i,r)=>{const s=new XMLHttpRequest;Jn.call(s,"PUT",t,!0),s.setRequestHeader("Content-Type",n),s.upload.onprogress=l=>{l.lengthComputable&&(a==null||a(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.")),Gn.call(s,e)})}function Me(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(0)} KB`:`${(t/1024/1024).toFixed(1)} MB`}function We(t){const e=Math.floor(t/60),n=t%60;return`${e}:${String(n).padStart(2,"0")}`}const ca=7*24*60*60*1e3;let Ye;function la(){return Ye||(Ye=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 Ye=null,t})),Ye}function da(t,e){var n,a;return JSON.stringify([t.apiBase,t.projectId,e||((n=t.user)==null?void 0:n.email)||((a=t.user)==null?void 0:a.id)||"anonymous"])}async function Xe(t,e){const n=await la();return new Promise((a,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=()=>a(l.result),r.onerror=()=>i(r.error),r.onabort=()=>i(r.error||new Error("Draft was not saved."))})}async function ua(t){const e=await Xe(t);return e&&Date.now()-e.savedAt>ca?(await Xe(t,null),null):e||null}const pa="bugcatch.session.",ht=t=>`${pa}${t}`,ft=new Map;function fe(t){var e;if(!t)return null;try{const n=(e=window.localStorage)==null?void 0:e.getItem(ht(t));if(n){const a=JSON.parse(n);if(a!=null&&a.token)return a}}catch{}return ft.get(t)||null}function ha(t,e){var a;if(!t||!(e!=null&&e.token))return;const n={token:e.token,email:e.email||null,name:e.name||null};ft.set(t,n);try{(a=window.localStorage)==null||a.setItem(ht(t),JSON.stringify(n))}catch{}}function gt(t){var e;if(t){ft.delete(t);try{(e=window.localStorage)==null||e.removeItem(ht(t))}catch{}}}function Ze({apiKey:t,token:e}){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function Ut(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 fa({apiBase:t,apiKey:e,projectId:n,token:a}){const i={mode:"off",signedIn:null,signedOutReason:null};try{const r=await fetch(`${t}/ingest/login?projectId=${encodeURIComponent(n)}`,{headers:Ze({apiKey:e,token:a})});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 ga({apiBase:t,apiKey:e,projectId:n,email:a,name:i}){const r=await fetch(`${t}/ingest/login/start`,{method:"POST",headers:Ze({apiKey:e}),body:JSON.stringify({projectId:n,email:a,...i?{name:i}:{}})});if(!r.ok)throw new Error(await Ut(r));return r.json()}async function ma({apiBase:t,apiKey:e,projectId:n,email:a,code:i}){const r=await fetch(`${t}/ingest/login`,{method:"POST",headers:Ze({apiKey:e}),body:JSON.stringify({projectId:n,email:a,code:i})});if(!r.ok)throw new Error(await Ut(r));const s=await r.json(),l={token:s.sessionToken,email:s.email,name:s.name};return ha(n,l),l}async function ba({apiBase:t,apiKey:e,projectId:n,token:a}){if(gt(n),!a)return{signedOut:!0};try{await fetch(`${t}/ingest/logout`,{method:"POST",headers:Ze({apiKey:e,token:a}),body:JSON.stringify({projectId:n})})}catch{}return{signedOut:!0}}const mt="bugcatch.watch",Vt=[2e3,5e3,15e3,3e4,6e4],ya=5e3;function re(){var t;try{return((t=window.localStorage)==null?void 0:t.getItem(mt))||null}catch{return null}}function bt(t){var e;if(t)try{(e=window.localStorage)==null||e.setItem(mt,t)}catch{}}function ve(){var t;try{(t=window.localStorage)==null||t.removeItem(mt)}catch{}Kt()}const Je="bugcatch.unread";function wa(t){return[(t==null?void 0:t.bugId)||"report",(t==null?void 0:t.kind)||"status",(t==null?void 0:t.at)||""].join("|")}function qe(){var t;try{const e=(t=window.localStorage)==null?void 0:t.getItem(Je),n=e?JSON.parse(e):[];return Array.isArray(n)?n.filter(a=>a&&typeof a.id=="string"):[]}catch{return[]}}function Ge(t){var e,n;try{t.length?(e=window.localStorage)==null||e.setItem(Je,JSON.stringify(t)):(n=window.localStorage)==null||n.removeItem(Je)}catch{}}function va(t){const e={...t,id:wa(t)},n=qe().filter(a=>a.id!==e.id);return n.push(e),Ge(n.slice(-20)),e}function xa(t){const e=qe().filter(n=>n.id!==t);return Ge(e),e}function ka(t){const e=qe().filter(n=>n.bugId!==t);return Ge(e),e}function Kt(){return Ge([]),[]}function Sa(t,e){let n=t,a=n.indexOf(`
|
|
1
|
+
(function(Z,pe){typeof exports=="object"&&typeof module<"u"?module.exports=pe():typeof define=="function"&&define.amd?define(pe):(Z=typeof globalThis<"u"?globalThis:Z||self,Z.BugCatch=pe())})(this,function(){"use strict";var $o=Z=>{throw TypeError(Z)};var Or=(Z,pe,Le)=>pe.has(Z)||$o("Cannot "+Le);var Co=(Z,pe,Le)=>pe.has(Z)?$o("Cannot add the same private member more than once"):pe instanceof WeakSet?pe.add(Z):pe.set(Z,Le);var N=(Z,pe,Le)=>(Or(Z,pe,"access private method"),Le);var L,Ct,To,Me,Eo,rn,Tt,Et,sn,Io,Mo,Lo,It,Ao;const Z="[REDACTED]",pe=new Set(["auth","bearer","session","sid","phpsessid","otp","pass","passwd","pwd","sig","codeverifier"]),Le=["token","secret","password","passwd","apikey","signature","credential","sessionid","privatekey","accesskey","authorization"],Bo=new Set(["key","code","state","nonce","salt","hash"]),cn=[/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],jo=/\bBearer\s+[\w.~+/=-]{8,}/gi,Ro=/^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#@]+)@/;function ln(t){try{return decodeURIComponent(t.replace(/\+/g," "))}catch{return t}}function zo(t){return ln(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function Oo(t){return t.length>=20?!0:cn.some(e=>(e.lastIndex=0,e.test(t)))}function Qo(t,e){const n=zo(t);if(!n)return!1;if(pe.has(n))return!0;for(const o of Le)if(n.includes(o))return!0;return Bo.has(n)?Oo(ln(e)):!1}function Mt(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&&Qo(o,i)?`${o}=${Z}`:e}).join("&")}function dn(t){let e=t;for(const n of cn)n.lastIndex=0,e=e.replace(n,Z);return e}function un(t){if(t==null)return t;let e=String(t);if(!e)return e;e=e.replace(Ro,`$1${Z}@`);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)+Mt(o.slice(r+1))),i.length>1){const s=i.slice(1),l=s.indexOf("?");l!==-1?i=`#${s.slice(0,l+1)}${Mt(s.slice(l+1))}`:s.includes("=")&&(i=`#${Mt(s)}`)}return dn(o+i)}function Ho(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=>un(n)),e=e.replace(jo,`Bearer ${Z}`),dn(e))}function st(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 pn=["log","info","warn","error","debug"];function qo(t,{levels:e=["warn","error","log","info"]}={}){const n={};for(const o of pn)n[o]=console[o],e.includes(o)&&(console[o]=(...i)=>{try{t.push({level:o,text:i.map(gn).join(" ").slice(0,2e3),at:new Date().toISOString()})}catch{}n[o].apply(console,i)});return()=>{for(const o of pn)console[o]=n[o]}}function Do(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,b,h;const l=performance.now(),g=typeof s[0]=="string"?s[0]:(m=s[0])==null?void 0:m.url,f=((b=s[1])==null?void 0:b.method)||(typeof s[0]=="object"?(h=s[0])==null?void 0:h.method:"GET")||"GET";try{const u=await n.apply(this,s);return Lt(t,{url:g,method:f,status:u.status,ms:Math.round(performance.now()-l),slowMs:e}),u}catch(u){throw Lt(t,{url:g,method:f,status:0,error:String((u==null?void 0:u.message)||u),ms:Math.round(performance.now()-l),slowMs:e}),u}}),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",()=>{Lt(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 Lt(t,{url:e,method:n,status:o,ms:i,error:r,slowMs:s}){try{t.push({url:un(String(e)).slice(0,500),method:n,status:o,ms:i,error:r===void 0?void 0:Ho(r),failed:o===0||o>=400,slow:i>=s,at:new Date().toISOString()})}catch{}}function No(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: ${gn(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 hn(){const t=fn();try{if(navigator.getBattery){const e=await navigator.getBattery();t.battery=Math.round(e.level*100),t.charging=e.charging}}catch{}return t}function fn(){var e,n,o,i,r,s,l,g,f;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:((f=window.matchMedia)==null?void 0:f.call(window,"(prefers-reduced-motion: reduce)").matches)||!1}}function gn(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 Uo=typeof window<"u"&&window.fetch?window.fetch.bind(window):null,Ko=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.open:null,Po=typeof XMLHttpRequest<"u"?XMLHttpRequest.prototype.send:null,bn=["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"],ct=["image/png","image/jpeg","image/webp","video/webm","video/mp4"],_o=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getUserMedia&&typeof MediaRecorder<"u")},Vo=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getDisplayMedia&&typeof MediaRecorder<"u")},Wo=()=>{var t;return!!((t=navigator==null?void 0:navigator.mediaDevices)!=null&&t.getDisplayMedia)};function lt(t,e=bn){const n=String(t||"").split(";")[0].trim().toLowerCase();return e.includes(n)?n:null}async function Fo(){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 Yo({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(u=>MediaRecorder.isTypeSupported(u))||"video/webm",r=new MediaRecorder(o,{mimeType:i,videoBitsPerSecond:25e5}),s=[];r.ondataavailable=u=>{u.data.size&&s.push(u.data)},r.start(1e3);const l=Date.now(),g=setInterval(()=>e==null?void 0:e(Math.floor((Date.now()-l)/1e3)),500),f=()=>{clearInterval(g),clearTimeout(h),o.getTracks().forEach(u=>u.stop())},m=new Promise(u=>{r.onstop=()=>{f(),u(new Blob(s,{type:i}))}}),b=()=>{r.state!=="inactive"&&r.stop(),n==null||n()};o.getVideoTracks()[0].addEventListener("ended",b);const h=setTimeout(b,t);return{mime:i,maxMs:t,async stop(){return clearTimeout(h),r.state!=="inactive"&&r.stop(),{blob:await m,mime:i,kind:"VIDEO"}},cancel(){f(),r.state!=="inactive"&&r.stop()}}}async function Xo({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(u=>MediaRecorder.isTypeSupported(u))||"",r=new MediaRecorder(o,i?{mimeType:i}:void 0),s=[];r.ondataavailable=u=>{u.data.size&&s.push(u.data)},r.start(1e3);const l=Date.now(),g=setInterval(()=>e==null?void 0:e(Math.floor((Date.now()-l)/1e3)),500),f=()=>{clearInterval(g),clearTimeout(h),o.getTracks().forEach(u=>u.stop())},m=new Promise(u=>{r.onstop=()=>{f(),u(new Blob(s,{type:r.mimeType||i}))}}),h=setTimeout(()=>{r.state!=="inactive"&&r.stop(),n==null||n()},t);return{mime:r.mimeType||i,maxMs:t,async stop(){clearTimeout(h),r.state!=="inactive"&&r.stop();const u=await m;return{blob:u,mime:u.type||r.mimeType||i,kind:"AUDIO"}},cancel(){f(),r.state!=="inactive"&&r.stop()}}}async function Jo({apiBase:t,apiKey:e,blob:n,mime:o,onProgress:i}){const r=lt(o||n.type);if(!r)throw new Error(`[BugCatch] ${o||n.type||"that file"} can't be attached.`);const s=await Uo(`${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 Zo(g,n,r,i),{storageKey:l,mime:r}}function Zo(t,e,n,o){return new Promise((i,r)=>{const s=new XMLHttpRequest;Ko.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.")),Po.call(s,e)})}function Re(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(0)} KB`:`${(t/1024/1024).toFixed(1)} MB`}function dt(t){const e=Math.floor(t/60),n=t%60;return`${e}:${String(n).padStart(2,"0")}`}const Go=7*24*60*60*1e3;let ut;function ea(){return ut||(ut=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 ut=null,t})),ut}function ta(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 pt(t,e){const n=await ea();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 na(t){const e=await pt(t);return e&&Date.now()-e.savedAt>Go?(await pt(t,null),null):e||null}const oa="bugcatch.session.",At=t=>`${oa}${t}`,Bt=new Map;function Se(t){var e;if(!t)return null;try{const n=(e=window.localStorage)==null?void 0:e.getItem(At(t));if(n){const o=JSON.parse(n);if(o!=null&&o.token)return o}}catch{}return Bt.get(t)||null}function aa(t,e){var o;if(!t||!(e!=null&&e.token))return;const n={token:e.token,email:e.email||null,name:e.name||null};Bt.set(t,n);try{(o=window.localStorage)==null||o.setItem(At(t),JSON.stringify(n))}catch{}}function jt(t){var e;if(t){Bt.delete(t);try{(e=window.localStorage)==null||e.removeItem(At(t))}catch{}}}function ht({apiKey:t,token:e}){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function mn(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 ra({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:ht({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 ia({apiBase:t,apiKey:e,projectId:n,email:o,name:i}){const r=await fetch(`${t}/ingest/login/start`,{method:"POST",headers:ht({apiKey:e}),body:JSON.stringify({projectId:n,email:o,...i?{name:i}:{}})});if(!r.ok)throw new Error(await mn(r));return r.json()}async function sa({apiBase:t,apiKey:e,projectId:n,email:o,code:i}){const r=await fetch(`${t}/ingest/login`,{method:"POST",headers:ht({apiKey:e}),body:JSON.stringify({projectId:n,email:o,code:i})});if(!r.ok)throw new Error(await mn(r));const s=await r.json(),l={token:s.sessionToken,email:s.email,name:s.name};return aa(n,l),l}async function ca({apiBase:t,apiKey:e,projectId:n,token:o}){if(jt(n),!o)return{signedOut:!0};try{await fetch(`${t}/ingest/logout`,{method:"POST",headers:ht({apiKey:e,token:o}),body:JSON.stringify({projectId:n})})}catch{}return{signedOut:!0}}const ft="bugcatch.watch",yn=[2e3,5e3,15e3,3e4,6e4],la=5e3;let Ke=null;function da(t){Ke=t||null}const Rt=()=>Ke?`${ft}:${Ke}`:ft;function zt(){return Ke?`?projectId=${encodeURIComponent(Ke)}`:""}function he(){try{const t=window.localStorage;return(t==null?void 0:t.getItem(Rt()))||(t==null?void 0:t.getItem(ft))||null}catch{return null}}function Ot(t){var e;if(t)try{(e=window.localStorage)==null||e.setItem(Rt(),t)}catch{}}function Ae(){var t,e;try{(t=window.localStorage)==null||t.removeItem(Rt()),(e=window.localStorage)==null||e.removeItem(ft)}catch{}wn()}const gt="bugcatch.unread";function ua(t){return[(t==null?void 0:t.bugId)||"report",(t==null?void 0:t.kind)||"status",(t==null?void 0:t.at)||""].join("|")}function Pe(){var t;try{const e=(t=window.localStorage)==null?void 0:t.getItem(gt),n=e?JSON.parse(e):[];return Array.isArray(n)?n.filter(o=>o&&typeof o.id=="string"):[]}catch{return[]}}function bt(t){var e,n;try{t.length?(e=window.localStorage)==null||e.setItem(gt,JSON.stringify(t)):(n=window.localStorage)==null||n.removeItem(gt)}catch{}}function pa(t){const e={...t,id:ua(t)},n=Pe().filter(o=>o.id!==e.id);return n.push(e),bt(n.slice(-20)),e}function ha(t){const e=Pe().filter(n=>n.id!==t);return bt(e),e}function fa(t){const e=Pe().filter(n=>n.bugId!==t);return bt(e),e}function wn(){return bt([]),[]}function ga(t,e){let n=t,o=n.indexOf(`
|
|
2
2
|
|
|
3
|
-
`);for(;
|
|
3
|
+
`);for(;o!==-1;){const i=n.slice(0,o);n=n.slice(o+2);const r=i.split(`
|
|
4
4
|
`).filter(s=>s.startsWith("data:")).map(s=>s.slice(5).trim()).join(`
|
|
5
|
-
`);if(r)try{e(JSON.parse(r))}catch{}
|
|
5
|
+
`);if(r)try{e(JSON.parse(r))}catch{}o=n.indexOf(`
|
|
6
6
|
|
|
7
|
-
`)}return n}async function Ca({apiBase:t}){const e=re();if(!e)return{reporter:null,reports:[]};const n=await fetch(`${t}/ingest/watch/reports`,{headers:{Authorization:`Bearer ${e}`}});if(n.status===401)return ve(),{reporter:null,reports:[]};if(!n.ok)throw new Error(`[BugCatch] could not load your reports (HTTP ${n.status})`);return n.json()}async function $a({apiBase:t,bugId:e}){const n=re();if(!n)return null;const a=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}`,{headers:{Authorization:`Bearer ${n}`}});if(a.status===401)return ve(),null;if(a.status===404)return null;if(!a.ok)throw new Error(`[BugCatch] could not load that report (HTTP ${a.status})`);return a.json()}async function Pt({apiBase:t,bugId:e,body:n,attachments:a}){const i=re();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}:{},...a!=null&&a.length?{attachments:a}:{}})});if(r.status===401)throw ve(),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 Ea({apiBase:t,apiKey:e,projectId:n}){const a={agent:!1,live:!1};try{const i=await fetch(`${t}/ingest/calls?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!i.ok)return a;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"}:a}catch{return a}}async function Ta({apiBase:t,apiKey:e,token:n,payload:a}){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(a)});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 _t({apiBase:t,bugId:e}){const n=re();if(!n)return!1;try{const a=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{headers:{Authorization:`Bearer ${n}`}});if(!a.ok)return!1;const i=await a.json();return!!(i!=null&&i.offered)}catch{return!1}}async function Ia({apiBase:t,bugId:e}){const n=re();if(!n)throw new Error("[BugCatch] nothing filed from this browser yet.");const a=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{method:"POST",headers:{Authorization:`Bearer ${n}`}});if(a.status===401)throw ve(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!a.ok){let i="";try{const r=await a.json();i=Array.isArray(r.message)?r.message.join(", "):r.message||""}catch{}throw new Error(i||`[BugCatch] the call could not be started (HTTP ${a.status}).`)}return a.json()}async function Ma({apiBase:t,bugId:e,callId:n}){const a=re();if(a)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/give-up`,{method:"POST",headers:{Authorization:`Bearer ${a}`}})}catch{}}async function La({apiBase:t,bugId:e,callId:n}){const a=re();if(!a)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 ${a}`}});if(i.status===401)throw ve(),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 Ft({apiBase:t,bugId:e,callId:n}){const a=re();if(a)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/decline`,{method:"POST",headers:{Authorization:`Bearer ${a}`}})}catch{}}function Ba({apiBase:t,onUpdate:e}){let n=!0,a=0,i=null;async function r(){const s=re();if(!s)throw new Error("nothing filed from this browser yet");i=new AbortController;const l=await fetch(`${t}/ingest/watch/stream`,{headers:{Authorization:`Bearer ${s}`,Accept:"text/event-stream"},signal:i.signal});if(l.status===401)throw n=!1,ve(),new Error("subscription no longer valid");if(!l.ok||!l.body)throw new Error(`stream failed (${l.status})`);const p=Date.now(),f=l.body.getReader(),y=new TextDecoder;let h="";try{for(;;){const{value:b,done:g}=await f.read();if(g)break;h+=y.decode(b,{stream:!0}),h=Sa(h,S=>{if(S.type!=="ping")try{e(S)}catch{}})}}finally{Date.now()-p>=ya&&(a=0)}}return(async()=>{for(;n;){try{await r()}catch(l){if((l==null?void 0:l.name)==="AbortError")return}if(!n)return;const s=Vt[Math.min(a,Vt.length-1)];a+=1,await new Promise(l=>setTimeout(l,s))}})(),()=>{n=!1,i==null||i.abort(),i=null}}const Aa={OPEN:"is open",IN_PROGRESS:"is being worked on",RESOLVED:"has been fixed",CLOSED:"has been closed"},Ra={status:null,severity:"#e8912a",assignment:"#4da3ff",reply:"#35b87f"};function Wt(t){return t!=null&&t.summary?t.summary:`Your report ${Aa[t==null?void 0:t.status]||"was updated"}`}function Qa(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 ja(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 a=Math.round(n/60);if(a<24)return`${a}h ago`;const i=Math.round(a/24);return i===1?"yesterday":`${i}d ago`}function za(){var t;try{const e=document.querySelector('[data-bugcatch="widget"]'),n=(t=e==null?void 0:e.shadowRoot)==null?void 0:t.querySelector(".dock"),a=(n==null?void 0:n.getBoundingClientRect().height)||0;return a?Math.round(a)+12:0}catch{return 0}}function Oa({accent:t="#4F46E5",position:e="bottom-right",onRead:n=null,onReadAll:a=null,onCall:i=null}={}){const[r,s]=String(e).split("-"),l=r==="top",p=s==="left",f=new Map;let y=null,h=null;function b(){return y!=null&&y.isConnected||(y=document.createElement("div"),y.setAttribute("data-bugcatch","updates"),y.style.cssText=["position:fixed",`${l?"top":"bottom"}:${20+za()}px`,`${p?"left":"right"}:20px`,"z-index:2147482998","display:flex","flex-direction:column","gap:10px",`align-items:${p?"flex-start":"flex-end"}`,"width:min(320px,calc(100vw - 40px))","pointer-events:none"].join(";"),document.body.appendChild(y)),y}function g(C){if(!C._bcShown){C.remove();return}C.style.opacity="0",C.style.transform=`translateY(${l?-8:8}px)`,setTimeout(()=>C.remove(),250)}function S(C,$){const T=document.createElement("button");return T.type="button",T.textContent=C,T.style.cssText=["padding:5px 11px",`border:${$?"0":"1px solid rgba(236,238,242,.24)"}`,"border-radius:6px",`background:${$?t:"transparent"}`,`color:${$?"#fff":"#eceef2"}`,"font:600 12px/1.4 inherit","cursor:pointer","white-space:nowrap"].join(";"),T}function A(C){var V;const $=document.createElement("div");$.setAttribute("role","status"),$.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 ${Ra[C==null?void 0:C.kind]||t}`,"opacity:0",`transform:translateY(${l?-8:8}px)`,"transition:opacity .2s ease,transform .2s ease","pointer-events:auto"].join(";");const T=document.createElement("div");T.style.cssText="font-weight:600;margin-bottom:2px",T.textContent=Wt(C);const L=document.createElement("div");L.style.cssText=(C==null?void 0:C.kind)==="reply"?"color:#a4abba;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden":"color:#a4abba",L.textContent=Qa(C);const X=document.createElement("div");X.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=ja(C==null?void 0:C.at);const j=S("Mark as read",!1);if(j.setAttribute("aria-label",`Mark as read: ${Wt(C)}`),j.addEventListener("click",()=>n==null?void 0:n(C.id)),X.append(U,j),(V=C==null?void 0:C.call)!=null&&V.offered&&typeof i=="function"){const E=S("Talk it through",!0);E.addEventListener("click",()=>i(C)),X.insertBefore(E,j)}return $.append(T,L,X),$}function ie(C){const $=document.createElement("div");$.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 T=document.createElement("span");T.style.cssText="margin-right:auto",T.textContent=`${C} more update${C===1?"":"s"}`;const L=S("Mark all as read",!1);return L.style.cssText+=";padding:4px 9px",L.addEventListener("click",()=>a==null?void 0:a()),$.append(T,L),$}return{render(C){const $=Array.isArray(C)?C:[];if(!$.length){for(const E of f.values())g(E);f.clear(),h==null||h.remove(),h=null,y==null||y.remove(),y=null;return}const T=$.slice(-3),L=new Set(T.map(E=>E.id)),X=b();for(const[E,se]of f)L.has(E)||(g(se),f.delete(E));const U=[];for(const E of T){if(f.has(E.id))continue;const se=A(E);f.set(E.id,se),U.push(se)}const j=l?[...T].reverse():T;for(const E of j)X.appendChild(f.get(E.id));const V=$.length-T.length;V>0?(h==null||h.remove(),h=ie(V),l?X.appendChild(h):X.insertBefore(h,X.firstChild)):(h==null||h.remove(),h=null),requestAnimationFrame(()=>{for(const E of U)E.isConnected&&(E._bcShown=!0,E.style.opacity="1",E.style.transform="translateY(0)")})},destroy(){f.clear(),h=null,y==null||y.remove(),y=null}}}async function Ha({apiBase:t,apiKey:e,projectId:n,sessionToken:a}){try{const i=await fetch(`${t}/ingest/known-issues?projectId=${encodeURIComponent(n)}`,{headers:yt(e,a)});if(!i.ok)return[];const r=await i.json();return Array.isArray(r==null?void 0:r.issues)?r.issues:[]}catch{return[]}}async function qa({apiBase:t,apiKey:e,projectId:n,q:a,sessionToken:i,signal:r}){try{const s=`${t}/ingest/known-issues/match?projectId=${encodeURIComponent(n)}&q=${encodeURIComponent(a)}`,l=await fetch(s,{headers:yt(e,i),signal:r});if(!l.ok)return[];const p=await l.json();return Array.isArray(p==null?void 0:p.issues)?p.issues:[]}catch{return[]}}async function Da({apiBase:t,apiKey:e,projectId:n,bugId:a,reporterEmail:i,reporterName:r,metadata:s,logs:l,sessionToken:p}){const f=await fetch(`${t}/ingest/known-issues/${encodeURIComponent(a)}/join`,{method:"POST",headers:yt(e,p),body:JSON.stringify({projectId:n,reporterEmail:i,reporterName:r,metadata:s,logs:l})});if(!f.ok)throw new Error(await Na(f));const y=await f.json();return y!=null&&y.watchToken&&bt(y.watchToken),y}function yt(t,e){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function Na(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 Ua({apiBase:t,bugId:e,...n}){const a=re();if(!a)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 ${a}`},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 Va=2e4,Yt=120,Xt=200;function Zt(t){if(typeof t!="string"||!t.trim()||t.length>400)return null;try{return new RegExp(t.trim(),"gi")}catch{return null}}const Ka=t=>String(t).replace(/\s+/g," ").trim().slice(0,Yt);function Jt(t){const e=t.length>1&&t[1]!==void 0?t[1]:t[0];return typeof e!="string"||!e.trim()?null:{value:Ka(e),start:t.index,end:t.index+t[0].length}}function Pa(t,e){const n=[],a=new Set;for(const i of e||[]){const r=typeof i=="string"?i.slice(0,Va):"";if(!r)continue;const s=[],l=(p,f)=>s.some(([y,h])=>p<h&&f>y);for(const p of t||[]){if(!p||p.scan===!1)continue;const f=Zt(p.pattern);if(!f)continue;let y,h=0;for(f.lastIndex=0;(y=f.exec(r))!==null&&!(++h>Xt);){if(y[0]===""){f.lastIndex+=1;continue}const b=Jt(y);if(b&&!l(b.start,b.end)){s.push([b.start,b.end]);const g=`${p.key}:${b.value.toLowerCase()}`;a.has(g)||(a.add(g),n.push({key:p.key,label:p.label,value:b.value}))}}}}return n}function _a(t,e){const n=typeof e=="string"?e.trim():"";if(!n||n.length>Yt)return null;const a=Zt(t&&(t.valuePattern||t.pattern));if(!a)return null;let i,r=0;for(a.lastIndex=0;(i=a.exec(n))!==null&&!(++r>Xt);){if(i[0]===""){a.lastIndex+=1;continue}const s=i.index+i[0].length===n.length,l=i.index===0||i.index===1;if(s&&l){const p=Jt(i);if(p)return p.value}}return null}async function Fa({apiBase:t,apiKey:e,projectId:n}){const a={policy:"off",fields:[]};try{const i=await fetch(`${t}/ingest/reference-fields?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!i.ok)return a;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}:a}catch{return a}}const Gt={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"},en=[{value:"LOW",label:"Low"},{value:"MEDIUM",label:"Medium"},{value:"HIGH",label:"High"},{value:"CRITICAL",label:"Critical"}],Q={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>'},Wa='<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>',tn=[{value:"light",label:"Light",icon:Q.sun},{value:"dark",label:"Dark",icon:Q.moon},{value:"auto",label:"System default",icon:Q.auto}],nn="bugcatch:theme";function Ya(){try{const t=localStorage.getItem(nn);return tn.some(e=>e.value===t)?t:null}catch{return null}}function Xa(t){try{localStorage.setItem(nn,t)}catch{}}const Za=t=>`
|
|
7
|
+
`)}return n}async function ba({apiBase:t}){const e=he();if(!e)return{reporter:null,reports:[]};const n=await fetch(`${t}/ingest/watch/reports${zt()}`,{headers:{Authorization:`Bearer ${e}`}});if(n.status===401)return Ae(),{reporter:null,reports:[]};if(!n.ok)throw new Error(`[BugCatch] could not load your reports (HTTP ${n.status})`);return n.json()}async function ma({apiBase:t,bugId:e}){const n=he();if(!n)return null;const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}${zt()}`,{headers:{Authorization:`Bearer ${n}`}});if(o.status===401)return Ae(),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 vn({apiBase:t,bugId:e,body:n,attachments:o}){const i=he();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 Ae(),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 ya({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 wa({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 xn({apiBase:t,bugId:e}){const n=he();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 va({apiBase:t,bugId:e}){const n=he();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 Ae(),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 xa({apiBase:t,bugId:e,callId:n}){const o=he();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 ka({apiBase:t,bugId:e,callId:n}){const o=he();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 Ae(),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 kn({apiBase:t,bugId:e,callId:n}){const o=he();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/decline`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}function Sa({apiBase:t,onUpdate:e}){let n=!0,o=0,i=null;async function r(){const s=he();if(!s)throw new Error("nothing filed from this browser yet");i=new AbortController;const l=await fetch(`${t}/ingest/watch/stream${zt()}`,{headers:{Authorization:`Bearer ${s}`,Accept:"text/event-stream"},signal:i.signal});if(l.status===401)throw n=!1,Ae(),new Error("subscription no longer valid");if(!l.ok||!l.body)throw new Error(`stream failed (${l.status})`);const g=Date.now(),f=l.body.getReader(),m=new TextDecoder;let b="";try{for(;;){const{value:h,done:u}=await f.read();if(u)break;b+=m.decode(h,{stream:!0}),b=ga(b,k=>{if(k.type!=="ping")try{e(k)}catch{}})}}finally{Date.now()-g>=la&&(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=yn[Math.min(o,yn.length-1)];o+=1,await new Promise(l=>setTimeout(l,s))}})(),()=>{n=!1,i==null||i.abort(),i=null}}const $a={OPEN:"is open",IN_PROGRESS:"is being worked on",RESOLVED:"has been fixed",CLOSED:"has been closed"},Ca={status:null,severity:"#e8912a",assignment:"#4da3ff",reply:"#35b87f"};function Sn(t){return t!=null&&t.summary?t.summary:`Your report ${$a[t==null?void 0:t.status]||"was updated"}`}function Ta(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 Ea(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 Ia(){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 Ma({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",f=new Map;let m=null,b=null;function h(){return m!=null&&m.isConnected||(m=document.createElement("div"),m.setAttribute("data-bugcatch","updates"),m.style.cssText=["position:fixed",`${l?"top":"bottom"}:${20+Ia()}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 u(S){if(!S._bcShown){S.remove();return}S.style.opacity="0",S.style.transform=`translateY(${l?-8:8}px)`,setTimeout(()=>S.remove(),250)}function k(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 R(S){var Q;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 ${Ca[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=Sn(S);const A=document.createElement("div");A.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",A.textContent=Ta(S);const H=document.createElement("div");H.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=Ea(S==null?void 0:S.at);const T=k("Mark as read",!1);if(T.setAttribute("aria-label",`Mark as read: ${Sn(S)}`),T.addEventListener("click",()=>n==null?void 0:n(S.id)),H.append(U,T),(Q=S==null?void 0:S.call)!=null&&Q.offered&&typeof i=="function"){const $=k("Talk it through",!0);$.addEventListener("click",()=>i(S)),H.insertBefore($,T)}return M.append(I,A,H),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 A=k("Mark all as read",!1);return A.style.cssText+=";padding:4px 9px",A.addEventListener("click",()=>o==null?void 0:o()),M.append(I,A),M}return{render(S){const M=Array.isArray(S)?S:[];if(!M.length){for(const $ of f.values())u($);f.clear(),b==null||b.remove(),b=null,m==null||m.remove(),m=null;return}const I=M.slice(-3),A=new Set(I.map($=>$.id)),H=h();for(const[$,Y]of f)A.has($)||(u(Y),f.delete($));const U=[];for(const $ of I){if(f.has($.id))continue;const Y=R($);f.set($.id,Y),U.push(Y)}const T=l?[...I].reverse():I;for(const $ of T)H.appendChild(f.get($.id));const Q=M.length-I.length;Q>0?(b==null||b.remove(),b=z(Q),l?H.appendChild(b):H.insertBefore(b,H.firstChild)):(b==null||b.remove(),b=null),requestAnimationFrame(()=>{for(const $ of U)$.isConnected&&($._bcShown=!0,$.style.opacity="1",$.style.transform="translateY(0)")})},destroy(){f.clear(),b=null,m==null||m.remove(),m=null}}}async function La({apiBase:t,apiKey:e,projectId:n,sessionToken:o}){try{const i=await fetch(`${t}/ingest/known-issues?projectId=${encodeURIComponent(n)}`,{headers:Qt(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 Aa({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:Qt(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 Ba({apiBase:t,apiKey:e,projectId:n,bugId:o,reporterEmail:i,reporterName:r,metadata:s,logs:l,sessionToken:g}){const f=await fetch(`${t}/ingest/known-issues/${encodeURIComponent(o)}/join`,{method:"POST",headers:Qt(e,g),body:JSON.stringify({projectId:n,reporterEmail:i,reporterName:r,metadata:s,logs:l})});if(!f.ok)throw new Error(await ja(f));const m=await f.json();return m!=null&&m.watchToken&&Ot(m.watchToken),m}function Qt(t,e){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function ja(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 Ra({apiBase:t,bugId:e,...n}){const o=he();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 za=2e4,$n=120,Ht=200;function Cn(t){if(typeof t!="string"||!t.trim()||t.length>400)return null;try{return new RegExp(t.trim(),"gi")}catch{return null}}const Tn=t=>String(t).replace(/\s+/g," ").trim().slice(0,$n);function En(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 In(t){const e=t.length>1&&t[1]!==void 0?t[1]:t[0];return typeof e!="string"||!e.trim()?null:{value:Tn(e),start:t.index,end:t.index+t[0].length}}function Oa(t,e){return((t.slice(e.start,e.valueStart)+t.slice(e.valueEnd,e.end)).match(/[a-z]/gi)||[]).length}const Qa=/[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,Mn=/[\w-]/;function Ha(t,e){const n=(t||[]).filter(h=>h&&h.scan!==!1&&h.pattern),o=[],i=[],r=(e||[]).map(h=>typeof h=="string"?h.slice(0,za):"");r.forEach((h,u)=>{h&&n.forEach((k,R)=>{const z=Cn(k.pattern);if(!z)return;let S,M=0;for(z.lastIndex=0;(S=z.exec(h))!==null&&!(++M>Ht);){if(S[0]===""){z.lastIndex+=1;continue}const I=In(S);if(!I)continue;const A=S.length>1&&S[1]!==void 0?S[1]:S[0],H=S.index+Math.max(0,S[0].indexOf(A)),U={block:u,order:R,key:k.key,label:k.label,value:En(k,I.value),start:I.start,end:I.end,valueStart:H,valueEnd:H+A.length};U.weight=Oa(h,U),o.push(U)}})});const s=o.filter(h=>!o.some(u=>u!==h&&u.block===h.block&&u.key!==h.key&&u.valueStart<=h.valueStart&&u.valueEnd>=h.valueEnd&&u.weight>h.weight)),l=new Map;for(const h of s){const u=h.value.toLowerCase();l.has(u)||l.set(u,{value:h.value,keys:new Map});const k=l.get(u);k.keys.has(h.key)||k.keys.set(h.key,{key:h.key,label:h.label,order:h.order})}const g=[],f=[],m=new Map;for(const h of l.values()){const u=[...h.keys.values()].sort((z,S)=>z.order-S.order);if(u.length>1){f.push({value:h.value,kinds:u.map(({key:z,label:S})=>({key:z,label:S}))});continue}const[{key:k,label:R}]=u;m.has(k)||m.set(k,[]),m.get(k).push({key:k,label:R,value:h.value})}for(const h of m.values())if(h.length===1)g.push(h[0]);else for(const u of h)f.push({value:u.value,kinds:[{key:u.key,label:u.label}]});const b=new Set(l.keys());return r.forEach((h,u)=>{if(!h)return;const k=o.filter(M=>M.block===u),R=new RegExp(Qa.source,"gi");let z,S=0;for(;(z=R.exec(h))!==null&&++S<=Ht;){const M=z.index,I=M+z[0].length;if(k.some($=>M<$.end&&I>$.start))continue;const A=h[M-1]||"",H=h[I]||"";if(Mn.test(A)||Mn.test(H)||A==="/"||A==="@"||H==="@"||A==="."||H==="."&&/\w/.test(h[I+1]||"")||!/\d/.test(z[0]))continue;const U=Tn(z[0]),T=U.toLowerCase();if(b.has(T))continue;const Q=(t||[]).filter($=>$&&Ln($,U)).map(({key:$,label:Y})=>({key:$,label:Y}));Q.length&&(b.add(T),i.push({value:U,kinds:Q}))}}),{found:g,conflicts:f,loose:i}}function Ln(t,e){const n=typeof e=="string"?e.trim():"";if(!n||n.length>$n)return null;const o=Cn(t&&(t.valuePattern||t.pattern));if(!o)return null;let i,r=0;for(o.lastIndex=0;(i=o.exec(n))!==null&&!(++r>Ht);){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=In(i);if(g)return En(t,g.value)}}return null}async function qa({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 An={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"},Bn=[{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>'},Da='<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>',jn=[{value:"light",label:"Light",icon:B.sun},{value:"dark",label:"Dark",icon:B.moon},{value:"auto",label:"System default",icon:B.auto}],Rn="bugcatch:theme";function Na(){try{const t=localStorage.getItem(Rn);return jn.some(e=>e.value===t)?t:null}catch{return null}}function Ua(t){try{localStorage.setItem(Rn,t)}catch{}}const Ka=t=>`
|
|
8
8
|
:host { all: initial; }
|
|
9
9
|
:host {
|
|
10
10
|
--bc-accent: ${t};
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
--bc-bg: #fff;
|
|
13
13
|
--bc-elevated: #fff;
|
|
14
14
|
--bc-fg: #14161c;
|
|
15
|
-
--bc-muted: #
|
|
15
|
+
--bc-muted: #5f6673;
|
|
16
16
|
--bc-faint: #9aa1ad;
|
|
17
|
-
--bc-border: #
|
|
18
|
-
--bc-subtle: #
|
|
17
|
+
--bc-border: #e6e8ee;
|
|
18
|
+
--bc-subtle: #f4f5f8;
|
|
19
19
|
--bc-raised: #fff;
|
|
20
20
|
--bc-danger: #dc2626;
|
|
21
21
|
--bc-ok: #059669;
|
|
@@ -25,36 +25,56 @@
|
|
|
25
25
|
identifies itself without out-shouting the host's page. */
|
|
26
26
|
--bc-brand-ink: #4b5563;
|
|
27
27
|
--bc-brand-mark: #5647ee;
|
|
28
|
-
--bc-shadow: 0 24px
|
|
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);
|
|
29
30
|
--bc-dur: 240ms;
|
|
30
31
|
--bc-ease: cubic-bezier(.22,1,.36,1);
|
|
32
|
+
/* The system everything below is drawn from. Derived tokens are written in
|
|
33
|
+
terms of the ones above, so a host that overrides --bc-accent or the
|
|
34
|
+
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);
|
|
38
|
+
--bc-r-sm: 8px;
|
|
39
|
+
--bc-r-md: 12px;
|
|
40
|
+
--bc-r-lg: 18px;
|
|
41
|
+
--bc-r-xl: 22px;
|
|
42
|
+
--bc-fs-xs: 11.5px;
|
|
43
|
+
--bc-fs-sm: 12.5px;
|
|
44
|
+
--bc-fs-md: 14px;
|
|
45
|
+
--bc-fs-lg: 16px;
|
|
46
|
+
--bc-pad: 18px;
|
|
31
47
|
}
|
|
32
48
|
:host(.bc-dark) {
|
|
33
49
|
--bc-bg: #14161c;
|
|
34
50
|
--bc-elevated: #1c1f27;
|
|
35
51
|
--bc-fg: #eef0f4;
|
|
36
|
-
--bc-muted: #
|
|
37
|
-
--bc-faint: #
|
|
38
|
-
--bc-border: #
|
|
39
|
-
--bc-subtle: #
|
|
52
|
+
--bc-muted: #a3aab6;
|
|
53
|
+
--bc-faint: #6f7682;
|
|
54
|
+
--bc-border: #2a2e38;
|
|
55
|
+
--bc-subtle: #1f222a;
|
|
40
56
|
--bc-raised: #363d4b;
|
|
41
57
|
--bc-brand-ink: #9aa1ad;
|
|
42
58
|
--bc-brand-mark: #9b8fff;
|
|
43
|
-
--bc-shadow: 0 24px
|
|
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));
|
|
44
62
|
}
|
|
45
63
|
@media (prefers-color-scheme: dark) {
|
|
46
64
|
:host(.bc-auto) {
|
|
47
65
|
--bc-bg: #14161c;
|
|
48
66
|
--bc-elevated: #1c1f27;
|
|
49
67
|
--bc-fg: #eef0f4;
|
|
50
|
-
--bc-muted: #
|
|
51
|
-
--bc-faint: #
|
|
52
|
-
--bc-border: #
|
|
53
|
-
--bc-subtle: #
|
|
68
|
+
--bc-muted: #a3aab6;
|
|
69
|
+
--bc-faint: #6f7682;
|
|
70
|
+
--bc-border: #2a2e38;
|
|
71
|
+
--bc-subtle: #1f222a;
|
|
54
72
|
--bc-raised: #363d4b;
|
|
55
73
|
--bc-brand-ink: #9aa1ad;
|
|
56
74
|
--bc-brand-mark: #9b8fff;
|
|
57
|
-
--bc-shadow: 0 24px
|
|
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));
|
|
58
78
|
}
|
|
59
79
|
}
|
|
60
80
|
|
|
@@ -65,6 +85,16 @@ svg { width: 1em; height: 1em; display: block; }
|
|
|
65
85
|
— which is why .dock and .launcher each had to say so themselves. Said once,
|
|
66
86
|
for all of them, so the next element hidden from JavaScript works. */
|
|
67
87
|
[hidden] { display: none !important; }
|
|
88
|
+
.sr-only {
|
|
89
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
90
|
+
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* One focus ring for everything clickable, so keyboard users never have to
|
|
94
|
+
guess where they are. Fields draw their own ring (a glow, not an outline). */
|
|
95
|
+
button:focus-visible, a:focus-visible, select:focus-visible, [tabindex]:focus-visible {
|
|
96
|
+
outline: 2px solid var(--bc-accent); outline-offset: 2px;
|
|
97
|
+
}
|
|
68
98
|
|
|
69
99
|
/* Motion is decoration here — every animation below is an entrance or a state
|
|
70
100
|
hint, so switching them off costs nothing but the polish. */
|
|
@@ -78,14 +108,19 @@ svg { width: 1em; height: 1em; display: block; }
|
|
|
78
108
|
|
|
79
109
|
@keyframes bc-fade-in { from { opacity: 0 } to { opacity: 1 } }
|
|
80
110
|
@keyframes bc-fade-out { to { opacity: 0 } }
|
|
81
|
-
@keyframes bc-panel-in { from { opacity: 0; transform: translateY(
|
|
82
|
-
@keyframes bc-panel-out { to { opacity: 0; transform: translateY(
|
|
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%) } }
|
|
83
115
|
@keyframes bc-launcher-in { from { opacity: 0; transform: translateY(12px) scale(.86) } to { opacity: 1; transform: none } }
|
|
84
|
-
@keyframes bc-item-in { from { opacity: 0; transform: translateY(8px) scale(.
|
|
116
|
+
@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 } }
|
|
85
118
|
@keyframes bc-pulse { 0%, 100% { opacity: 1; transform: scale(1) } 50% { opacity: .3; transform: scale(.78) } }
|
|
86
119
|
@keyframes bc-spin { to { transform: rotate(360deg) } }
|
|
87
120
|
@keyframes bc-pop { 0% { transform: scale(.5); opacity: 0 } 65% { transform: scale(1.1) } 100% { transform: scale(1); opacity: 1 } }
|
|
88
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 } }
|
|
89
124
|
|
|
90
125
|
/* ---------- dock: theme switch stacked above the launcher ---------- */
|
|
91
126
|
.dock {
|
|
@@ -106,7 +141,7 @@ svg { width: 1em; height: 1em; display: block; }
|
|
|
106
141
|
display: inline-flex; align-items: center; gap: 2px;
|
|
107
142
|
padding: 3px; border-radius: 999px;
|
|
108
143
|
background: var(--bc-elevated); border: 1px solid var(--bc-border);
|
|
109
|
-
box-shadow: 0 6px 18px rgba(10,12,16,.
|
|
144
|
+
box-shadow: 0 6px 18px rgba(10,12,16,.14), 0 1px 3px rgba(10,12,16,.08);
|
|
110
145
|
animation: bc-launcher-in 420ms var(--bc-ease) both;
|
|
111
146
|
}
|
|
112
147
|
.themer button {
|
|
@@ -121,88 +156,152 @@ svg { width: 1em; height: 1em; display: block; }
|
|
|
121
156
|
.themer button:active { transform: scale(.92); }
|
|
122
157
|
.themer button[aria-pressed="true"] { background: var(--bc-accent); color: var(--bc-on-accent); }
|
|
123
158
|
.themer button[aria-pressed="true"]:hover { background: var(--bc-accent); filter: brightness(1.06); }
|
|
124
|
-
.themer button:focus-visible { outline: 2px solid var(--bc-accent); outline-offset: 2px; }
|
|
125
159
|
|
|
126
160
|
/* ---------- launcher ---------- */
|
|
127
161
|
.launcher {
|
|
128
|
-
|
|
129
|
-
|
|
162
|
+
position: relative;
|
|
163
|
+
display: inline-flex; align-items: center; gap: 9px;
|
|
164
|
+
height: 48px; padding: 0 20px 0 16px; border: none; border-radius: 999px;
|
|
130
165
|
background: var(--bc-accent); color: var(--bc-on-accent);
|
|
131
|
-
font-size:
|
|
166
|
+
font-size: var(--bc-fs-md); font-weight: 620; line-height: 1; letter-spacing: -.01em;
|
|
132
167
|
cursor: pointer;
|
|
133
|
-
box-shadow: 0
|
|
134
|
-
transition: transform var(--bc-dur) var(--bc-ease), box-shadow var(--bc-dur) var(--bc-ease), filter 160ms linear;
|
|
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);
|
|
135
170
|
animation: bc-launcher-in 420ms var(--bc-ease) both;
|
|
136
171
|
}
|
|
137
|
-
|
|
138
|
-
|
|
172
|
+
/* Two glyphs in one box: the bubble when closed, a chevron when open, turned
|
|
173
|
+
into each other so the button reads as the panel's own handle. */
|
|
174
|
+
.launcher .ico { position: relative; width: 20px; height: 20px; flex: none; }
|
|
175
|
+
.launcher .ico > span {
|
|
176
|
+
position: absolute; inset: 0;
|
|
177
|
+
transition: transform 320ms var(--bc-ease), opacity 200ms ease;
|
|
178
|
+
}
|
|
179
|
+
.launcher .ico svg { width: 20px; height: 20px; }
|
|
180
|
+
.launcher .ico .when-open { opacity: 0; transform: rotate(-90deg) scale(.6); }
|
|
181
|
+
.launcher[aria-expanded="true"] .ico .when-closed { opacity: 0; transform: rotate(90deg) scale(.6); }
|
|
182
|
+
.launcher[aria-expanded="true"] .ico .when-open { opacity: 1; transform: none; }
|
|
183
|
+
.launcher:hover { transform: translateY(-2px); filter: brightness(1.06); }
|
|
139
184
|
.launcher:active { transform: translateY(0) scale(.97); transition-duration: 90ms; }
|
|
140
|
-
.launcher:focus-visible { outline
|
|
185
|
+
.launcher:focus-visible { outline-offset: 3px; }
|
|
141
186
|
.launcher[hidden] { display: none; }
|
|
142
187
|
|
|
143
188
|
/* ---------- scrim + panel ---------- */
|
|
144
189
|
.scrim {
|
|
145
190
|
position: fixed; inset: 0; z-index: 2147483001;
|
|
146
|
-
background: rgba(10,12,16,.
|
|
191
|
+
background: rgba(10,12,16,.32);
|
|
147
192
|
animation: bc-fade-in 200ms ease both;
|
|
148
193
|
}
|
|
149
|
-
.scrim.closing { animation: bc-fade-out
|
|
194
|
+
.scrim.closing { animation: bc-fade-out 170ms ease both; }
|
|
150
195
|
|
|
151
196
|
/* The panel belongs to the launcher, so it opens out of that corner rather
|
|
152
197
|
than in the middle of the screen. --bc-dock-h is measured at open time
|
|
153
|
-
because the dock's height depends on whether the launcher is showing.
|
|
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. */
|
|
154
200
|
.panel {
|
|
155
201
|
position: fixed;
|
|
156
|
-
|
|
202
|
+
display: flex; flex-direction: column;
|
|
203
|
+
width: calc(100vw - 40px); max-width: 420px;
|
|
157
204
|
max-height: calc(100vh - var(--bc-dock-h, 84px) - 52px);
|
|
158
|
-
overflow
|
|
205
|
+
overflow: hidden;
|
|
159
206
|
background: var(--bc-bg); color: var(--bc-fg);
|
|
160
|
-
border-radius:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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; }
|
|
165
216
|
.scrim.at-bottom .panel { bottom: calc(20px + var(--bc-dock-h, 84px) + 12px); }
|
|
166
|
-
.scrim.at-top .panel { top: calc(20px + var(--bc-dock-h, 84px) + 12px); }
|
|
167
|
-
.scrim.closing .panel { animation: bc-panel-out
|
|
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; }
|
|
168
219
|
|
|
169
|
-
|
|
170
|
-
.head
|
|
171
|
-
flex: none;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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);
|
|
175
240
|
}
|
|
176
241
|
.head .titles { flex: 1; min-width: 0; }
|
|
177
|
-
.head h2 {
|
|
178
|
-
|
|
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); }
|
|
179
247
|
.icon-btn {
|
|
180
248
|
flex: none; display: grid; place-items: center;
|
|
181
|
-
width:
|
|
182
|
-
border: none; border-radius:
|
|
183
|
-
cursor: pointer; transition: background 160ms, color 160ms;
|
|
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);
|
|
184
252
|
}
|
|
185
|
-
.icon-btn:hover { background: var(--bc-
|
|
186
|
-
.icon-btn:
|
|
187
|
-
|
|
188
|
-
.body { padding: 16px 18px 0; }
|
|
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; }
|
|
189
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
|
+
}
|
|
190
261
|
|
|
191
|
-
label { display: block; font-size:
|
|
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); }
|
|
192
264
|
input[type=text], input[type=email], textarea {
|
|
193
|
-
width: 100%; padding:
|
|
265
|
+
width: 100%; padding: 11px 13px; font-size: var(--bc-fs-md); line-height: 1.45;
|
|
194
266
|
color: var(--bc-fg); background: var(--bc-elevated);
|
|
195
|
-
border: 1px solid var(--bc-border); border-radius:
|
|
196
|
-
transition: border-color 160ms, box-shadow 160ms;
|
|
267
|
+
border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
|
|
268
|
+
transition: border-color 160ms, box-shadow 160ms, background 160ms;
|
|
197
269
|
}
|
|
198
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)); }
|
|
199
272
|
input[type=text]:focus, input[type=email]:focus, textarea:focus {
|
|
200
|
-
outline: none; border-color: var(--bc-accent);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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); }
|
|
206
305
|
|
|
207
306
|
/* ---------- what is already known ----------
|
|
208
307
|
A help surface in front of the form, not a step in it. Everything here is
|
|
@@ -210,34 +309,35 @@ textarea { min-height: 84px; resize: vertical; }
|
|
|
210
309
|
leave, tap "this is happening to me too" and leave, or type straight past all
|
|
211
310
|
of it and file the report they came to file. Nothing is ever in their way,
|
|
212
311
|
because a reporter who feels handled rather than helped stops reporting. */
|
|
213
|
-
.known { margin-bottom:
|
|
312
|
+
.known { margin-bottom: 16px; }
|
|
214
313
|
.known .known-h {
|
|
215
|
-
margin: 0 0 8px; font-size:
|
|
314
|
+
margin: 0 0 8px; font-size: 11px; font-weight: 680; letter-spacing: .06em;
|
|
216
315
|
text-transform: uppercase; color: var(--bc-faint);
|
|
217
316
|
}
|
|
218
317
|
.known .issue {
|
|
219
|
-
display: flex; align-items:
|
|
318
|
+
display: flex; align-items: center; gap: 10px;
|
|
220
319
|
padding: 10px 12px; margin-bottom: 6px;
|
|
221
|
-
background: var(--bc-subtle); border: 1px solid
|
|
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;
|
|
222
322
|
}
|
|
223
|
-
.known .issue.is-joined { border-color: var(--bc-ok); }
|
|
323
|
+
.known .issue.is-joined { border-color: color-mix(in srgb, var(--bc-ok) 55%, var(--bc-border)); }
|
|
224
324
|
.known .issue .who { flex: 1; min-width: 0; }
|
|
225
325
|
.known .issue .t {
|
|
226
326
|
display: block; font-size: 13px; font-weight: 620; line-height: 1.4;
|
|
227
327
|
color: var(--bc-fg); overflow-wrap: anywhere;
|
|
228
328
|
}
|
|
229
329
|
.known .issue .meta {
|
|
230
|
-
display: block; margin-top: 3px; font-size:
|
|
330
|
+
display: block; margin-top: 3px; font-size: var(--bc-fs-xs); line-height: 1.45; color: var(--bc-muted);
|
|
231
331
|
}
|
|
232
332
|
.known .issue .join {
|
|
233
|
-
flex: none; padding:
|
|
333
|
+
flex: none; padding: 7px 12px; font-size: 12px; font-weight: 620;
|
|
234
334
|
color: var(--bc-accent); background: var(--bc-bg);
|
|
235
|
-
border: 1px solid var(--bc-
|
|
335
|
+
border: 1px solid var(--bc-accent-line); border-radius: 999px; cursor: pointer;
|
|
336
|
+
transition: background 160ms, border-color 160ms;
|
|
236
337
|
}
|
|
237
|
-
.known .issue .join:hover { border-color: var(--bc-accent); }
|
|
238
|
-
.known .issue .join:disabled { color: var(--bc-ok); border-color: var(--bc-border); cursor: default; }
|
|
239
|
-
.known .
|
|
240
|
-
.known .ask { display: flex; gap: 8px; margin-top: 8px; }
|
|
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; }
|
|
241
341
|
.known .ask input { flex: 1; min-width: 0; }
|
|
242
342
|
.known .ask button { flex: none; }
|
|
243
343
|
.known .err { margin: 6px 0 0; font-size: 12px; line-height: 1.45; color: var(--bc-danger); }
|
|
@@ -249,97 +349,117 @@ textarea { min-height: 84px; resize: vertical; }
|
|
|
249
349
|
strip is the whole panel. */
|
|
250
350
|
.whoami {
|
|
251
351
|
display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
|
|
252
|
-
margin-bottom:
|
|
253
|
-
font-size:
|
|
254
|
-
background: var(--bc-subtle); border-radius:
|
|
352
|
+
margin-bottom: 16px; padding: 10px 12px;
|
|
353
|
+
font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted);
|
|
354
|
+
background: var(--bc-subtle); border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
|
|
255
355
|
}
|
|
256
|
-
.whoami.asking { display: block; }
|
|
356
|
+
.whoami.asking { display: block; padding: 14px; }
|
|
257
357
|
.whoami .who { flex: 1; min-width: 0; }
|
|
258
358
|
.whoami .who strong { color: var(--bc-fg); font-weight: 620; word-break: break-all; }
|
|
259
|
-
.
|
|
260
|
-
padding: 0; font-size:
|
|
261
|
-
color: var(--bc-accent); background: none; border: none; cursor: pointer;
|
|
262
|
-
}
|
|
263
|
-
.
|
|
264
|
-
.
|
|
265
|
-
.whoami .
|
|
266
|
-
.whoami .why { margin: 0 0 10px; font-size: 12.5px; line-height: 1.5; color: var(--bc-muted); }
|
|
359
|
+
.link {
|
|
360
|
+
padding: 0; font: inherit; font-size: var(--bc-fs-sm); font-weight: 620; line-height: 1.45;
|
|
361
|
+
color: var(--bc-accent); background: none; border: none; cursor: pointer; border-radius: 4px;
|
|
362
|
+
}
|
|
363
|
+
.link:hover { text-decoration: underline; text-underline-offset: 2px; }
|
|
364
|
+
.link:disabled { color: var(--bc-faint); cursor: default; text-decoration: none; }
|
|
365
|
+
.whoami .why { margin: 0 0 10px; font-size: var(--bc-fs-sm); line-height: 1.5; color: var(--bc-muted); }
|
|
267
366
|
.whoami .why strong { color: var(--bc-fg); font-weight: 620; word-break: break-all; }
|
|
268
367
|
.whoami .row { display: flex; gap: 8px; align-items: stretch; }
|
|
269
368
|
.whoami .row input { flex: 1; min-width: 0; }
|
|
270
369
|
.whoami .row button { flex: none; }
|
|
271
|
-
.whoami .after { display: flex; gap:
|
|
370
|
+
.whoami .after { display: flex; gap: 14px; align-items: center; margin-top: 9px; }
|
|
272
371
|
.whoami .err { color: var(--bc-danger); }
|
|
273
372
|
.whoami .code { letter-spacing: .28em; font-variant-numeric: tabular-nums; }
|
|
274
373
|
|
|
275
374
|
/* ---------- which record is this about ----------
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
after Send has been pressed and only when what they wrote did not already
|
|
280
|
-
answer it, so most people never see this at all. */
|
|
375
|
+
Drawn only after Send has been pressed and only when what they wrote did not
|
|
376
|
+
already answer it, so most people never see this at all. Presented as the
|
|
377
|
+
assistant asking one friendly follow-up, not as a form field that appeared. */
|
|
281
378
|
.refs {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
379
|
+
position: relative;
|
|
380
|
+
margin-bottom: 16px; padding: 14px;
|
|
381
|
+
font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted);
|
|
382
|
+
background: var(--bc-accent-soft);
|
|
383
|
+
border: 1px solid var(--bc-accent-line); border-radius: var(--bc-r-lg);
|
|
384
|
+
animation: bc-item-in 280ms var(--bc-ease) both;
|
|
385
|
+
}
|
|
386
|
+
.refs .ask-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 11px; }
|
|
387
|
+
.refs .ask-head .avatar { width: 28px; height: 28px; font-size: 14px; box-shadow: none; }
|
|
388
|
+
.refs .ask-head strong { display: block; font-size: 13.5px; font-weight: 650; color: var(--bc-fg); margin-bottom: 1px; }
|
|
389
|
+
.refs .why { margin: 0; font-size: var(--bc-fs-sm); line-height: 1.5; color: var(--bc-muted); }
|
|
287
390
|
.refs .row { display: flex; gap: 8px; align-items: stretch; }
|
|
288
|
-
.refs .row input { flex: 1; min-width: 0; }
|
|
391
|
+
.refs .row input { flex: 1; min-width: 0; background: var(--bc-bg); }
|
|
289
392
|
.refs select {
|
|
290
393
|
flex: none; max-width: 45%; padding: 10px 10px; font-size: 13px;
|
|
291
|
-
color: var(--bc-fg); background: var(--bc-
|
|
292
|
-
border: 1px solid var(--bc-border); border-radius:
|
|
293
|
-
}
|
|
294
|
-
.refs select:focus {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
394
|
+
color: var(--bc-fg); background: var(--bc-bg);
|
|
395
|
+
border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
|
|
396
|
+
}
|
|
397
|
+
.refs select:focus { outline: none; border-color: var(--bc-accent); box-shadow: var(--bc-ring); }
|
|
398
|
+
.refs .chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 9px; }
|
|
399
|
+
.refs .chips button {
|
|
400
|
+
padding: 5px 11px; font: inherit; font-size: 12px; font-weight: 600; border-radius: 999px; cursor: pointer;
|
|
401
|
+
color: var(--bc-muted); background: var(--bc-bg); border: 1px solid var(--bc-border);
|
|
402
|
+
transition: background 160ms, color 160ms, border-color 160ms;
|
|
403
|
+
}
|
|
404
|
+
.refs .chips button[aria-pressed="true"] { color: var(--bc-on-accent); background: var(--bc-accent); border-color: var(--bc-accent); }
|
|
405
|
+
.refs .hint { margin: 8px 0 0; font-size: 12px; line-height: 1.45; color: var(--bc-muted); }
|
|
299
406
|
.refs .after { display: flex; gap: 12px; align-items: center; margin-top: 9px; }
|
|
300
|
-
.refs .err { color: var(--bc-danger); }
|
|
301
|
-
.refs .link {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
.
|
|
306
|
-
|
|
407
|
+
.refs .err { color: var(--bc-danger); font-weight: 550; }
|
|
408
|
+
.refs .link { margin-top: 10px; font-size: 12px; color: var(--bc-muted); }
|
|
409
|
+
.refs .link:hover { color: var(--bc-fg); }
|
|
410
|
+
.refs .chips.cands button { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 550; }
|
|
411
|
+
/* "We read your ID" — under the description, while they type. */
|
|
412
|
+
.ref-found {
|
|
413
|
+
display: flex; align-items: center; gap: 6px;
|
|
414
|
+
margin: -6px 0 14px; font-size: 12px; line-height: 1.45; color: var(--bc-muted);
|
|
415
|
+
}
|
|
416
|
+
.ref-found svg { width: 14px; height: 14px; flex: none; color: var(--bc-ok); }
|
|
417
|
+
.ref-found b { font-weight: 600; color: var(--bc-fg); }
|
|
307
418
|
|
|
308
419
|
/* ---------- severity ---------- */
|
|
309
|
-
.seg { display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px; padding: 3px; background: var(--bc-subtle); border-radius:
|
|
420
|
+
.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); }
|
|
310
421
|
.seg button {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
422
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
423
|
+
padding: 8px 4px; font-size: var(--bc-fs-sm); font-weight: 550; line-height: 1;
|
|
424
|
+
color: var(--bc-muted); background: none; border: none; border-radius: 9px;
|
|
425
|
+
cursor: pointer; transition: background 180ms var(--bc-ease), color 180ms, box-shadow 180ms;
|
|
426
|
+
}
|
|
427
|
+
.seg button::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--sev, var(--bc-faint)); opacity: .55; transition: opacity 180ms; }
|
|
428
|
+
.seg button[data-prio="LOW"] { --sev: #94a3b8; }
|
|
429
|
+
.seg button[data-prio="MEDIUM"] { --sev: #3b82f6; }
|
|
430
|
+
.seg button[data-prio="HIGH"] { --sev: #f59e0b; }
|
|
431
|
+
.seg button[data-prio="CRITICAL"] { --sev: #ef4444; }
|
|
315
432
|
.seg button:hover { color: var(--bc-fg); }
|
|
316
433
|
.seg button[aria-pressed=true] {
|
|
317
|
-
background: var(--bc-raised); color: var(--bc-
|
|
318
|
-
box-shadow: 0 1px 3px rgba(10,12,16,.14);
|
|
434
|
+
background: var(--bc-raised); color: var(--bc-fg); font-weight: 650;
|
|
435
|
+
box-shadow: 0 1px 3px rgba(10,12,16,.14), 0 0 0 1px color-mix(in srgb, var(--bc-border) 60%, transparent);
|
|
319
436
|
}
|
|
320
|
-
.seg button
|
|
437
|
+
.seg button[aria-pressed=true]::before { opacity: 1; }
|
|
438
|
+
.seg button:focus-visible { outline-offset: -2px; }
|
|
321
439
|
|
|
322
440
|
/* ---------- attachments ---------- */
|
|
323
|
-
.tools { display:
|
|
441
|
+
.tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 8px; }
|
|
324
442
|
.tool {
|
|
325
|
-
display: inline-flex; align-items: center; gap: 6px;
|
|
326
|
-
padding: 8px
|
|
443
|
+
display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
|
|
444
|
+
padding: 12px 8px; font-size: 12px; font-weight: 600; line-height: 1;
|
|
327
445
|
color: var(--bc-fg); background: var(--bc-elevated);
|
|
328
|
-
border: 1px
|
|
329
|
-
transition: border-color 160ms, background 160ms, transform 160ms var(--bc-ease);
|
|
446
|
+
border: 1px dashed color-mix(in srgb, var(--bc-fg) 20%, var(--bc-border)); border-radius: var(--bc-r-md); cursor: pointer;
|
|
447
|
+
transition: border-color 160ms, background 160ms, color 160ms, transform 160ms var(--bc-ease);
|
|
330
448
|
}
|
|
331
|
-
.tool svg { font-size:
|
|
332
|
-
.tool:hover:not(:disabled) { border-color: var(--bc-accent); transform: translateY(-1px); }
|
|
449
|
+
.tool svg { font-size: 19px; color: var(--bc-muted); transition: color 160ms; }
|
|
450
|
+
.tool:hover:not(:disabled) { border-color: var(--bc-accent); border-style: solid; background: var(--bc-accent-soft); transform: translateY(-1px); }
|
|
333
451
|
.tool:hover:not(:disabled) svg { color: var(--bc-accent); }
|
|
334
452
|
.tool:active:not(:disabled) { transform: translateY(0) scale(.98); }
|
|
335
|
-
.tool:focus-visible { outline: 2px solid var(--bc-accent); outline-offset: 1px; }
|
|
336
453
|
.tool:disabled { opacity: .45; cursor: not-allowed; }
|
|
454
|
+
.attach-hint { margin: 8px 0 0; font-size: var(--bc-fs-xs); color: var(--bc-faint); }
|
|
337
455
|
|
|
338
456
|
.thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
|
|
457
|
+
.thumbs:empty { display: none; }
|
|
339
458
|
.thumb {
|
|
340
|
-
position: relative; width:
|
|
341
|
-
border-radius:
|
|
459
|
+
position: relative; width: 76px; height: 76px; flex: none;
|
|
460
|
+
border-radius: var(--bc-r-md); overflow: hidden;
|
|
342
461
|
border: 1px solid var(--bc-border); background: var(--bc-subtle);
|
|
462
|
+
box-shadow: var(--bc-shadow-sm);
|
|
343
463
|
animation: bc-item-in 300ms var(--bc-ease) both;
|
|
344
464
|
}
|
|
345
465
|
.thumb img, .thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
@@ -350,11 +470,11 @@ textarea { min-height: 84px; resize: vertical; }
|
|
|
350
470
|
background: rgba(10,12,16,.72); color: #fff; font-size: 9.5px; font-weight: 600; line-height: 1.3;
|
|
351
471
|
}
|
|
352
472
|
.thumb .drop {
|
|
353
|
-
position: absolute; top:
|
|
354
|
-
width:
|
|
355
|
-
background: rgba(10,12,16,.
|
|
473
|
+
position: absolute; top: 4px; right: 4px;
|
|
474
|
+
width: 20px; height: 20px; font-size: 11px; border-radius: 50%;
|
|
475
|
+
background: rgba(10,12,16,.7); color: #fff; border: none;
|
|
356
476
|
display: grid; place-items: center; cursor: pointer;
|
|
357
|
-
opacity: 0; transition: opacity 160ms;
|
|
477
|
+
opacity: 0; transition: opacity 160ms, background 160ms;
|
|
358
478
|
}
|
|
359
479
|
.thumb:hover .drop, .thumb:focus-within .drop { opacity: 1; }
|
|
360
480
|
.thumb .drop:hover { background: var(--bc-danger); }
|
|
@@ -378,354 +498,538 @@ textarea { min-height: 84px; resize: vertical; }
|
|
|
378
498
|
|
|
379
499
|
/* ---------- messages ---------- */
|
|
380
500
|
.msg {
|
|
381
|
-
display: flex; align-items: flex-start; gap:
|
|
382
|
-
font-size:
|
|
501
|
+
display: flex; align-items: flex-start; gap: 8px;
|
|
502
|
+
font-size: var(--bc-fs-sm); line-height: 1.45; padding: 10px 12px; border-radius: var(--bc-r-md); margin-bottom: 14px;
|
|
383
503
|
animation: bc-item-in 240ms var(--bc-ease) both;
|
|
384
504
|
}
|
|
385
|
-
.msg
|
|
386
|
-
.msg.
|
|
505
|
+
.msg svg { flex: none; font-size: 15px; margin-top: 1px; }
|
|
506
|
+
.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); }
|
|
507
|
+
.msg.info { background: var(--bc-subtle); color: var(--bc-muted); border: 1px solid var(--bc-border); }
|
|
387
508
|
|
|
388
509
|
/* ---------- footer ---------- */
|
|
389
510
|
.foot {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
511
|
+
flex: none;
|
|
512
|
+
display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
|
|
513
|
+
padding: 12px var(--bc-pad); border-top: 1px solid var(--bc-border);
|
|
514
|
+
background: var(--bc-bg);
|
|
393
515
|
}
|
|
394
516
|
.foot .spacer { flex: 1; }
|
|
395
|
-
.foot .note { font-size:
|
|
517
|
+
.foot .note { flex-basis: 100%; order: -1; font-size: var(--bc-fs-xs); color: var(--bc-muted); }
|
|
518
|
+
.foot .note:empty { display: none; }
|
|
396
519
|
/* Quiet, but still legible: this sits in someone else's product, so it identifies
|
|
397
520
|
where the report goes without competing with their interface. Quiet is bought
|
|
398
521
|
with colour (--bc-brand-ink sits a step below body text) rather than with a
|
|
399
522
|
blanket opacity — the BUGCATCH half is only about 3px of cap height at this
|
|
400
523
|
size, and dimming it on top of that left it as a smudge in both themes. */
|
|
401
|
-
.
|
|
402
|
-
|
|
524
|
+
.powered {
|
|
525
|
+
flex: none; display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
526
|
+
padding: 7px var(--bc-pad) 9px; font-size: 10.5px; color: var(--bc-faint);
|
|
527
|
+
background: var(--bc-subtle); border-top: 1px solid var(--bc-border);
|
|
528
|
+
}
|
|
529
|
+
.brand-credit { display: flex; align-items: center; transition: color 0.15s; border-radius: 4px; }
|
|
530
|
+
.bc-brand { height: 16px; width: auto; display: block; color: var(--bc-brand-ink); }
|
|
403
531
|
.brand-credit:hover .bc-brand { color: var(--bc-fg); }
|
|
404
|
-
.brand-credit:focus-visible { outline: 2px solid var(--bc-accent); outline-offset: 3px; border-radius: 4px; }
|
|
405
532
|
button.ghost, button.primary {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
button.ghost
|
|
533
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 7px;
|
|
534
|
+
height: 38px; padding: 0 15px; font: inherit; font-size: 13px; font-weight: 620; line-height: 1;
|
|
535
|
+
border-radius: 999px; cursor: pointer; white-space: nowrap;
|
|
536
|
+
transition: transform 160ms var(--bc-ease), filter 160ms, background 160ms, border-color 160ms, color 160ms, box-shadow 160ms;
|
|
537
|
+
}
|
|
538
|
+
button.ghost { background: var(--bc-bg); border: 1px solid var(--bc-border); color: var(--bc-fg); }
|
|
539
|
+
button.ghost:hover:not(:disabled) { background: var(--bc-subtle); border-color: color-mix(in srgb, var(--bc-fg) 20%, var(--bc-border)); }
|
|
540
|
+
button.ghost svg, button.primary svg { font-size: 15px; }
|
|
412
541
|
button.primary {
|
|
413
|
-
|
|
542
|
+
padding: 0 18px;
|
|
414
543
|
background: var(--bc-accent); border: 1px solid var(--bc-accent); color: var(--bc-on-accent);
|
|
544
|
+
box-shadow: 0 4px 12px color-mix(in srgb, var(--bc-accent) 30%, transparent);
|
|
415
545
|
}
|
|
416
546
|
button.primary:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.07); }
|
|
417
547
|
button.primary:active:not(:disabled) { transform: none; }
|
|
418
|
-
button.ghost:focus-visible, button.primary:focus-visible { outline: 2px solid var(--bc-accent); outline-offset: 2px; }
|
|
419
548
|
button:disabled { opacity: .5; cursor: not-allowed; }
|
|
420
|
-
button.primary
|
|
549
|
+
button.primary:disabled { box-shadow: none; }
|
|
550
|
+
button.primary .spinner { border-color: rgba(255,255,255,.4); border-top-color: #fff; width: 14px; height: 14px; }
|
|
551
|
+
button.quiet {
|
|
552
|
+
padding: 0 4px; height: 38px; font: inherit; font-size: 13px; font-weight: 600;
|
|
553
|
+
border: none; background: none; color: var(--bc-muted); cursor: pointer; border-radius: 8px;
|
|
554
|
+
}
|
|
555
|
+
button.quiet:hover { color: var(--bc-accent); }
|
|
421
556
|
|
|
422
|
-
/* ---------- success ---------- */
|
|
423
557
|
/* ---------- your reports ---------- */
|
|
424
|
-
.history {
|
|
558
|
+
.history {
|
|
559
|
+
flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
|
|
560
|
+
padding: 8px 12px 14px;
|
|
561
|
+
}
|
|
425
562
|
/* On the conversation screen the inner thread does the scrolling, so this
|
|
426
563
|
wrapper must not — two nested scrollers means the composer drifts out of
|
|
427
564
|
reach exactly when someone reaches for it. */
|
|
428
565
|
.history.chatting {
|
|
429
|
-
|
|
566
|
+
overflow: hidden; display: flex; flex-direction: column; padding: 0;
|
|
430
567
|
}
|
|
431
568
|
.history .row {
|
|
432
|
-
display: flex; align-items:
|
|
433
|
-
padding:
|
|
569
|
+
display: flex; align-items: center; gap: 12px;
|
|
570
|
+
padding: 12px 10px; border-bottom: 1px solid var(--bc-border);
|
|
434
571
|
}
|
|
435
572
|
.history .row:last-child { border-bottom: 0; }
|
|
436
573
|
.history .who { flex: 1; min-width: 0; }
|
|
437
574
|
.history .ttl {
|
|
438
|
-
display: block; font-size:
|
|
575
|
+
display: block; font-size: 13.5px; font-weight: 600; color: var(--bc-fg);
|
|
439
576
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
440
577
|
}
|
|
441
|
-
.history .sub { display: block; margin-top:
|
|
442
|
-
.history .
|
|
578
|
+
.history .sub { display: block; margin-top: 3px; font-size: var(--bc-fs-xs); color: var(--bc-faint); }
|
|
579
|
+
.history .row-ico {
|
|
580
|
+
flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px;
|
|
581
|
+
background: var(--bc-subtle); color: var(--bc-muted); font-size: 16px;
|
|
582
|
+
}
|
|
583
|
+
.history .chev { flex: none; color: var(--bc-faint); font-size: 15px; transition: transform 160ms var(--bc-ease), color 160ms; }
|
|
584
|
+
.empty {
|
|
585
|
+
display: flex; flex-direction: column; align-items: center; gap: 10px;
|
|
586
|
+
padding: 40px 16px; text-align: center; font-size: var(--bc-fs-sm); color: var(--bc-muted);
|
|
587
|
+
}
|
|
588
|
+
.empty .empty-ico {
|
|
589
|
+
display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
|
|
590
|
+
background: var(--bc-accent-soft); color: var(--bc-accent); font-size: 20px;
|
|
591
|
+
}
|
|
592
|
+
.skel {
|
|
593
|
+
display: block; height: 12px; border-radius: 6px; margin: 6px 0;
|
|
594
|
+
background: linear-gradient(90deg, var(--bc-subtle) 25%, color-mix(in srgb, var(--bc-fg) 8%, var(--bc-subtle)) 50%, var(--bc-subtle) 75%);
|
|
595
|
+
background-size: 200% 100%; animation: bc-shimmer 1.4s linear infinite;
|
|
596
|
+
}
|
|
443
597
|
/* A history row is a button now — it opens the report — so the UA styling has
|
|
444
598
|
to go, and the hit area has to read as one. */
|
|
445
599
|
.history button.row {
|
|
446
|
-
width: 100%; background: none; border: 0;
|
|
600
|
+
width: 100%; background: none; border: 0; margin-bottom: 2px;
|
|
447
601
|
font: inherit; color: inherit; text-align: left; cursor: pointer;
|
|
448
|
-
border-radius:
|
|
602
|
+
border-radius: var(--bc-r-md); transition: background 160ms;
|
|
603
|
+
animation: bc-rise 260ms var(--bc-ease) both;
|
|
449
604
|
}
|
|
450
605
|
.history button.row:hover { background: var(--bc-subtle); }
|
|
451
|
-
.history button.row:
|
|
606
|
+
.history button.row:hover .chev { transform: translateX(2px); color: var(--bc-accent); }
|
|
607
|
+
.history button.row:focus-visible { outline-offset: -2px; }
|
|
452
608
|
|
|
453
609
|
/* ---------- one report, and what happened to it ---------- */
|
|
454
610
|
.report-head {
|
|
455
|
-
|
|
456
|
-
|
|
611
|
+
flex: none;
|
|
612
|
+
display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: center;
|
|
613
|
+
padding: 10px var(--bc-pad); border-bottom: 1px solid var(--bc-border);
|
|
614
|
+
background: var(--bc-bg);
|
|
457
615
|
}
|
|
458
|
-
.report-head .ttl { font-size:
|
|
459
|
-
.report-head .sub { font-size:
|
|
460
|
-
.report-head .pill { grid-row: 1 / span 2; align-self: center; }
|
|
616
|
+
.report-head .ttl { font-size: 13px; font-weight: 620; color: var(--bc-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
617
|
+
.report-head .sub { font-size: var(--bc-fs-xs); color: var(--bc-faint); }
|
|
618
|
+
.report-head .pill { grid-row: 1 / span 2; grid-column: 2; align-self: center; }
|
|
461
619
|
|
|
462
620
|
/* The thread scrolls; the composer under it does not. Both live inside the
|
|
463
621
|
panel, which stops scrolling as a whole on this screen — a composer that
|
|
464
622
|
scrolls away is a composer people think isn't there. */
|
|
465
623
|
.thread {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
624
|
+
flex: 1 1 auto; min-height: 140px;
|
|
625
|
+
display: flex; flex-direction: column; gap: 4px;
|
|
626
|
+
padding: 14px var(--bc-pad) 12px; overflow-y: auto; overscroll-behavior: contain;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/* The day a run of messages happened on, as a centred label on a rule. */
|
|
630
|
+
.day-sep {
|
|
631
|
+
display: flex; align-items: center; gap: 10px; margin: 10px 0 6px;
|
|
632
|
+
font-size: 11px; font-weight: 620; letter-spacing: .02em; color: var(--bc-faint);
|
|
469
633
|
}
|
|
634
|
+
.day-sep::before, .day-sep::after { content: ''; flex: 1; height: 1px; background: var(--bc-border); }
|
|
470
635
|
|
|
471
636
|
/* A status change is not a message and is not drawn as one: no side, no
|
|
472
637
|
bubble, no author. Centred and quiet, the way every chat marks the things
|
|
473
638
|
that happened rather than the things that were said. */
|
|
474
639
|
.sys {
|
|
475
|
-
align-self: center; max-width: 90%; text-align: center;
|
|
476
|
-
|
|
640
|
+
align-self: center; max-width: 90%; text-align: center; margin: 6px 0;
|
|
641
|
+
padding: 4px 10px; border-radius: 999px; background: var(--bc-subtle);
|
|
642
|
+
font-size: var(--bc-fs-xs); line-height: 1.45; color: var(--bc-muted);
|
|
643
|
+
animation: bc-fade-in 240ms ease both;
|
|
477
644
|
}
|
|
478
645
|
.sys .dot {
|
|
479
646
|
display: inline-block; width: 6px; height: 6px; margin-right: 6px;
|
|
480
647
|
border-radius: 50%; background: var(--bc-muted); vertical-align: middle;
|
|
481
648
|
}
|
|
649
|
+
.sys .t { color: var(--bc-faint); }
|
|
482
650
|
.sys.k-status .dot { background: #4da3ff; }
|
|
483
651
|
.sys.k-severity .dot { background: #e8912a; }
|
|
484
652
|
.sys.k-assignment .dot { background: #8b7dff; }
|
|
485
653
|
.sys.k-filed .dot { background: var(--bc-faint); }
|
|
486
|
-
.icon-btn.back { margin-right: 2px; }
|
|
487
654
|
|
|
488
655
|
/* ---------- the conversation ----------
|
|
489
656
|
|
|
490
657
|
A status change and a message are different things and are drawn as such: a
|
|
491
658
|
change is a line on the rail above, a message is a bubble with a side. The
|
|
492
|
-
reporter's own words sit right and accented, the
|
|
493
|
-
the arrangement every chat has taught people to
|
|
494
|
-
|
|
495
|
-
.msg-row
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
659
|
+
reporter's own words sit right and accented, the other side's left beside an
|
|
660
|
+
avatar that says who it is — the arrangement every chat has taught people to
|
|
661
|
+
read without a legend. */
|
|
662
|
+
.msg-row, .typing-row {
|
|
663
|
+
display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: 8px;
|
|
664
|
+
max-width: 88%; margin-top: 10px;
|
|
665
|
+
animation: bc-rise 260ms var(--bc-ease) both;
|
|
666
|
+
}
|
|
667
|
+
.msg-row.cont { margin-top: 3px; }
|
|
668
|
+
.msg-row > *, .typing-row > * { grid-column: 2; }
|
|
669
|
+
/* Fixed rows, so the avatar lines up with the bubble whatever else the
|
|
670
|
+
message carries (a name above it, answers or a time below it). */
|
|
671
|
+
.msg-row > .msg-who, .typing-row > .msg-who { grid-row: 1; }
|
|
672
|
+
.msg-row > .bubble, .typing-row > .bubble { grid-row: 2; }
|
|
673
|
+
.msg-row > .quick { grid-row: 3; }
|
|
674
|
+
.msg-row > .msg-when { grid-row: 4; }
|
|
675
|
+
.msg-row > .av, .typing-row > .av { grid-column: 1; grid-row: 2; align-self: end; }
|
|
676
|
+
/* In a run of messages the avatar sits by the last bubble and the time under
|
|
677
|
+
it, the way a chat marks where one turn ends. */
|
|
678
|
+
.msg-row:has(+ .msg-row.cont) > .av { visibility: hidden; }
|
|
679
|
+
.msg-row:has(+ .msg-row.cont) > .msg-when { display: none; }
|
|
680
|
+
.msg-row.theirs, .typing-row { align-self: flex-start; }
|
|
681
|
+
.msg-row.mine { align-self: flex-end; grid-template-columns: minmax(0, 1fr); justify-items: end; }
|
|
682
|
+
.msg-row.mine > * { grid-column: 1; }
|
|
683
|
+
.av {
|
|
684
|
+
display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
|
|
685
|
+
font-size: 11px; font-weight: 700; letter-spacing: .02em;
|
|
686
|
+
background: color-mix(in srgb, var(--bc-fg) 10%, var(--bc-bg)); color: var(--bc-fg);
|
|
687
|
+
}
|
|
688
|
+
.av.bot {
|
|
689
|
+
background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));
|
|
690
|
+
color: var(--bc-on-accent); font-size: 14px;
|
|
691
|
+
}
|
|
692
|
+
.msg-who { font-size: var(--bc-fs-xs); font-weight: 620; color: var(--bc-muted); padding: 0 4px 4px; }
|
|
693
|
+
.msg-who.auto { color: var(--bc-accent); display: inline-flex; align-items: center; gap: 5px; }
|
|
694
|
+
.msg-who .tag {
|
|
695
|
+
padding: 1px 6px; border-radius: 999px; font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
|
|
696
|
+
text-transform: uppercase; background: var(--bc-accent-soft); color: var(--bc-accent);
|
|
697
|
+
}
|
|
499
698
|
.bubble {
|
|
500
|
-
|
|
699
|
+
justify-self: start; max-width: 100%;
|
|
700
|
+
padding: 9px 13px; border-radius: 18px; font-size: 13.5px; line-height: 1.5;
|
|
501
701
|
background: var(--bc-subtle); color: var(--bc-fg);
|
|
502
702
|
border: 1px solid var(--bc-border);
|
|
703
|
+
border-bottom-left-radius: 6px;
|
|
503
704
|
}
|
|
504
705
|
/* pre-wrap belongs to the text, not the box — on the box it would also render
|
|
505
706
|
the markup's own indentation. */
|
|
506
707
|
.msg-text { display: block; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
507
708
|
.msg-row.mine .bubble {
|
|
709
|
+
justify-self: end;
|
|
508
710
|
background: var(--bc-accent); border-color: var(--bc-accent); color: var(--bc-on-accent);
|
|
509
|
-
border-bottom-right-radius:
|
|
510
|
-
}
|
|
511
|
-
.msg-row.theirs .bubble { border-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
so it reads the same to someone who cannot see the
|
|
515
|
-
.msg-row.auto .bubble { border-
|
|
516
|
-
.msg-who.auto { color: var(--bc-accent); }
|
|
711
|
+
border-bottom-left-radius: 18px; border-bottom-right-radius: 6px;
|
|
712
|
+
}
|
|
713
|
+
.msg-row.cont.theirs .bubble { border-top-left-radius: 6px; }
|
|
714
|
+
.msg-row.cont.mine .bubble { border-top-right-radius: 6px; }
|
|
715
|
+
/* A reply nobody wrote: tinted with the accent and labelled "Assistant" above
|
|
716
|
+
it, so it reads the same to someone who cannot see the colour at all. */
|
|
717
|
+
.msg-row.auto .bubble { background: var(--bc-accent-soft); border-color: var(--bc-accent-line); }
|
|
517
718
|
/* An attachment-only message has no text to wrap, so the bubble would be an
|
|
518
719
|
empty coloured box around it. */
|
|
519
|
-
.bubble.silent { padding: 0; background: none; border: 0; }
|
|
520
|
-
.msg-when { font-size: 10.5px; color: var(--bc-faint); padding: 0
|
|
521
|
-
.msg-row.pending { opacity: .
|
|
720
|
+
.bubble.silent { padding: 0; background: none !important; border: 0; }
|
|
721
|
+
.msg-when { font-size: 10.5px; color: var(--bc-faint); padding: 4px 4px 0; }
|
|
722
|
+
.msg-row.pending { opacity: .6; }
|
|
723
|
+
.msg-row.pending .msg-when::after { content: ' · Sending…'; }
|
|
522
724
|
.msg-row.failed .bubble { border: 1px solid var(--bc-danger); }
|
|
725
|
+
.msg-row.failed .msg-when { color: var(--bc-danger); }
|
|
726
|
+
.msg-row.failed .msg-when::after { content: ' · Not sent'; }
|
|
727
|
+
|
|
728
|
+
/* The assistant, thinking. Three dots in a bubble where its next message will be. */
|
|
729
|
+
.typing { display: inline-flex; align-items: center; gap: 4px; padding: 12px 14px; justify-self: start; }
|
|
730
|
+
.typing i {
|
|
731
|
+
width: 7px; height: 7px; border-radius: 50%; background: var(--bc-accent); opacity: .45;
|
|
732
|
+
animation: bc-typing 1.2s ease-in-out infinite;
|
|
733
|
+
}
|
|
734
|
+
.typing i:nth-child(2) { animation-delay: .15s; }
|
|
735
|
+
.typing i:nth-child(3) { animation-delay: .3s; }
|
|
736
|
+
.typing-row .msg-when { padding-top: 4px; }
|
|
523
737
|
|
|
524
738
|
.att { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
|
|
525
739
|
.bubble.silent .att { margin-top: 0; }
|
|
526
740
|
.att img {
|
|
527
|
-
display: block; max-width: 100%; max-height: 190px; border-radius:
|
|
741
|
+
display: block; max-width: 100%; max-height: 190px; border-radius: var(--bc-r-md);
|
|
528
742
|
border: 1px solid var(--bc-border); object-fit: cover; cursor: zoom-in;
|
|
529
743
|
}
|
|
530
744
|
.att audio { width: 232px; max-width: 100%; height: 34px; }
|
|
531
|
-
.att video { max-width: 100%; max-height: 190px; border-radius:
|
|
745
|
+
.att video { max-width: 100%; max-height: 190px; border-radius: var(--bc-r-md); }
|
|
532
746
|
.att .file {
|
|
533
747
|
display: flex; align-items: center; gap: 8px; text-decoration: none;
|
|
534
|
-
padding: 8px 10px; border-radius:
|
|
748
|
+
padding: 8px 10px; border-radius: 10px; font-size: 12px;
|
|
535
749
|
background: var(--bc-elevated); border: 1px solid var(--bc-border); color: var(--bc-fg);
|
|
536
750
|
}
|
|
537
751
|
.att .file:hover { border-color: var(--bc-accent); }
|
|
538
752
|
.att .file .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
539
753
|
.att .file .sz { flex: none; color: var(--bc-faint); font-size: 11px; }
|
|
540
754
|
|
|
541
|
-
/*
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
.
|
|
546
|
-
.
|
|
755
|
+
/* ---------- the assistant's live card ----------
|
|
756
|
+
A status header pinned between the report and the conversation: where the
|
|
757
|
+
investigation is, as words and as a stepper. It does not scroll with the
|
|
758
|
+
thread, because "is anything happening?" is the question on screen the
|
|
759
|
+
whole time. */
|
|
760
|
+
.assistant {
|
|
761
|
+
flex: none; position: relative; overflow: hidden;
|
|
762
|
+
margin: 10px 12px 0; padding: 12px 14px 12px;
|
|
763
|
+
border-radius: var(--bc-r-lg); border: 1px solid var(--bc-border);
|
|
764
|
+
background: var(--bc-subtle);
|
|
765
|
+
transition: background 400ms ease, border-color 400ms ease;
|
|
766
|
+
}
|
|
767
|
+
.assistant.analyzing, .assistant.confirming, .assistant.needs_info {
|
|
768
|
+
background: var(--bc-accent-soft); border-color: var(--bc-accent-line);
|
|
769
|
+
}
|
|
770
|
+
.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)); }
|
|
771
|
+
/* On a call: the call's own card is what they are watching. This one states
|
|
772
|
+
what is happening and stops moving — no spinner, no travelling bar. */
|
|
773
|
+
.assistant.calling { background: var(--bc-bg); }
|
|
774
|
+
.assistant.calling .as-progress i { transition: none; }
|
|
775
|
+
.as-body.entering { animation: bc-rise 360ms var(--bc-ease) both; }
|
|
776
|
+
.as-top { display: flex; gap: 11px; align-items: center; }
|
|
777
|
+
.as-orb {
|
|
778
|
+
flex: none; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
|
|
779
|
+
color: var(--bc-on-accent); font-size: 15px;
|
|
780
|
+
background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));
|
|
781
|
+
}
|
|
782
|
+
.assistant.resolved .as-orb { background: var(--bc-ok); }
|
|
783
|
+
.assistant.reviewing .as-orb { background: color-mix(in srgb, var(--bc-fg) 55%, var(--bc-bg)); }
|
|
784
|
+
.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; }
|
|
547
785
|
@keyframes as-rot { to { transform: rotate(360deg); } }
|
|
548
|
-
.as-
|
|
549
|
-
.
|
|
550
|
-
|
|
551
|
-
.as-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
.as-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
786
|
+
.as-text { min-width: 0; flex: 1; }
|
|
787
|
+
.as-title { font-weight: 650; font-size: 13.5px; letter-spacing: -.01em; color: var(--bc-fg); }
|
|
788
|
+
.as-detail { font-size: 12px; line-height: 1.45; color: var(--bc-muted); margin-top: 2px; }
|
|
789
|
+
.as-progress {
|
|
790
|
+
height: 4px; margin: 12px 0 0; border-radius: 999px; overflow: hidden;
|
|
791
|
+
background: color-mix(in srgb, var(--bc-fg) 9%, transparent);
|
|
792
|
+
}
|
|
793
|
+
.as-progress i {
|
|
794
|
+
display: block; height: 100%; border-radius: inherit; background: var(--bc-accent);
|
|
795
|
+
transition: width 700ms var(--bc-ease);
|
|
796
|
+
}
|
|
797
|
+
.assistant.resolved .as-progress i { background: var(--bc-ok); }
|
|
798
|
+
.assistant.analyzing .as-progress i {
|
|
799
|
+
background: linear-gradient(90deg, var(--bc-accent) 0%, color-mix(in srgb, var(--bc-accent) 45%, #fff) 50%, var(--bc-accent) 100%);
|
|
800
|
+
background-size: 200% 100%; animation: bc-shimmer 1.6s linear infinite;
|
|
801
|
+
}
|
|
802
|
+
.as-steps {
|
|
803
|
+
list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
|
|
804
|
+
margin: 8px 0 0; padding: 0; font-size: 10.5px; color: var(--bc-faint);
|
|
805
|
+
}
|
|
806
|
+
.as-steps li { display: flex; align-items: center; gap: 5px; min-width: 0; white-space: nowrap; }
|
|
807
|
+
.as-steps li .lbl { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
808
|
+
.as-steps li .dot { flex: none; width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid currentColor; }
|
|
809
|
+
.as-steps li.done { color: var(--bc-muted); }
|
|
810
|
+
.as-steps li.done .dot { background: var(--bc-accent); border-color: var(--bc-accent); }
|
|
811
|
+
.assistant.resolved .as-steps li.done .dot { background: var(--bc-ok); border-color: var(--bc-ok); }
|
|
812
|
+
.as-steps li.active { color: var(--bc-fg); font-weight: 650; }
|
|
813
|
+
.as-steps li.active .dot { border-color: var(--bc-accent); background: var(--bc-bg); animation: as-pulse 1.2s ease-in-out infinite; }
|
|
559
814
|
@keyframes as-pulse { 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--bc-accent) 25%, transparent); } }
|
|
560
|
-
@media (prefers-reduced-motion: reduce) { .as-spin, .as-steps li.active
|
|
815
|
+
@media (prefers-reduced-motion: reduce) { .as-spin, .as-steps li.active .dot, .assistant.analyzing .as-progress i, .typing i { animation: none; } }
|
|
561
816
|
|
|
562
817
|
/* Quick answers under the confirm question — only on the latest message. */
|
|
563
|
-
.msg-row .quick { display: none; gap: 6px; flex-wrap: wrap; margin-top:
|
|
818
|
+
.msg-row .quick { display: none; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
|
|
564
819
|
.msg-row:last-child .quick { display: flex; }
|
|
565
820
|
.quick button {
|
|
566
|
-
|
|
567
|
-
|
|
821
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
822
|
+
font: inherit; font-size: 13px; font-weight: 600; padding: 8px 14px; border-radius: 999px; cursor: pointer;
|
|
823
|
+
border: 1px solid var(--bc-accent); background: var(--bc-bg); color: var(--bc-accent);
|
|
824
|
+
transition: background 160ms, color 160ms, transform 160ms var(--bc-ease);
|
|
825
|
+
animation: bc-item-in 300ms var(--bc-ease) both;
|
|
568
826
|
}
|
|
569
|
-
.quick button:
|
|
827
|
+
.quick button:first-child { background: var(--bc-accent); color: var(--bc-on-accent); }
|
|
828
|
+
.quick button:nth-child(2) { animation-delay: 60ms; }
|
|
829
|
+
.quick button svg { font-size: 13px; }
|
|
830
|
+
.quick button:hover { transform: translateY(-1px); background: var(--bc-accent-soft); }
|
|
831
|
+
.quick button:first-child:hover { background: var(--bc-accent); filter: brightness(1.07); }
|
|
570
832
|
|
|
571
833
|
/* ---------- the composer ---------- */
|
|
572
834
|
.composer {
|
|
573
|
-
|
|
574
|
-
padding: 10px
|
|
835
|
+
flex: none; position: relative;
|
|
836
|
+
padding: 10px 12px max(12px, env(safe-area-inset-bottom, 0px));
|
|
575
837
|
/* The panel's own background, not --bc-elevated: the two are the same white
|
|
576
838
|
in light mode but differ in dark, where a raised composer reads as a band
|
|
577
839
|
laid over the panel rather than the foot of it. */
|
|
578
840
|
background: var(--bc-bg);
|
|
579
841
|
border-top: 1px solid var(--bc-border);
|
|
580
842
|
}
|
|
581
|
-
|
|
843
|
+
/* One rounded field holding the tools, the text and the send button — the
|
|
844
|
+
shape every chat box has, so nobody has to look for where to type. */
|
|
845
|
+
.composer .row {
|
|
846
|
+
display: flex; align-items: flex-end; gap: 4px;
|
|
847
|
+
padding: 5px; border-radius: 22px;
|
|
848
|
+
background: var(--bc-subtle); border: 1px solid var(--bc-border);
|
|
849
|
+
transition: border-color 160ms, box-shadow 160ms;
|
|
850
|
+
}
|
|
851
|
+
.composer .row:focus-within { border-color: var(--bc-accent); box-shadow: var(--bc-ring); }
|
|
582
852
|
.composer textarea {
|
|
583
|
-
flex: 1; min-height:
|
|
584
|
-
font: inherit; font-size:
|
|
585
|
-
color: var(--bc-fg); background:
|
|
586
|
-
border: 1px solid var(--bc-border); border-radius: 10px;
|
|
853
|
+
flex: 1; min-width: 0; min-height: 34px; max-height: 120px; resize: none; padding: 7px 6px;
|
|
854
|
+
font: inherit; font-size: 13.5px; line-height: 1.45;
|
|
855
|
+
color: var(--bc-fg); background: transparent; border: 0; border-radius: 0;
|
|
587
856
|
}
|
|
588
|
-
.composer textarea:focus {
|
|
589
|
-
.composer .tools { display: flex; gap:
|
|
857
|
+
.composer textarea:hover, .composer textarea:focus { border: 0; box-shadow: none; outline: none; }
|
|
858
|
+
.composer .tools { display: flex; gap: 0; grid-template-columns: none; }
|
|
590
859
|
.composer .tool {
|
|
591
|
-
width: 34px; height: 34px; display: grid; place-items: center; cursor: pointer;
|
|
592
|
-
border-radius:
|
|
860
|
+
width: 34px; height: 34px; min-height: 0; padding: 0; display: grid; place-items: center; cursor: pointer;
|
|
861
|
+
border-radius: 50%; border: 0; background: none; font-size: 17px;
|
|
593
862
|
color: var(--bc-muted);
|
|
594
863
|
}
|
|
595
|
-
.composer .tool
|
|
864
|
+
.composer .tool svg { font-size: 17px; }
|
|
865
|
+
.composer .tool:hover:not(:disabled) { background: color-mix(in srgb, var(--bc-fg) 8%, transparent); color: var(--bc-fg); border: 0; transform: none; }
|
|
866
|
+
.composer .tool:hover:not(:disabled) svg { color: var(--bc-fg); }
|
|
596
867
|
.composer .tool:disabled { opacity: .4; cursor: default; }
|
|
597
868
|
.composer .tool.recording { color: var(--bc-danger); background: color-mix(in srgb, var(--bc-danger) 12%, transparent); }
|
|
869
|
+
.composer .tool.recording svg { color: var(--bc-danger); }
|
|
598
870
|
.composer .send {
|
|
599
|
-
width: 34px; height: 34px; flex: none; border-radius:
|
|
600
|
-
background: var(--bc-accent); border:
|
|
871
|
+
width: 34px; height: 34px; flex: none; border-radius: 50%; cursor: pointer; font-size: 16px;
|
|
872
|
+
background: var(--bc-accent); border: 0; color: var(--bc-on-accent);
|
|
601
873
|
display: grid; place-items: center;
|
|
874
|
+
transition: transform 160ms var(--bc-ease), opacity 160ms, filter 160ms;
|
|
602
875
|
}
|
|
603
|
-
.composer .send:disabled {
|
|
876
|
+
.composer .send:hover:not(:disabled) { filter: brightness(1.08); transform: scale(1.05); }
|
|
877
|
+
.composer .send:disabled { opacity: .35; cursor: default; background: var(--bc-faint); }
|
|
604
878
|
.composer .staged { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 0 8px; }
|
|
605
879
|
.composer .chip {
|
|
606
|
-
display: flex; align-items: center; gap: 6px; max-width:
|
|
607
|
-
padding: 5px 7px; border-radius:
|
|
880
|
+
display: flex; align-items: center; gap: 6px; max-width: 200px;
|
|
881
|
+
padding: 5px 7px 5px 10px; border-radius: 999px; font-size: var(--bc-fs-xs);
|
|
608
882
|
background: var(--bc-subtle); border: 1px solid var(--bc-border); color: var(--bc-muted);
|
|
883
|
+
animation: bc-item-in 240ms var(--bc-ease) both;
|
|
609
884
|
}
|
|
610
|
-
.composer .chip .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
885
|
+
.composer .chip .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--bc-fg); }
|
|
611
886
|
.composer .chip button {
|
|
612
|
-
flex: none; width:
|
|
613
|
-
border: 0; border-radius:
|
|
887
|
+
flex: none; width: 18px; height: 18px; display: grid; place-items: center;
|
|
888
|
+
border: 0; border-radius: 50%; background: none; color: var(--bc-faint); cursor: pointer;
|
|
614
889
|
}
|
|
615
890
|
.composer .chip button:hover { color: var(--bc-danger); }
|
|
616
891
|
.composer .chip.uploading { border-style: dashed; }
|
|
617
|
-
.composer .note { padding: 6px
|
|
892
|
+
.composer .note { padding: 6px 6px 0; font-size: var(--bc-fs-xs); color: var(--bc-muted); }
|
|
618
893
|
.composer .note.err { color: var(--bc-danger); }
|
|
619
894
|
.reccount { font-variant-numeric: tabular-nums; }
|
|
620
895
|
|
|
621
896
|
/* Status carries the only colour in this list, so it is the thing the eye
|
|
622
897
|
lands on — which is what someone opening this screen came to check. */
|
|
623
898
|
.pill {
|
|
624
|
-
flex: none;
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
.pill
|
|
630
|
-
.pill.
|
|
631
|
-
.pill.
|
|
899
|
+
flex: none; display: inline-flex; align-items: center; gap: 5px;
|
|
900
|
+
font-size: 11px; font-weight: 650;
|
|
901
|
+
padding: 3px 9px; border-radius: 999px;
|
|
902
|
+
color: var(--bc-muted); background: var(--bc-subtle);
|
|
903
|
+
}
|
|
904
|
+
.pill::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
|
905
|
+
.pill.open { color: #c2410c; background: color-mix(in srgb, #f59e0b 14%, var(--bc-bg)); }
|
|
906
|
+
.pill.in_progress { color: #1d6fd1; background: color-mix(in srgb, #3b82f6 14%, var(--bc-bg)); }
|
|
907
|
+
.pill.resolved { color: #0f8a5a; background: color-mix(in srgb, #10b981 14%, var(--bc-bg)); }
|
|
908
|
+
.pill.closed { color: var(--bc-muted); }
|
|
909
|
+
:host(.bc-dark) .pill.open { color: #fbbf24; }
|
|
910
|
+
:host(.bc-dark) .pill.in_progress { color: #7cb8ff; }
|
|
911
|
+
:host(.bc-dark) .pill.resolved { color: #4ade9a; }
|
|
912
|
+
@media (prefers-color-scheme: dark) {
|
|
913
|
+
:host(.bc-auto) .pill.open { color: #fbbf24; }
|
|
914
|
+
:host(.bc-auto) .pill.in_progress { color: #7cb8ff; }
|
|
915
|
+
:host(.bc-auto) .pill.resolved { color: #4ade9a; }
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/* ---------- does the fix work ---------- */
|
|
919
|
+
.fix-verification {
|
|
920
|
+
flex: none; margin: 10px 12px 0; padding: 12px 14px;
|
|
921
|
+
border-radius: var(--bc-r-lg); border: 1px solid color-mix(in srgb, var(--bc-ok) 35%, var(--bc-border));
|
|
922
|
+
background: color-mix(in srgb, var(--bc-ok) 8%, var(--bc-bg));
|
|
923
|
+
font-size: var(--bc-fs-sm); color: var(--bc-fg);
|
|
924
|
+
animation: bc-rise 280ms var(--bc-ease) both;
|
|
925
|
+
}
|
|
926
|
+
.fix-verification p { margin: 0 0 8px; font-weight: 620; }
|
|
927
|
+
.fix-verification textarea { min-height: 56px; margin-bottom: 8px; font-size: 13px; background: var(--bc-bg); }
|
|
928
|
+
.fix-verification button {
|
|
929
|
+
padding: 7px 13px; font: inherit; font-size: 12.5px; font-weight: 620; border-radius: 999px; cursor: pointer;
|
|
930
|
+
border: 1px solid var(--bc-border); background: var(--bc-bg); color: var(--bc-fg);
|
|
931
|
+
}
|
|
932
|
+
.fix-verification button[data-result="CONFIRMED"] { background: var(--bc-ok); border-color: var(--bc-ok); color: #fff; }
|
|
933
|
+
.fix-verification .feedback-status { margin: 6px 0 0; font-weight: 450; color: var(--bc-danger); font-size: var(--bc-fs-xs); }
|
|
934
|
+
.fix-verification .feedback-status:empty { display: none; }
|
|
632
935
|
|
|
633
936
|
/* A call in progress, in place of the form.
|
|
634
937
|
---------------------------------------------------------------------------
|
|
635
938
|
Deliberately not styled as a chat. Nothing arrives here to read — the
|
|
636
939
|
conversation is happening out loud — so this is a state, a line of
|
|
637
940
|
reassurance about the report, and a way to hang up. */
|
|
638
|
-
.call { padding:
|
|
639
|
-
.call-head
|
|
640
|
-
.call-head
|
|
941
|
+
.call { display: flex; flex-direction: column; padding: 26px 0 0; animation: bc-fade-in 240ms ease both; }
|
|
942
|
+
.call-head, .call-note { padding: 0 var(--bc-pad); }
|
|
943
|
+
.call-head { display: flex; align-items: center; gap: 9px; margin-bottom: 8px; }
|
|
944
|
+
.call-head strong { font-size: 15.5px; font-weight: 680; letter-spacing: -.01em; }
|
|
641
945
|
.call-dot {
|
|
642
|
-
width:
|
|
946
|
+
width: 9px; height: 9px; border-radius: 50%; background: var(--bc-accent);
|
|
643
947
|
animation: bc-call-pulse 1.6s ease-in-out infinite;
|
|
644
948
|
}
|
|
645
949
|
.call-dot.off { background: var(--bc-faint); animation: none; }
|
|
646
950
|
@keyframes bc-call-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
|
|
647
951
|
/* Said while it is still ringing, because that is when somebody is deciding
|
|
648
952
|
whether it is worth waiting. */
|
|
649
|
-
.call-note { margin: 0 0
|
|
953
|
+
.call-note { margin: 0 0 18px; font-size: 13px; color: var(--bc-muted); line-height: 1.55; }
|
|
650
954
|
|
|
651
955
|
/* ---------- the same call, on top of the report it is about ---------- */
|
|
652
956
|
/* A bar rather than its own screen, unlike the one the composer opens. The
|
|
653
957
|
thread underneath is exactly what both people are talking about, so taking it
|
|
654
958
|
off screen would be taking away the only shared reference. */
|
|
655
959
|
.livecall {
|
|
960
|
+
flex: none;
|
|
656
961
|
display: flex; align-items: center; gap: 9px;
|
|
657
|
-
margin: 12px 0
|
|
658
|
-
border: 1px solid var(--bc-
|
|
659
|
-
background: var(--bc-
|
|
962
|
+
margin: 10px 12px 0; padding: 9px 11px;
|
|
963
|
+
border: 1px solid var(--bc-accent-line); border-radius: var(--bc-r-md);
|
|
964
|
+
background: var(--bc-accent-soft); font-size: var(--bc-fs-sm); color: var(--bc-fg);
|
|
965
|
+
animation: bc-rise 240ms var(--bc-ease) both;
|
|
660
966
|
}
|
|
661
967
|
.livecall .who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
968
|
+
.livecall .call-kept { font-size: 11px; opacity: .75; }
|
|
662
969
|
.livecall .secs { font-variant-numeric: tabular-nums; color: var(--bc-muted); }
|
|
663
970
|
.livecall .act {
|
|
664
|
-
padding: 5px
|
|
665
|
-
background: var(--bc-
|
|
666
|
-
font: inherit; font-size: 12px; cursor: pointer;
|
|
971
|
+
padding: 5px 11px; border-radius: 999px; border: 1px solid var(--bc-border);
|
|
972
|
+
background: var(--bc-bg); color: var(--bc-fg);
|
|
973
|
+
font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
|
|
667
974
|
}
|
|
668
975
|
.livecall .act:hover { background: var(--bc-subtle); }
|
|
669
976
|
.livecall .act.danger { border-color: transparent; background: var(--bc-danger); color: #fff; }
|
|
670
|
-
.livecall.over { color: var(--bc-muted); }
|
|
977
|
+
.livecall.over { color: var(--bc-muted); background: var(--bc-subtle); border-color: var(--bc-border); }
|
|
671
978
|
.call-note code { font-size: 11px; }
|
|
672
979
|
@media (prefers-reduced-motion: reduce) { .call-dot { animation: none; } }
|
|
673
980
|
|
|
674
|
-
.done
|
|
981
|
+
/* div.done, not .done: the assistant's stepper marks finished steps li.done,
|
|
982
|
+
and this screen's padding was landing on every one of them. */
|
|
983
|
+
div.done { padding: 44px 26px 44px; text-align: center; animation: bc-fade-in 240ms ease both; }
|
|
675
984
|
.done .tick {
|
|
676
|
-
width:
|
|
677
|
-
display: grid; place-items: center; font-size:
|
|
985
|
+
width: 56px; height: 56px; margin: 0 auto 16px; border-radius: 50%;
|
|
986
|
+
display: grid; place-items: center; font-size: 26px;
|
|
678
987
|
background: color-mix(in srgb, var(--bc-ok) 14%, transparent); color: var(--bc-ok);
|
|
988
|
+
box-shadow: 0 0 0 8px color-mix(in srgb, var(--bc-ok) 6%, transparent);
|
|
679
989
|
animation: bc-pop 420ms var(--bc-ease) both;
|
|
680
990
|
}
|
|
681
|
-
.done h3 { margin: 0 0
|
|
682
|
-
.done p { margin: 0; font-size:
|
|
683
|
-
.done code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
|
|
991
|
+
.done h3 { margin: 0 0 6px; font-size: 17px; font-weight: 680; letter-spacing: -.015em; }
|
|
992
|
+
.done p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--bc-muted); }
|
|
993
|
+
.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); }
|
|
684
994
|
/* The likely wait. Set apart from the reference line above it by a rule rather
|
|
685
995
|
than by weight: one is bookkeeping and the other is the answer to the
|
|
686
996
|
question the reporter actually has, and they should not read as one
|
|
687
997
|
paragraph. */
|
|
688
998
|
.done .eta {
|
|
689
|
-
margin:
|
|
690
|
-
padding-top:
|
|
691
|
-
border-top: 1px solid var(--bc-
|
|
999
|
+
margin: 14px auto 0;
|
|
1000
|
+
padding-top: 12px;
|
|
1001
|
+
border-top: 1px solid var(--bc-border);
|
|
692
1002
|
max-width: 30ch;
|
|
693
1003
|
line-height: 1.5;
|
|
694
1004
|
}
|
|
695
|
-
.done .eta strong { color: var(--bc-
|
|
1005
|
+
.done .eta strong { color: var(--bc-fg); font-weight: 620; }
|
|
696
1006
|
/* The same figure on the report the reporter came back to. Sits under the
|
|
697
1007
|
status pill in the header block, muted — it is context, not the headline the
|
|
698
1008
|
status is. */
|
|
699
1009
|
.report-head .eta-line {
|
|
700
1010
|
grid-column: 1 / -1;
|
|
701
|
-
font-size:
|
|
1011
|
+
font-size: var(--bc-fs-xs);
|
|
702
1012
|
color: var(--bc-muted);
|
|
703
1013
|
margin-top: 2px;
|
|
704
1014
|
}
|
|
705
1015
|
|
|
706
1016
|
/* ---------- recording bar ---------- */
|
|
707
1017
|
.recbar {
|
|
708
|
-
position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
|
|
1018
|
+
position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%);
|
|
709
1019
|
z-index: 2147483002;
|
|
710
1020
|
display: flex; align-items: center; gap: 12px;
|
|
711
|
-
padding:
|
|
1021
|
+
padding: 8px 8px 8px 16px; border-radius: 999px;
|
|
712
1022
|
background: var(--bc-elevated); color: var(--bc-fg);
|
|
713
1023
|
border: 1px solid var(--bc-border); box-shadow: var(--bc-shadow);
|
|
714
1024
|
animation: bc-item-in 300ms var(--bc-ease) both;
|
|
715
1025
|
}
|
|
716
1026
|
.recbar .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--bc-danger); animation: bc-pulse 1.4s ease-in-out infinite; }
|
|
717
|
-
.recbar .time { font-variant-numeric: tabular-nums; font-size: 13px; font-weight:
|
|
1027
|
+
.recbar .time { font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 650; min-width: 38px; }
|
|
718
1028
|
.recbar .hint { font-size: 12px; color: var(--bc-muted); }
|
|
719
|
-
.recbar button { padding:
|
|
1029
|
+
.recbar button { height: 34px; padding: 0 14px; font-size: 12.5px; font-weight: 620; border-radius: 999px; cursor: pointer; }
|
|
720
1030
|
.recbar .stop { background: var(--bc-danger); border: 1px solid var(--bc-danger); color: #fff; }
|
|
721
1031
|
.recbar .stop:hover { filter: brightness(1.08); }
|
|
722
1032
|
|
|
723
|
-
@media (max-width: 520px) {
|
|
724
|
-
.panel { width: calc(100vw - 24px); max-width: none; }
|
|
725
|
-
.scrim.at-right .panel, .scrim.at-left .panel { left: 12px; right: 12px; }
|
|
726
|
-
.recbar .hint { display: none; }
|
|
727
|
-
}
|
|
728
|
-
|
|
729
1033
|
/* ---------- phones ----------------------------------------------------------
|
|
730
1034
|
This widget runs inside a shadow root on somebody else's site, so its own
|
|
731
1035
|
stylesheet is the whole story: the host page cannot correct any of this, and
|
|
@@ -750,6 +1054,32 @@ button.primary .spinner { border-color: rgba(255,255,255,.4); border-top-color:
|
|
|
750
1054
|
padding-left: env(safe-area-inset-left, 0px);
|
|
751
1055
|
}
|
|
752
1056
|
|
|
1057
|
+
/* Short desktop windows: the chat height gives way to the space there is. */
|
|
1058
|
+
@media (max-height: 820px) { .panel.is-chat { height: calc(100vh - var(--bc-dock-h, 84px) - 52px); } }
|
|
1059
|
+
|
|
1060
|
+
/* A phone gets the whole screen: a sheet that slides up from the bottom, with
|
|
1061
|
+
the notch and the home indicator kept clear. The dock steps aside while it
|
|
1062
|
+
is open — the sheet has its own close button, and a launcher floating over
|
|
1063
|
+
the send button would be in the way of the one thing that matters. */
|
|
1064
|
+
@media (max-width: 480px) {
|
|
1065
|
+
.scrim { background: rgba(10,12,16,.4); }
|
|
1066
|
+
.panel, .scrim.at-right .panel, .scrim.at-left .panel, .scrim.at-bottom .panel, .scrim.at-top .panel {
|
|
1067
|
+
inset: 0; width: 100%; max-width: none; height: 100%; max-height: none;
|
|
1068
|
+
border-radius: 0; border: 0;
|
|
1069
|
+
animation: bc-sheet-in 380ms var(--bc-ease) both;
|
|
1070
|
+
}
|
|
1071
|
+
@supports (height: 100dvh) { .panel { height: 100dvh; max-height: none; } }
|
|
1072
|
+
.panel.is-chat { height: 100%; }
|
|
1073
|
+
.scrim.closing .panel { animation: bc-sheet-out 200ms ease both; }
|
|
1074
|
+
.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)); }
|
|
1075
|
+
.foot, .powered { padding-bottom: max(10px, env(safe-area-inset-bottom, 0px)); }
|
|
1076
|
+
.foot + .powered { padding-bottom: max(9px, env(safe-area-inset-bottom, 0px)); }
|
|
1077
|
+
.foot:has(+ .powered) { padding-bottom: 12px; }
|
|
1078
|
+
.dock.over { display: none; }
|
|
1079
|
+
.recbar .hint { display: none; }
|
|
1080
|
+
.msg-row, .typing-row { max-width: 92%; }
|
|
1081
|
+
}
|
|
1082
|
+
|
|
753
1083
|
@media (pointer: coarse) {
|
|
754
1084
|
/* iOS zooms the whole page when a field it focuses is under 16px, and does
|
|
755
1085
|
not zoom back out. On the host's site that is not the widget's page to
|
|
@@ -761,70 +1091,51 @@ button.primary .spinner { border-color: rgba(255,255,255,.4); border-top-color:
|
|
|
761
1091
|
/* 44px is the floor a thumb hits reliably. Every control in this form was
|
|
762
1092
|
under it, on the one surface in the product designed for someone who is
|
|
763
1093
|
not an operator and gets no second attempt. */
|
|
764
|
-
.
|
|
765
|
-
.
|
|
766
|
-
.
|
|
767
|
-
|
|
1094
|
+
.seg button { min-height: 44px; }
|
|
1095
|
+
.tool { min-height: 56px; }
|
|
1096
|
+
button.primary, button.ghost { height: 44px; }
|
|
1097
|
+
.composer .send, .composer .tool { width: 40px; height: 40px; min-height: 0; }
|
|
768
1098
|
textarea { min-height: 104px; }
|
|
1099
|
+
.composer textarea { min-height: 40px; }
|
|
769
1100
|
|
|
770
|
-
/* The theme switch and the close button are
|
|
1101
|
+
/* The theme switch and the close button are small glyphs whose boxes cannot
|
|
771
1102
|
grow without moving the header text beside them, so each keeps its size
|
|
772
1103
|
and gets an invisible target drawn around it instead. */
|
|
773
|
-
.themer button, .head .icon-btn, .thumb .
|
|
774
|
-
.
|
|
1104
|
+
.themer button, .head .icon-btn, .thumb .drop, .brand-credit { position: relative; }
|
|
1105
|
+
.thumb .drop { position: absolute; opacity: 1; }
|
|
1106
|
+
.themer button::after, .head .icon-btn::after, .thumb .drop::after, .brand-credit::after {
|
|
775
1107
|
content: '';
|
|
776
1108
|
position: absolute;
|
|
777
|
-
inset: -
|
|
1109
|
+
inset: -8px;
|
|
778
1110
|
}
|
|
779
|
-
/* The close button is the one people reach for in a hurry, and it sits in
|
|
780
|
-
the corner where a thumb is least accurate. */
|
|
781
|
-
.head .icon-btn::after { inset: -12px; }
|
|
782
1111
|
/* The themer's three buttons sit side by side, so their expanders would
|
|
783
1112
|
overlap each other without a gap to grow into. */
|
|
784
1113
|
.themer { gap: 4px; }
|
|
785
|
-
|
|
786
|
-
/* Thumb-sized controls make the form taller, which on a short phone pushed
|
|
787
|
-
"Send report" below the fold of a panel that scrolls — so the one button
|
|
788
|
-
the whole widget exists for was reachable only by scrolling past the
|
|
789
|
-
attachment row. It stays on screen instead. */
|
|
790
|
-
.foot {
|
|
791
|
-
position: sticky;
|
|
792
|
-
bottom: 0;
|
|
793
|
-
margin-top: 16px;
|
|
794
|
-
/* The panel scrolls under this, so the bar cannot be translucent. */
|
|
795
|
-
background: var(--bc-subtle);
|
|
796
|
-
padding-bottom: max(14px, env(safe-area-inset-bottom, 0px));
|
|
797
|
-
}
|
|
798
1114
|
}
|
|
799
1115
|
|
|
800
1116
|
/* Hover is a lie on a touchscreen: it latches on tap and stays. The launcher's
|
|
801
1117
|
lift in particular was left applied after the panel closed. */
|
|
802
1118
|
@media (hover: none) {
|
|
803
|
-
.launcher:hover { transform: none; filter: none;
|
|
804
|
-
.tool:hover, .themer button:hover {
|
|
1119
|
+
.launcher:hover { transform: none; filter: none; }
|
|
1120
|
+
.tool:hover:not(:disabled), .themer button:hover { transform: none; }
|
|
805
1121
|
}
|
|
806
|
-
`;function
|
|
1122
|
+
`;function Pa(t){const e=document.createElement("div");e.setAttribute("data-bugcatch","widget");let n=Na()||_a(t.config.theme);e.className=zn(n);const o=e.attachShadow({mode:"open"}),i=document.createElement("style");i.textContent=Ka(t.config.accent||"#4F46E5"),o.appendChild(i);const r=An[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=An[r];const l=document.createElement("div");l.className="themer",l.setAttribute("role","group"),l.setAttribute("aria-label","Report widget theme"),l.innerHTML=jn.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 b=m.target.closest("button[data-theme]");!b||b.dataset.theme===n||(n=b.dataset.theme,e.className=zn(n),Ua(n),l.querySelectorAll("button").forEach(h=>{h.setAttribute("aria-pressed",String(h.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 f=null;return g.addEventListener("click",()=>{if(f){f.close();return}s.classList.add("over"),g.setAttribute("aria-expanded","true"),f=Va({root:o,host:e,dock:s,client:t,onClose:()=>{f=null,s.classList.remove("over"),g.setAttribute("aria-expanded","false")}})}),s.append(l,g),o.appendChild(s),document.body.appendChild(e),t.widgetControls={open:()=>{f||g.click()},close:()=>{f==null||f.close()},isOpen:()=>!!f},()=>{f==null||f.destroy(),e.remove(),t.widgetControls=null}}function _a(t){return t==="dark"||t==="light"?t:"auto"}function zn(t){return t==="dark"?"bc-dark":t==="light"?"bc-light":"bc-auto"}function Va({root:t,host:e,dock:n,client:o,onClose:i}){var yo,wo,vo,xo,ko,So;const r=o.config,s=r.attachments!==!1,l=s&&Wo(),g=s&&Vo(),f=[];let m="MEDIUM",b=null,h=!1;const u=document.createElement("div");u.className=`scrim ${n.classList.contains("at-left")?"at-left":"at-right"} ${n.classList.contains("at-top")?"at-top":"at-bottom"}`,u.style.setProperty("--bc-dock-h",`${Math.round(n.getBoundingClientRect().height)}px`),u.innerHTML=`
|
|
807
1123
|
<div class="panel" role="dialog" aria-modal="true" aria-labelledby="bc-h">
|
|
808
|
-
|
|
809
|
-
<div class="mark">${Q.bug}</div>
|
|
810
|
-
<div class="titles">
|
|
811
|
-
<h2 id="bc-h">Report a bug</h2>
|
|
812
|
-
<p>We'll attach what the page was doing, so you don't have to explain it.</p>
|
|
813
|
-
</div>
|
|
814
|
-
<button class="icon-btn" type="button" data-act="close" aria-label="Close">${Q.close}</button>
|
|
815
|
-
</div>
|
|
1124
|
+
${qt({id:"bc-h",title:"Tell us what went wrong",sub:"Our assistant starts looking right away. We attach the page details for you."})}
|
|
816
1125
|
<div class="body">
|
|
817
|
-
<div class="slot"></div>
|
|
1126
|
+
<div class="slot" role="alert"></div>
|
|
818
1127
|
<div class="whoami" hidden></div>
|
|
819
1128
|
<div class="known" hidden></div>
|
|
820
1129
|
<div class="compose">
|
|
821
1130
|
<div class="field">
|
|
822
1131
|
<label for="bc-title">What went wrong?</label>
|
|
823
|
-
<input id="bc-title" type="text" placeholder="
|
|
1132
|
+
<input id="bc-title" type="text" placeholder="e.g. The checkout button does nothing" autocomplete="off"
|
|
1133
|
+
aria-describedby="bc-title-err" />
|
|
1134
|
+
<p class="field-err" id="bc-title-err" hidden></p>
|
|
824
1135
|
</div>
|
|
825
1136
|
<div class="field">
|
|
826
|
-
<label for="bc-desc">
|
|
827
|
-
<textarea id="bc-desc" placeholder="
|
|
1137
|
+
<label for="bc-desc">Anything else that helps <span class="opt">(optional)</span></label>
|
|
1138
|
+
<textarea id="bc-desc" placeholder="What you were doing, what you expected, what happened instead."></textarea>
|
|
828
1139
|
</div>
|
|
829
1140
|
<!-- Drawn only when this project asks for an identifier and the report
|
|
830
1141
|
does not already carry one. Between the description and the
|
|
@@ -832,207 +1143,250 @@ button.primary .spinner { border-color: rgba(255,255,255,.4); border-top-color:
|
|
|
832
1143
|
it is a question about what they just typed. -->
|
|
833
1144
|
<div class="refs" hidden></div>
|
|
834
1145
|
<div class="field">
|
|
835
|
-
<label id="bc-prio-l">
|
|
1146
|
+
<label id="bc-prio-l">How much is it getting in your way?</label>
|
|
836
1147
|
<div class="seg" role="group" aria-labelledby="bc-prio-l">
|
|
837
|
-
${
|
|
838
|
-
<button type="button" data-prio="${
|
|
1148
|
+
${Bn.map(a=>`
|
|
1149
|
+
<button type="button" data-prio="${a.value}" aria-pressed="${a.value===m}">${a.label}</button>
|
|
839
1150
|
`).join("")}
|
|
840
1151
|
</div>
|
|
841
1152
|
</div>
|
|
842
1153
|
${s?`
|
|
843
1154
|
<div class="field">
|
|
844
|
-
<
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1155
|
+
<div class="field-head">
|
|
1156
|
+
<label id="bc-att-l">Show us <span class="opt">(optional)</span></label>
|
|
1157
|
+
<span class="count" aria-live="polite"></span>
|
|
1158
|
+
</div>
|
|
1159
|
+
<div class="tools" role="group" aria-labelledby="bc-att-l">
|
|
1160
|
+
${l?`<button class="tool" type="button" data-act="shot">${B.camera}Screenshot</button>`:""}
|
|
1161
|
+
${g?`<button class="tool" type="button" data-act="rec">${B.video}Record screen</button>`:""}
|
|
1162
|
+
<button class="tool" type="button" data-act="file">${B.paperclip}Upload file</button>
|
|
849
1163
|
</div>
|
|
850
1164
|
<div class="thumbs"></div>
|
|
851
|
-
<input type="file" accept="${
|
|
1165
|
+
<input type="file" accept="${ct.join(",")}" multiple hidden />
|
|
852
1166
|
</div>`:""}
|
|
853
1167
|
</div>
|
|
854
1168
|
</div>
|
|
855
1169
|
<div class="foot">
|
|
856
|
-
<
|
|
857
|
-
<
|
|
1170
|
+
<span class="note" aria-live="polite"></span>
|
|
1171
|
+
${o.hasReports()?'<button class="quiet" type="button" data-act="history">Your reports</button>':""}
|
|
858
1172
|
<span class="spacer"></span>
|
|
859
|
-
${a.hasReports()?'<button class="ghost" type="button" data-act="history">Your reports</button>':""}
|
|
860
|
-
<button class="ghost" type="button" data-act="cancel">Cancel</button>
|
|
861
1173
|
<!-- Hidden until the API confirms this project can take a call, which
|
|
862
1174
|
most cannot. Drawn here rather than as a second launcher because it
|
|
863
1175
|
is the same errand as Send report — the difference is only whether
|
|
864
1176
|
the reporter would rather say it than type it. -->
|
|
865
|
-
<button class="ghost" type="button" data-act="call" hidden
|
|
866
|
-
<button class="primary" type="button" data-act="send">Send report</button>
|
|
1177
|
+
<button class="ghost" type="button" data-act="call" hidden>${B.phone}Talk to the team</button>
|
|
1178
|
+
<button class="primary" type="button" data-act="send">Send report ${B.send}</button>
|
|
867
1179
|
</div>
|
|
868
|
-
|
|
1180
|
+
<div class="powered">Powered by
|
|
1181
|
+
<a class="brand-credit" href="https://bugcatch.sphoro.com" target="_blank" rel="noopener noreferrer" aria-label="BugCatch">${Da}</a>
|
|
1182
|
+
</div>
|
|
1183
|
+
</div>`;const k=a=>u.querySelector(a),R=k("#bc-title"),z=k("#bc-desc"),S=k(".slot"),M=k(".thumbs"),I=u.querySelector("input[type=file]"),A=k('[data-act="send"]'),H=k('[data-act="call"]'),U=k(".note"),T=k(".compose"),Q=k(".whoami"),$=k(".refs"),Y=k(".known"),ze=k('[data-act="history"]'),fe=document.createElement("div");fe.setAttribute("role","status"),fe.className="note",T.prepend(fe);const Oe=ta(r,(yo=Se(r.projectId))==null?void 0:yo.email);let Qe=r.drafts===!1,xt=!1,G=Promise.resolve(),ge;function xe(){if(clearTimeout(ge),!Qe||xt||r.drafts===!1)return G;const a=R.value||z.value||f.length?{title:R.value,description:z.value,priority:m,items:f.map(({blob:c,mime:p,kind:d})=>({blob:c,mime:p,kind:d}))}:null;return G=G.catch(()=>{}).then(()=>pt(Oe,a)).then(()=>{fe.textContent=a?"Draft saved on this device":""}).catch(()=>{fe.textContent="Draft could not be saved. Keep this tab open."}),G}function Ye(){clearTimeout(ge),ge=setTimeout(xe,250)}T.addEventListener("input",Ye),T.addEventListener("change",Ye);const Xe=()=>{xe()};window.addEventListener("pagehide",Xe);function Je(){f.filter(a=>a.status==="failed").forEach(nt)}window.addEventListener("online",Je),r.drafts!==!1&&(T.inert=!0,na(Oe).then(a=>{Be||!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=()=>{R.value=a.title||"",z.value=a.description||"",m=Bn.some(c=>c.value===a.priority)?a.priority:"MEDIUM",u.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))y(c);Qe=!0,xe()},fe.querySelector("[data-discard]").onclick=()=>{Qe=!0,xe()})}).catch(()=>{fe.textContent="Draft storage is unavailable. Keep this tab open."}).finally(()=>{T.inert=!1,fe.querySelector("[data-resume]")||(Qe=!0)}));const Ee=document.activeElement,Ze=document.documentElement.style,Ft=Ze.overflow;Ze.overflow="hidden";let Be=!1,je=null;function Ge(){var a;Be||(xe(),Be=!0,clearTimeout(ge),window.removeEventListener("pagehide",Xe),window.removeEventListener("online",Je),b==null||b.cancel(),je==null||je(),f.forEach(c=>c.previewUrl&&URL.revokeObjectURL(c.previewUrl)),Ze.overflow=Ft,document.removeEventListener("keydown",et,!0),u.remove(),(a=t.querySelector(".recbar"))==null||a.remove(),n.hidden=!1,e.style.opacity="",i==null||i())}function be(){var a;if(!Be){u.classList.add("closing"),setTimeout(Ge,160);try{(a=Ee==null?void 0:Ee.focus)==null||a.call(Ee)}catch{}}}function et(a){if(a.key==="Escape"){if(a.stopPropagation(),b){q({keep:!1});return}h||be();return}if(a.key!=="Tab"||b)return;const c=[...u.querySelectorAll("button:not(:disabled), input:not([hidden]), textarea, select, [href]")].filter(x=>x.offsetParent!==null||x===document.activeElement);if(!c.length)return;const p=c[0],d=c[c.length-1],w=u.getRootNode().activeElement;a.shiftKey&&w===p?(a.preventDefault(),d.focus()):!a.shiftKey&&w===d&&(a.preventDefault(),p.focus())}document.addEventListener("keydown",et,!0),u.addEventListener("click",a=>{a.target===u&&!h&&be()}),k('[data-act="close"]').addEventListener("click",be),u.querySelectorAll("[data-prio]").forEach(a=>{a.addEventListener("click",()=>{m=a.dataset.prio,Ye(),u.querySelectorAll("[data-prio]").forEach(c=>{c.setAttribute("aria-pressed",String(c===a))})})});function ee(a,c="err"){S.innerHTML=a?`<div class="msg ${c}">${c==="err"?B.alert:""}<span>${v(a)}</span></div>`:""}const kt=k("#bc-title-err");function St(a){kt.hidden=!a,kt.innerHTML=a?`${B.alert}<span>${v(a)}</span>`:"",R.classList.toggle("invalid",!!a),R.setAttribute("aria-invalid",String(!!a))}R.addEventListener("input",()=>{R.value.trim()&&St("")});function me(){const a=f.some(d=>d.status==="uploading");A.disabled=h||a||f.some(d=>d.status==="failed"),U.textContent=a?"Waiting for the upload to finish…":f.some(d=>d.status==="failed")?"Retry or remove failed attachments before sending.":"";const c=r.maxAttachments??5;u.querySelectorAll(".tool").forEach(d=>{d.disabled=h||f.length>=c});const p=k(".field-head .count");p&&(p.textContent=f.length?`${f.length} of ${c}`:"")}function tt(a){var x;const c=a.el,p=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=`
|
|
869
1184
|
${d}
|
|
870
|
-
<span class="kind">${
|
|
871
|
-
<button class="drop" type="button" data-act="drop" aria-label="Remove ${
|
|
872
|
-
${
|
|
1185
|
+
<span class="kind">${p} · ${Re(a.size)}</span>
|
|
1186
|
+
<button class="drop" type="button" data-act="drop" aria-label="Remove ${p.toLowerCase()}">${B.close}</button>
|
|
1187
|
+
${w}`,c.querySelector('[data-act="drop"]').addEventListener("click",()=>Yt(a)),(x=c.querySelector('[data-act="retry"]'))==null||x.addEventListener("click",()=>nt(a))}function Yt(a){const c=f.indexOf(a);c>=0&&f.splice(c,1),a.previewUrl&&URL.revokeObjectURL(a.previewUrl),a.el.remove(),xe(),me()}async function nt(a){a.status="uploading",a.progress=0,tt(a),me();try{const{storageKey:c,mime:p}=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=p,a.status="ready"}catch(c){a.status="failed",ee(c.message.replace("[BugCatch] ",""))}tt(a),me()}function y({blob:a,mime:c,kind:p}){const d=r.maxAttachmentBytes??26214400;if(a.size>d){ee(`That file is ${Re(a.size)} — the limit is ${Re(d)}.`);return}const w=lt(c||a.type,ct);if(!w){ee("Only PNG, JPEG, WebP, WebM and MP4 files can be attached to a report.");return}const x={blob:a,kind:p,mime:w,size:a.size,previewUrl:URL.createObjectURL(a),status:"uploading",progress:0,storageKey:null,el:document.createElement("div")};x.el.className="thumb",f.push(x),M.appendChild(x.el),ee(""),xe(),nt(x)}async function E(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=""}}(wo=u.querySelector('[data-act="shot"]'))==null||wo.addEventListener("click",async()=>{try{const a=await E(Fo);y(a)}catch(a){(a==null?void 0:a.name)!=="NotAllowedError"&&ee(`Screenshot failed: ${a.message}`)}}),(vo=u.querySelector('[data-act="file"]'))==null||vo.addEventListener("click",()=>I.click()),I==null||I.addEventListener("change",()=>{const a=(r.maxAttachments??5)-f.length;[...I.files].slice(0,Math.max(a,0)).forEach(c=>{y({blob:c,mime:c.type,kind:c.type.startsWith("video/")?"VIDEO":"SCREENSHOT"})}),I.value=""}),(xo=u.querySelector('[data-act="rec"]'))==null||xo.addEventListener("click",async()=>{const a=r.maxRecordingMs??12e4;let c;try{b=await Yo({maxMs:a,onTick:p=>{c&&(c.querySelector(".time").textContent=dt(p))},onAutoStop:()=>{b&&q({keep:!0})}})}catch(p){(p==null?void 0:p.name)!=="NotAllowedError"&&ee(`Recording failed: ${p.message}`);return}u.style.display="none",n.hidden=!0,c=document.createElement("div"),c.className="recbar",c.innerHTML=`
|
|
873
1188
|
<span class="dot"></span>
|
|
874
1189
|
<span class="time">0:00</span>
|
|
875
|
-
<span class="hint">Recording — up to ${
|
|
1190
|
+
<span class="hint">Recording — up to ${dt(Math.round(a/1e3))}</span>
|
|
876
1191
|
<button class="stop" type="button">Stop</button>
|
|
877
|
-
<button class="ghost" type="button">Discard</button>`,c.querySelector(".stop").addEventListener("click",()=>
|
|
878
|
-
<
|
|
1192
|
+
<button class="ghost" type="button">Discard</button>`,c.querySelector(".stop").addEventListener("click",()=>q({keep:!0})),c.querySelector(".ghost").addEventListener("click",()=>q({keep:!1})),t.appendChild(c),c.querySelector(".stop").focus()});async function q({keep:a}){var p;const c=b;if(c){if(b=null,(p=t.querySelector(".recbar"))==null||p.remove(),u.style.display="",n.hidden=!1,!a){c.cancel(),R.focus();return}try{const d=await c.stop();d.blob.size?y(d):ee("That recording came back empty — nothing was captured.")}catch(d){ee(`Recording failed: ${d.message}`)}R.focus()}}let K={policy:"off",fields:[]},_=!1,te=null,se="",ae="",X=[],Xt="ask",eo=!1,He=!1;o.referenceFields().then(a=>{K=a,oo()}).catch(()=>{});function Jt(){return K.policy==="off"||!K.fields.length?{found:[],conflicts:[],loose:[]}:Ha(K.fields,[R.value,z.value])}function to(a=Jt()){return a.found.length>0&&a.conflicts.length===0}function Zt(){return!te||!se.trim()?[]:[{key:te,value:se.trim().slice(0,120)}]}const Ie=document.createElement("p");Ie.className="ref-found",Ie.setAttribute("aria-live","polite"),Ie.hidden=!0,z.closest(".field").after(Ie);let no;function oo(){const a=Jt();if(to(a)){const c=a.found.slice(0,2).map(p=>`${v(p.label)} <b>${v(ro(p.value))}</b>`).join(", ");Ie.innerHTML=`${B.check}<span>Found your ${c} — no need to add it again.</span>`,Ie.hidden=!1,_&&!Zt().length&&(_=!1,Ce())}else Ie.hidden=!0,Ie.innerHTML=""}const ao=()=>{clearTimeout(no),no=setTimeout(oo,250)};R.addEventListener("input",ao),z.addEventListener("input",ao);function ro(a){return a.length>20?`${a.slice(0,8)}…${a.slice(-4)}`:a}function Cr(a){const c=a.trim().toLowerCase();return X.find(p=>p.value.toLowerCase()===c)||null}function io(a){se=a.value,a.kinds.length===1?te=a.kinds[0].key:a.kinds.some(c=>c.key===te)||(te=null),ae="",He=!1}function Ce(){var W,ne;if(!_){$.hidden=!0,$.innerHTML="";return}const a=K.fields.find(C=>C.key===te)||null,c=K.policy!=="required",p=Cr(se),d=p&&p.kinds.length>1?K.fields.filter(C=>p.kinds.some(oe=>oe.key===C.key)):K.fields,w=X.length>1&&X.every(C=>C.kinds.length===1&&C.kinds[0].key===X[0].kinds[0].key),x={confirm:X.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."]}[Xt];$.hidden=!1;const D=d.length>6;$.innerHTML=`
|
|
1193
|
+
<div class="ask-head">
|
|
1194
|
+
<span class="avatar" aria-hidden="true">${B.sparkle}</span>
|
|
1195
|
+
<div>
|
|
1196
|
+
<strong>${v(x[0])}</strong>
|
|
1197
|
+
<p class="why">${v(x[1])}</p>
|
|
1198
|
+
</div>
|
|
1199
|
+
</div>
|
|
1200
|
+
${X.length>1?`
|
|
1201
|
+
<div class="chips cands" role="group" aria-label="IDs found in your message">
|
|
1202
|
+
${X.map((C,oe)=>`
|
|
1203
|
+
<button type="button" data-ref-cand="${oe}" title="${v(C.value)}"
|
|
1204
|
+
aria-pressed="${!!p&&p===C}">${v(ro(C.value))}</button>
|
|
1205
|
+
`).join("")}
|
|
1206
|
+
</div>`:""}
|
|
1207
|
+
${d.length>1&&!D&&!w?`
|
|
1208
|
+
<div class="chips" role="group" aria-label="Which detail you are giving">
|
|
1209
|
+
${d.map(C=>`
|
|
1210
|
+
<button type="button" data-ref-kind="${v(C.key)}" aria-pressed="${!!a&&C.key===a.key}">${v(C.label)}</button>
|
|
1211
|
+
`).join("")}
|
|
1212
|
+
</div>`:""}
|
|
879
1213
|
<div class="row">
|
|
880
|
-
${
|
|
1214
|
+
${D?`
|
|
881
1215
|
<select class="ref-kind" aria-label="Which detail you are giving">
|
|
882
|
-
${
|
|
883
|
-
|
|
1216
|
+
${a?"":'<option value="" selected disabled>What is it?</option>'}
|
|
1217
|
+
${d.map(C=>`
|
|
1218
|
+
<option value="${v(C.key)}" ${a&&C.key===a.key?"selected":""}>${v(C.label)}</option>
|
|
884
1219
|
`).join("")}
|
|
885
1220
|
</select>`:""}
|
|
886
|
-
<input class="ref-value" type="${
|
|
887
|
-
inputmode="${
|
|
888
|
-
value="${
|
|
889
|
-
placeholder="${
|
|
890
|
-
aria-label="${
|
|
1221
|
+
<input class="ref-value${ae?" invalid":""}" type="${(a==null?void 0:a.inputMode)==="email"?"email":"text"}"
|
|
1222
|
+
inputmode="${v((a==null?void 0:a.inputMode)==="numeric"||(a==null?void 0:a.inputMode)==="tel"?"numeric":"text")}"
|
|
1223
|
+
value="${v(se)}"
|
|
1224
|
+
placeholder="${v(a?a.placeholder||a.label:"Your ID")}"
|
|
1225
|
+
aria-label="${v(a?a.label:"Your ID")}" ${ae?'aria-invalid="true"':""} />
|
|
891
1226
|
</div>
|
|
892
|
-
${
|
|
893
|
-
${
|
|
894
|
-
${c?`<button type="button" class="link" data-act="ref-skip"
|
|
1227
|
+
${a!=null&&a.hint?`<p class="hint">${v(a.hint)}</p>`:""}
|
|
1228
|
+
${ae?`<p class="field-err" role="alert">${B.alert}<span>${v(ae)}</span></p>`:""}
|
|
1229
|
+
${c?`<button type="button" class="link" data-act="ref-skip">${Xt==="ask"?"I don't have this — send without it":"Not sure — send as it is"}</button>`:""}`,$.querySelectorAll("[data-ref-cand]").forEach(C=>{C.addEventListener("click",()=>{io(X[Number(C.dataset.refCand)]),Ce()})}),$.querySelectorAll("[data-ref-kind]").forEach(C=>{C.addEventListener("click",()=>{te=C.dataset.refKind,ae="",He=!1,Ce()})});const j=$.querySelector(".ref-value");j.addEventListener("input",()=>{se=j.value,ae="",He=!1}),j.addEventListener("keydown",C=>{C.key==="Enter"&&(C.preventDefault(),A.click())}),(W=$.querySelector(".ref-kind"))==null||W.addEventListener("change",C=>{var oe;te=C.target.value,ae="",Ce(),(oe=$.querySelector(".ref-value"))==null||oe.focus()}),(ne=$.querySelector('[data-act="ref-skip"]'))==null||ne.addEventListener("click",()=>{eo=!0,_=!1,te=null,se="",ae="",He=!1,Ce(),ee("")}),((!a&&se?$.querySelector("[data-ref-kind], .ref-kind"):null)||j).focus()}function Tr(a){if(X=a.conflicts.length?a.conflicts:a.loose.slice(0,4),Xt=a.conflicts.length?"confirm":a.loose.length?"suggest":"ask",_=!0,X.length)if(X.length===1)io(X[0]);else{se="";const[c]=X;te=X.every(d=>d.kinds.length===1&&d.kinds[0].key===c.kinds[0].key)?c.kinds[0].key:null}else te=te||K.fields[0].key;Ce()}function Er(){if(K.policy==="off"||!K.fields.length||Zt().length)return!1;const a=Jt();return to(a)||eo&&K.policy!=="required"?!1:a}A.addEventListener("click",async()=>{const a=R.value;if(!a.trim()){St("Add a few words about what went wrong — a short sentence is plenty."),R.focus();return}if(_&&se.trim()&&!te){ae="Pick what this ID is.",Ce();return}const c=Er();if(c){_?Ce():Tr(c),ee("");return}if(te&&se.trim()){const d=K.fields.find(w=>w.key===te);if(!He&&d&&!Ln(d,se)){ae=`That does not look like a ${d.label.toLowerCase()}. Press Send again to use it anyway.`,He=!0,Ce();return}}h=!0,ee(""),me();const p=A.innerHTML;A.innerHTML='<span class="spinner" aria-hidden="true"></span>Sending…';try{const d=await o.report({title:a,description:z.value,priority:m,references:Zt(),attachments:f.filter(w=>w.status==="ready").map(w=>({type:w.kind,storageKey:w.storageKey,mime:w.mime}))});if(d!=null&&d.cancelled){h=!1,A.innerHTML=p,me();return}xt=!0,clearTimeout(ge),await G,r.drafts!==!1&&await pt(Oe,null).catch(()=>{}),ho(d)}catch(d){h=!1,A.innerHTML=p,ee(d.message.replace("[BugCatch] ","")),me(),ce.mode!=="off"&&nn().catch(()=>{})}}),(ko=u.querySelector('[data-act="history"]'))==null||ko.addEventListener("click",go);let qe=[],$t="",Gt=null,ot=null;function so(){r.knownIssues!==!1&&o.knownIssues().then(a=>{qe=a,at()}).catch(()=>{})}let co=null;function Ir(a){if(r.knownIssues===!1)return;clearTimeout(co),ot==null||ot.abort();const c=(a||"").trim();if(c.length<3){so();return}co=setTimeout(()=>{ot=new AbortController,o.matchKnownIssues(c,{signal:ot.signal}).then(p=>{p.length&&(qe=p,at())}).catch(()=>{})},250)}function at(){if(!qe.length){Y.hidden=!0,Y.innerHTML="";return}Y.hidden=!1,Y.innerHTML=`
|
|
895
1230
|
<p class="known-h">Already reported</p>
|
|
896
|
-
${
|
|
897
|
-
<div class="issue ${
|
|
1231
|
+
${qe.map(a=>`
|
|
1232
|
+
<div class="issue ${a.joined?"is-joined":""}">
|
|
898
1233
|
<span class="who">
|
|
899
|
-
<span class="t">${
|
|
900
|
-
<span class="meta">${
|
|
1234
|
+
<span class="t">${v(a.title)}</span>
|
|
1235
|
+
<span class="meta">${v(Mr(a))}</span>
|
|
901
1236
|
</span>
|
|
902
|
-
<button class="join" type="button" data-join="${
|
|
903
|
-
${
|
|
1237
|
+
<button class="join" type="button" data-join="${v(a.id)}"
|
|
1238
|
+
${a.joined?"disabled":""}>${a.joined?"Following":"Me too"}</button>
|
|
904
1239
|
</div>
|
|
905
|
-
${
|
|
1240
|
+
${Gt===a.id?`
|
|
906
1241
|
<div class="ask">
|
|
907
1242
|
<input type="email" class="join-email" placeholder="you@example.com"
|
|
908
1243
|
aria-label="Your email, so we can tell you when this is fixed" />
|
|
909
|
-
<button class="ghost" type="button" data-join-confirm="${
|
|
1244
|
+
<button class="ghost" type="button" data-join-confirm="${v(a.id)}">Follow</button>
|
|
910
1245
|
</div>`:""}
|
|
911
1246
|
`).join("")}
|
|
912
|
-
${
|
|
1247
|
+
${$t?`<p class="err">${v($t)}</p>`:""}`}function Mr(a){const c=a.status==="IN_PROGRESS"?"Being worked on now":"Not fixed yet",p=a.affectedCount===0?"":` · ${a.affectedCount} ${a.affectedCount===1?"person has":"people have"} said this is happening to them`;return`${c} · reported ${mt(a.since)}${p}`}Y.addEventListener("click",async a=>{var x,D,j,V,W;const c=a.target.closest("[data-join]"),p=a.target.closest("[data-join-confirm]");if(!c&&!p)return;const d=(p||c).getAttribute(p?"data-join-confirm":"data-join");$t="";const w=((D=(x=Y.querySelector(".join-email"))==null?void 0:x.value)==null?void 0:D.trim())||((j=o.signedInAs())==null?void 0:j.email)||((V=o.config.user)==null?void 0:V.email);if(!w){Gt=d,at(),(W=Y.querySelector(".join-email"))==null||W.focus();return}try{const ne=await o.meToo(d,{email:w});Gt=null,qe=qe.map(C=>C.id===d?{...C,joined:!0,affectedCount:ne.affectedCount??C.affectedCount}:C),at()}catch(ne){$t=ne.message.replace("[BugCatch] ",""),at()}}),so(),R.addEventListener("input",()=>Ir(R.value));let lo=!1;function uo(){o.callKind().then(a=>{a&&(lo=!0,H.hidden=en())}).catch(()=>{})}uo();let ce={mode:o.lastLoginMode||"off",signedIn:null},le="idle",De=((So=o.config.user)==null?void 0:So.email)||"",Ne="",de=!1,we="";const en=()=>ce.mode==="required"&&!ce.signedIn;function Lr(){const a=en();T.hidden=a,A.hidden=a,ze&&(ze.hidden=a),a?H.hidden=!0:lo&&(H.hidden=!1)}function Ar(){var c;if(ce.mode==="off"){Q.hidden=!0,Q.innerHTML="";return}Q.hidden=!1,Q.classList.toggle("asking",le!=="idle");const a=we?`<div class="after err">${v(we)}</div>`:"";if(le==="idle"){if(ce.signedIn){Q.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 p=(c=o.config.user)==null?void 0:c.email;Q.innerHTML=`<span class="who">${ce.mode==="required"?"Sign in to report a bug here.":p?`Reporting as <strong>${v(p)}</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"){Q.innerHTML=`
|
|
913
1248
|
<p class="why">Sign in with any email address — we'll send you a code.
|
|
914
|
-
It's how you hear back about this report${
|
|
1249
|
+
It's how you hear back about this report${ce.mode==="required"?", and this project asks for it":""}.</p>
|
|
915
1250
|
<div class="row">
|
|
916
|
-
<input type="email" data-fld="email" value="${
|
|
1251
|
+
<input type="email" data-fld="email" value="${v(De)}"
|
|
917
1252
|
placeholder="you@example.com" autocomplete="email" spellcheck="false"
|
|
918
|
-
aria-label="Your email address" ${
|
|
919
|
-
<button type="button" class="
|
|
920
|
-
${
|
|
1253
|
+
aria-label="Your email address" ${de?"disabled":""} />
|
|
1254
|
+
<button type="button" class="primary" data-act="code" ${de?"disabled":""}>
|
|
1255
|
+
${de?"Sending…":"Send code"}
|
|
921
1256
|
</button>
|
|
922
1257
|
</div>
|
|
923
|
-
${
|
|
924
|
-
${
|
|
925
|
-
<p class="why">We sent a code to <strong>${
|
|
1258
|
+
${a}
|
|
1259
|
+
${ce.mode==="optional"?'<div class="after"><button type="button" class="link" data-act="skip">Not now</button></div>':""}`;return}Q.innerHTML=`
|
|
1260
|
+
<p class="why">We sent a code to <strong>${v(De)}</strong>. It expires in ten minutes.</p>
|
|
926
1261
|
<div class="row">
|
|
927
|
-
<input type="text" class="code" data-fld="code" value="${
|
|
1262
|
+
<input type="text" class="code" data-fld="code" value="${v(Ne)}"
|
|
928
1263
|
placeholder="123456" inputmode="numeric" autocomplete="one-time-code"
|
|
929
|
-
maxlength="8" aria-label="The code we emailed you" ${
|
|
930
|
-
<button type="button" class="
|
|
931
|
-
${
|
|
1264
|
+
maxlength="8" aria-label="The code we emailed you" ${de?"disabled":""} />
|
|
1265
|
+
<button type="button" class="primary" data-act="confirm" ${de?"disabled":""}>
|
|
1266
|
+
${de?"Checking…":"Sign in"}
|
|
932
1267
|
</button>
|
|
933
1268
|
</div>
|
|
934
|
-
${
|
|
1269
|
+
${a}
|
|
935
1270
|
<div class="after">
|
|
936
|
-
<button type="button" class="link" data-act="resend" ${
|
|
937
|
-
<button type="button" class="link" data-act="signin-back" ${
|
|
938
|
-
</div>`}function
|
|
1271
|
+
<button type="button" class="link" data-act="resend" ${de?"disabled":""}>Send it again</button>
|
|
1272
|
+
<button type="button" class="link" data-act="signin-back" ${de?"disabled":""}>Use another address</button>
|
|
1273
|
+
</div>`}function ye({focus:a=!1}={}){var p;if(Ar(),Lr(),!a||de)return;const c=Q.querySelector("[data-fld]");c&&(c.focus(),(p=c.setSelectionRange)==null||p.call(c,c.value.length,c.value.length))}Q.addEventListener("input",a=>{const c=a.target.closest("[data-fld]");c&&(c.dataset.fld==="email"?De=c.value:Ne=c.value)}),Q.addEventListener("keydown",a=>{a.key!=="Enter"||!a.target.closest("[data-fld]")||(a.preventDefault(),le==="email"?tn():po())});async function tn({resent:a=!1}={}){const c=De.trim();if(!c){we="Enter the address to send the code to.",ye({focus:!0});return}de=!0,we="",ye();try{await o.signIn(c),De=c,a||(Ne=""),le="code"}catch(p){we=p.message}finally{de=!1,ye({focus:!0})}}async function po(){const a=Ne.trim();if(!a){we="Enter the code we emailed you.",ye({focus:!0});return}de=!0,we="",ye();try{const c=await o.confirmSignIn(De.trim(),a);ce.signedIn=c,le="idle",Ne="",ee(""),uo()}catch(c){we=c.message}finally{de=!1,ye({focus:le!=="idle"})}}Q.addEventListener("click",async a=>{const c=a.target.closest("button[data-act]");if(!c)return;const p=c.dataset.act;if(p==="signin"){le="email",we="",ye({focus:!0});return}if(p==="skip"){le="idle",we="",ye();return}if(p==="signin-back"){le="email",we="",Ne="",ye({focus:!0});return}if(p==="code"){tn();return}if(p==="resend"){tn({resent:!0});return}if(p==="confirm"){po();return}if(p==="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 nn({announce:a=!1}={}){const c=await o.loginState();ce={mode:c.mode,signedIn:c.signedIn},o.lastLoginMode=c.mode,en()&&le==="idle"&&(le="email"),ce.signedIn&&(le="idle"),a&&c.signedOutReason&&ee(c.signedOutReason,"info"),ye()}nn({announce:!0}).catch(()=>{}),H.addEventListener("click",async()=>{h=!0,ee(""),me(),H.disabled=!0,H.innerHTML='<span class="spinner"></span>Ringing…';try{const a=await o.startCall();Br(a)}catch(a){h=!1,H.disabled=!1,H.innerHTML=`${B.phone}Talk to the team`,me(),a.filed?ho(a.filed,{callFailed:a.reason||"The call could not be started."}):(ee(a.message.replace("[BugCatch] ","")),ce.mode!=="off"&&nn().catch(()=>{}))}});function Br(a){const c=u.querySelector(".panel"),p=a!=null&&a.bugId?String(a.bugId).slice(0,8):null;c.innerHTML=`
|
|
939
1274
|
<div class="call">
|
|
940
1275
|
<div class="call-head">
|
|
941
1276
|
<span class="call-dot" aria-hidden="true"></span>
|
|
942
1277
|
<strong class="call-state">Ringing…</strong>
|
|
943
1278
|
</div>
|
|
944
1279
|
<p class="call-note">
|
|
945
|
-
We're ringing the team now. Someone will pick up if they're free — your report${
|
|
1280
|
+
We're ringing the team now. Someone will pick up if they're free — your report${p?` <code>${v(p)}</code>`:""} is already filed either way, so nothing is lost if nobody answers.
|
|
946
1281
|
</p>
|
|
947
1282
|
<div class="foot">
|
|
948
1283
|
<span class="spacer"></span>
|
|
949
1284
|
<button class="primary" type="button" data-act="hangup">Cancel</button>
|
|
950
1285
|
</div>
|
|
951
|
-
</div>`;const d=c.querySelector(".call-state"),
|
|
1286
|
+
</div>`;const d=c.querySelector(".call-state"),w=c.querySelector(".call-dot"),x={ringing:"Ringing the team…",connecting:"Someone answered — connecting…",connected:"Connected",missed:"Nobody was free",ended:"Call ended",failed:"The call dropped"},D=["ended","failed","missed"],j=o.on("call",({state:V})=>{if(d.textContent=x[V]||V,w.classList.toggle("off",D.includes(V)),D.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",()=>{j(),o.endCall(),be()})}function ho(a,{callFailed:c=null}={}){if(a!=null&&a.id&&!c){on(a.id,{fresh:!0}).then(p=>{p||fo(a,{callFailed:c})});return}fo(a,{callFailed:c})}function fo(a,{callFailed:c=null}={}){var x;const p=a!=null&&a.id?String(a.id).slice(0,8):null,d=(x=a==null?void 0:a.estimate)!=null&&x.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>`:"";u.querySelector(".panel").innerHTML=`
|
|
952
1287
|
<div class="done">
|
|
953
|
-
<div class="tick">${
|
|
1288
|
+
<div class="tick">${B.check}</div>
|
|
954
1289
|
<h3>Thanks — that's on its way.</h3>
|
|
955
|
-
<p>${
|
|
956
|
-
${
|
|
957
|
-
${d?`<p class="eta">Reports like this usually take <strong>${
|
|
958
|
-
</div>`,setTimeout(
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
<
|
|
962
|
-
<
|
|
963
|
-
<p>Everything you've reported, and where each one got to.</p>
|
|
964
|
-
</div>
|
|
965
|
-
<button class="icon-btn" type="button" data-act="close" aria-label="Close">${Q.close}</button>
|
|
966
|
-
</div>
|
|
967
|
-
<div class="history"><div class="empty">Loading…</div></div>`,o.querySelector('[data-act="close"]').addEventListener("click",ce);let c=[];try{({reports:c}=await a.myReports())}catch{o.querySelector(".history").innerHTML=`<div class="empty">Couldn't load your reports just now.</div>`;return}const u=o.querySelector(".history");if(u){u.innerHTML=c.length?c.map(d=>`
|
|
968
|
-
<button class="row" type="button" data-report="${x(d.id)}">
|
|
1290
|
+
<p>${p?`Filed as <code>${v(p)}</code>.`:"The team has what they need to reproduce it."}</p>
|
|
1291
|
+
${w}
|
|
1292
|
+
${d?`<p class="eta">Reports like this usually take <strong>${v(d)}</strong>. We'll let you know when it moves.</p>`:""}
|
|
1293
|
+
</div>`,setTimeout(be,w?6e3:d?4e3:2e3)}async function go(){const a=u.querySelector(".panel");clearTimeout(rt),Te=null,a.classList.remove("is-chat"),a.innerHTML=`
|
|
1294
|
+
${qt({title:"Your reports",sub:"Everything you've reported, and where each one got to."})}
|
|
1295
|
+
<div class="history" aria-busy="true">${Xa(3)}</div>`,a.querySelector('[data-act="close"]').addEventListener("click",be);let c=[];try{({reports:c}=await o.myReports())}catch{const d=a.querySelector(".history");d&&(d.innerHTML=Dt("Couldn't load your reports just now.",B.alert));return}const p=a.querySelector(".history");if(p){p.removeAttribute("aria-busy"),p.innerHTML=c.length?c.map((d,w)=>`
|
|
1296
|
+
<button class="row" type="button" data-report="${v(d.id)}" style="animation-delay:${Math.min(w,8)*30}ms">
|
|
1297
|
+
<span class="row-ico" aria-hidden="true">${B.chat}</span>
|
|
969
1298
|
<span class="who">
|
|
970
|
-
<span class="ttl">${
|
|
971
|
-
<span class="sub">${
|
|
1299
|
+
<span class="ttl">${v(d.title)}</span>
|
|
1300
|
+
<span class="sub">${v(d.reference)} · ${v(mt(d.createdAt))}</span>
|
|
972
1301
|
</span>
|
|
973
|
-
<span class="pill ${
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
<h2>${c?"Looking into it":"Your report"}</h2>
|
|
979
|
-
<p>${c?"Our assistant is investigating — it may ask you a question.":"Everything that has happened, and a way to answer."}</p>
|
|
980
|
-
</div>
|
|
981
|
-
<button class="icon-btn" type="button" data-act="close" aria-label="Close">${Q.close}</button>
|
|
982
|
-
</div>
|
|
983
|
-
<div class="history"><div class="empty">Loading…</div></div>`,u.querySelector('[data-act="close"]').addEventListener("click",ce),u.querySelector('[data-act="back"]').addEventListener("click",Cn);let d=null;try{d=await a.myReport(o)}catch{d=null}const k=u.querySelector(".history");if(!k||!u.querySelector('[data-act="back"]'))return!1;if(!d)return c||(k.innerHTML=`<div class="empty">Couldn't load that report just now.</div>`),!1;be=o,a.markReportRead(o),k.classList.add("chatting"),k.innerHTML=`
|
|
1302
|
+
<span class="pill ${v(String(d.status).toLowerCase())}">${v(Ut(d.status))}</span>
|
|
1303
|
+
<span class="chev" aria-hidden="true">${B.chevronRight}</span>
|
|
1304
|
+
</button>`).join(""):Dt("You haven't reported anything yet.",B.inbox);for(const d of p.querySelectorAll("[data-report]"))d.addEventListener("click",()=>on(d.dataset.report))}}async function on(a,{fresh:c=!1}={}){var j;const p=u.querySelector(".panel");clearTimeout(rt),p.classList.add("is-chat"),p.innerHTML=`
|
|
1305
|
+
${qt({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."})}
|
|
1306
|
+
<div class="history chatting" aria-busy="true"><div class="thread">${Ja()}</div></div>`,p.querySelector('[data-act="close"]').addEventListener("click",be),p.querySelector('[data-act="back"]').addEventListener("click",go);let d=null;try{d=await o.myReport(a)}catch{d=null}const w=p.querySelector(".history");if(!w||!p.querySelector('[data-act="back"]'))return!1;if(!d)return c?(p.classList.remove("is-chat"),!1):(w.removeAttribute("aria-busy"),w.innerHTML=Dt("Couldn't load that report just now.",B.alert),!1);Te=a,o.markReportRead(a);const x=On(d.timeline);w.classList.add("chatting"),w.removeAttribute("aria-busy"),w.innerHTML=`
|
|
984
1307
|
<div class="report-head">
|
|
985
|
-
<span class="ttl">${
|
|
986
|
-
<span class="sub">${
|
|
987
|
-
<span class="pill ${
|
|
988
|
-
${(
|
|
1308
|
+
<span class="ttl">${v(d.title)}</span>
|
|
1309
|
+
<span class="sub">${v(d.reference)} · ${v(d.project||"")}</span>
|
|
1310
|
+
<span class="pill ${v(String(d.status).toLowerCase())}">${v(Ut(d.status))}</span>
|
|
1311
|
+
${(j=d.estimate)!=null&&j.label?`<span class="eta-line">Usually takes ${v(String(d.estimate.label))}</span>`:""}
|
|
989
1312
|
</div>
|
|
990
|
-
<div class="thread">${
|
|
991
|
-
<div class="as-
|
|
992
|
-
<
|
|
993
|
-
|
|
994
|
-
<div class="as-
|
|
995
|
-
|
|
1313
|
+
<div class="thread" role="log" aria-live="polite" aria-label="Conversation">${x}</div>`;const D=w.querySelector(".thread");return Nt.set(D,x),Ve(D),mo(w,d),d.canReply&&jr(w,D,a),bo(w,d,a),Ve(D),!0}let rt=null;function bo(a,c,p){var F,re,ke;clearTimeout(rt);const d=c.assistant,w=a.querySelector(".thread");let x=a.querySelector(".assistant");if(!d){x==null||x.remove(),(F=w==null?void 0:w.querySelector(".typing-row"))==null||F.remove();return}const D=(x==null?void 0:x.dataset.phase)||null;x||(x=document.createElement("div"),x.setAttribute("role","status"),x.setAttribute("aria-live","polite"),(re=a.querySelector(".report-head"))==null||re.after(x)),x.className=`assistant ${v(d.phase)}`,x.dataset.phase=d.phase;const j=d.steps||[],V=j.filter(ie=>ie.state==="done").length,W=j.some(ie=>ie.state==="active")?.5:0,ne=j.length?Math.round((V+W)/j.length*100):0,C=`
|
|
1314
|
+
<div class="as-body${D&&D!==d.phase?" entering":""}">
|
|
1315
|
+
<div class="as-top">
|
|
1316
|
+
<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>
|
|
1317
|
+
<div class="as-text">
|
|
1318
|
+
<div class="as-title">${v(d.title)}</div>
|
|
1319
|
+
<div class="as-detail">${v(d.detail)}</div>
|
|
1320
|
+
</div>
|
|
996
1321
|
</div>
|
|
997
1322
|
</div>
|
|
998
|
-
|
|
1323
|
+
${j.length?`
|
|
1324
|
+
<div class="as-progress" role="progressbar" aria-label="Progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${ne}"><i style="width:${ne}%"></i></div>
|
|
1325
|
+
<ol class="as-steps">${j.map(ie=>`<li class="${v(ie.state)}"${ie.state==="active"?' aria-current="step"':""}><span class="dot"></span><span class="lbl">${v(ie.label)}</span></li>`).join("")}</ol>`:""}`,oe=JSON.stringify([d.phase,d.title,d.detail,j]);if(x.dataset.key!==oe){const ie=x.querySelector(".as-progress i"),ue=ie==null?void 0:ie.style.width;x.innerHTML=C,x.dataset.key=oe;const Ue=x.querySelector(".as-progress i");Ue&&ue&&(Ue.style.width=ue,requestAnimationFrame(()=>{Ue.style.width=`${ne}%`}))}if(w){const ie=qn(w),ue=w.querySelector(".typing-row");d.phase!=="analyzing"?ue==null||ue.remove():(!ue||ue!==w.lastElementChild)&&(ue==null||ue.remove(),w.insertAdjacentHTML("beforeend",Ya()),ie&&Ve(w))}d.phase==="analyzing"?rt=setTimeout(()=>it(p),3e3):d.phase==="calling"?rt=setTimeout(()=>it(p),1e4):d.phase==="needs_info"&&D!=="needs_info"&&((ke=a.querySelector(".composer textarea"))==null||ke.focus())}function mo(a,c){var d,w;if((d=a.querySelector(".fix-verification"))==null||d.remove(),!c.resolvedAt||!["RESOLVED","CLOSED"].includes(c.status))return;const p=document.createElement("div");p.className="fix-verification",c.verification?p.textContent=c.verification==="CONFIRMED"?"You confirmed that this fix works.":"Your feedback was recorded.":(p.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>',p.querySelectorAll("[data-result]").forEach(x=>{x.onclick=async()=>{p.querySelectorAll("button").forEach(D=>{D.disabled=!0});try{await o.verifyFix(c.id,{result:x.dataset.result,resolvedAt:c.resolvedAt,note:p.querySelector("textarea").value}),await on(c.id)}catch(D){p.querySelector(".feedback-status").textContent=D.message,p.querySelectorAll("button").forEach(j=>{j.disabled=!1})}}})),(w=a.querySelector(".report-head"))==null||w.after(p)}async function it(a){const c=u.querySelector(".history.chatting"),p=c==null?void 0:c.querySelector(".thread");if(!p||Te!==a)return;let d=null;try{d=await o.myReport(a)}catch{}if(!d||Te!==a)return;mo(c,d);const w=qn(p),x=On(d.timeline);Nt.get(p)!==x&&(p.innerHTML=x,Nt.set(p,x)),bo(c,d,a),w&&Ve(p);const D=c.querySelector(".report-head .pill");D&&(D.className=`pill ${String(d.status).toLowerCase()}`,D.textContent=Ut(d.status))}function jr(a,c,p){var Ue;const d=document.createElement("div");d.className="composer",d.innerHTML=`
|
|
999
1326
|
<div class="staged" hidden></div>
|
|
1000
1327
|
<div class="row">
|
|
1001
1328
|
<div class="tools">
|
|
1002
|
-
|
|
1003
|
-
<button class="tool" type="button" data-act="
|
|
1004
|
-
<button class="tool" type="button" data-act="
|
|
1329
|
+
${r.attachments===!1?"":`
|
|
1330
|
+
<button class="tool" type="button" data-act="attach" title="Attach a file" aria-label="Attach a file">${B.paperclip}</button>
|
|
1331
|
+
<button class="tool" type="button" data-act="voice" title="Record a voice note" aria-label="Record a voice note">${B.mic}</button>`}
|
|
1332
|
+
<button class="tool" type="button" data-act="livecall" title="Call the team" aria-label="Call the team" hidden>${B.phone}</button>
|
|
1005
1333
|
</div>
|
|
1006
1334
|
<textarea rows="1" placeholder="Write a reply…" aria-label="Write a reply"></textarea>
|
|
1007
|
-
<button class="send" type="button" data-act="send" title="Send" aria-label="Send" disabled>${
|
|
1335
|
+
<button class="send" type="button" data-act="send" title="Send" aria-label="Send" disabled>${B.send}</button>
|
|
1008
1336
|
</div>
|
|
1009
1337
|
<div class="note" hidden></div>
|
|
1010
|
-
<input type="file" hidden multiple accept="${
|
|
1011
|
-
<span class="chip ${
|
|
1012
|
-
<span class="nm">${
|
|
1013
|
-
<span class="sz">${
|
|
1014
|
-
<button type="button" data-drop="${
|
|
1015
|
-
</span>`).join("");for(const
|
|
1338
|
+
<input type="file" hidden multiple accept="${bn.join(",")}">`,a.appendChild(d);const w=d.querySelector(".staged"),x=d.querySelector("textarea"),D=d.querySelector('[data-act="send"]'),j=d.querySelector('[data-act="voice"]'),V=d.querySelector('[data-act="livecall"]'),W=d.querySelector("input[type=file]"),ne=d.querySelector(".note"),C=[];let oe=null,F=!1;function re(O,P=""){ne.hidden=!O,ne.className=`note ${P}`,ne.textContent=O||""}function ke(){const O=C.filter(P=>P.storageKey);D.disabled=F||!x.value.trim()&&!O.length,w.hidden=!C.length,w.innerHTML=C.map((P,ve)=>`
|
|
1339
|
+
<span class="chip ${P.storageKey?"":"uploading"}">
|
|
1340
|
+
<span class="nm">${v(P.name)}</span>
|
|
1341
|
+
<span class="sz">${P.storageKey?Re(P.size):"sending…"}</span>
|
|
1342
|
+
<button type="button" data-drop="${ve}" aria-label="Remove ${v(P.name)}">${B.close}</button>
|
|
1343
|
+
</span>`).join("");for(const P of w.querySelectorAll("[data-drop]"))P.addEventListener("click",()=>{C.splice(Number(P.dataset.drop),1),ke()})}async function ie(O,P,ve){const J={name:ve,size:O.size,storageKey:null};C.push(J),ke();try{const{storageKey:an,mime:zr}=await o.uploadAttachment(O,P);J.storageKey=an,J.mime=zr}catch(an){C.splice(C.indexOf(J),1),re(an.message||"That file could not be sent.","err")}ke()}x.addEventListener("input",()=>{x.style.height="auto",x.style.height=`${Math.min(x.scrollHeight,120)}px`,ke()}),x.addEventListener("keydown",O=>{O.key==="Enter"&&!O.shiftKey&&!O.isComposing&&(O.preventDefault(),ue())}),(Ue=d.querySelector('[data-act="attach"]'))==null||Ue.addEventListener("click",()=>W.click()),W.addEventListener("change",()=>{re("");for(const O of W.files){if(O.size>o.config.maxAttachmentBytes){re(`${O.name} is larger than ${Re(o.config.maxAttachmentBytes)}.`,"err");continue}ie(O,O.type,O.name)}W.value=""}),j&&!_o()&&(j.disabled=!0,j.title="This browser cannot record audio"),j==null||j.addEventListener("click",async()=>{if(oe){const O=oe;oe=null,j.classList.remove("recording"),j.innerHTML=B.mic,re("");const{blob:P,mime:ve}=await O.stop();P.size&&ie(P,ve,"Voice note");return}try{re(""),oe=await Xo({maxMs:o.config.maxRecordingMs,onTick:O=>re(`Recording — ${dt(O)}. Tap the mic to stop.`),onAutoStop:()=>j.click()}),j.classList.add("recording"),j.innerHTML=B.stopSquare}catch{oe=null,re("The microphone is not available — check the browser permission.","err")}}),o.canCallTeam(p).then(O=>{O&&(V.hidden=!1)}).catch(()=>{}),V.addEventListener("click",async()=>{V.disabled=!0,re("");try{const O=await o.startLiveCall(p);Rr(a,c,O)}catch(O){re(O.message.replace("[BugCatch] ",""),"err")}finally{V.disabled=!1}}),D.addEventListener("click",ue),c.addEventListener("click",O=>{var ve,J;const P=(J=(ve=O.target).closest)==null?void 0:J.call(ve,"[data-quick]");!P||F||(x.value=P.dataset.quick,ue())});async function ue(){var ve;const O=x.value.trim(),P=C.filter(J=>J.storageKey);if(!(F||!O&&!P.length)){F=!0,ke(),re(""),c.insertAdjacentHTML("beforeend",Qn({from:"reporter",body:O,at:new Date().toISOString(),attachments:P.map(J=>({type:"FILE",name:J.name,size:J.size}))},"pending")),Ve(c,{smooth:!0});try{await vn({apiBase:o.config.apiBase,bugId:p,body:O,attachments:P.map(J=>({storageKey:J.storageKey,mime:J.mime,name:J.name,size:J.size}))}),x.value="",x.style.height="auto",C.length=0,await it(p)}catch(J){(ve=c.querySelector(".msg-row.pending"))==null||ve.classList.replace("pending","failed"),re(J.message||"That could not be sent — try again.","err")}finally{F=!1,ke()}}}ke()}function Rr(a,c,p){const d=document.createElement("div");d.className="livecall",d.innerHTML=`
|
|
1016
1344
|
<span class="call-dot" aria-hidden="true"></span>
|
|
1017
1345
|
<span class="who" role="status" aria-live="polite">Calling the team…</span>
|
|
1018
1346
|
<span class="secs" hidden></span>
|
|
1347
|
+
${p!=null&&p.recorded?'<span class="call-kept">This call is recorded.</span>':""}
|
|
1019
1348
|
<button class="act" type="button" data-act="mute" hidden>Mute</button>
|
|
1020
|
-
<button class="act danger" type="button" data-act="hangup">Cancel</button>`,
|
|
1021
|
-
<div class="sys k-${
|
|
1022
|
-
<span class="dot" aria-hidden="true"></span>${
|
|
1023
|
-
|
|
1024
|
-
</div>`}function
|
|
1025
|
-
<button type="button" data-quick="Yes, this is the problem"
|
|
1026
|
-
<button type="button" data-quick="No, that is not the problem">
|
|
1349
|
+
<button class="act danger" type="button" data-act="hangup">Cancel</button>`,a.insertBefore(d,c);const w=d.querySelector(".who"),x=d.querySelector(".secs"),D=d.querySelector(".call-dot"),j=d.querySelector('[data-act="mute"]'),V=d.querySelector('[data-act="hangup"]');let W=null;const ne=()=>{W&&(clearInterval(W),W=null)},C=F=>{ne(),D.classList.add("off"),d.classList.add("over"),w.textContent=F,x.hidden=!0,j.hidden=!0,V.textContent="Dismiss",V.classList.remove("danger")},oe=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",j.hidden=!1,x.hidden=!1;const re=Date.now();ne(),W=setInterval(()=>{x.textContent=dt(Math.floor((Date.now()-re)/1e3))},1e3),x.textContent="0s"}F==="missed"&&C("Nobody was free. Send a message instead and the team will pick it up."),F==="ended"&&C("Call ended."),F==="failed"&&C("The call dropped.")});j.addEventListener("click",()=>{const F=p.setMuted(!p.isMuted());j.textContent=F?"Unmute":"Mute",w.textContent=F?"You're muted":"You're on a call with the team"}),V.addEventListener("click",()=>{oe(),ne(),o.endCall(),d.remove(),it(Te)})}let Te=null;return je=o.on("update",a=>{!Te||(a==null?void 0:a.bugId)!==Te||(o.markReportRead(Te),it(Te))}),t.appendChild(u),me(),R.focus(),{destroy:Ge,close:be}}function On(t){const e=t||[];if(!e.length)return"";let n=null,o=null;return e.map(i=>{var m,b,h,u;const r=_e(i.at),s=r&&r!==n?`<div class="day-sep" role="separator">${v(Ga(i.at))}</div>`:"";if(s&&(n=r,o=null),!i.from)return o=null,s+Fa(i);const l={from:i.from,author:(m=i.reply)==null?void 0:m.author,automated:(b=i.reply)==null?void 0:b.automated,body:(h=i.reply)==null?void 0:h.body,at:i.at,attachments:(u=i.reply)==null?void 0:u.attachments},g=Wa(l),f=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+Qn(l,f?"cont":"")}).join("")}function Wa(t){return t.from==="reporter"?"me":t.automated?"assistant":`team:${t.author||""}`}function Fa(t){const e=Hn(t.at);return`
|
|
1350
|
+
<div class="sys k-${v(t.kind)}">
|
|
1351
|
+
<span class="dot" aria-hidden="true"></span>${v(t.summary||"")}${e?`
|
|
1352
|
+
<span class="t">· ${v(e)}</span>`:""}
|
|
1353
|
+
</div>`}function Qn(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(er).join("")}</div>`:"";let g="",f="";if(r)g='<span class="msg-who auto">Assistant <span class="tag">AI</span></span>',f=`<span class="av bot" aria-hidden="true">${B.sparkle}</span>`;else if(!n){const b=t.author||"Support team";g=`<span class="msg-who">${v(b)}</span>`,f=`<span class="av" aria-hidden="true">${v(Za(b))}</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">
|
|
1354
|
+
<button type="button" data-quick="Yes, this is the problem">${B.check}That's right</button>
|
|
1355
|
+
<button type="button" data-quick="No, that is not the problem">That's not it</button>
|
|
1027
1356
|
</div>`:"";return`
|
|
1028
1357
|
<div class="msg-row ${n?"mine":"theirs"} ${r?"auto":""} ${e}">
|
|
1029
|
-
${p}
|
|
1030
|
-
<div class="bubble ${a?"":"silent"}">${s}${l}</div>
|
|
1031
1358
|
${f}
|
|
1032
|
-
|
|
1033
|
-
|
|
1359
|
+
${g}
|
|
1360
|
+
<div class="bubble ${o?"":"silent"}">${s}${l}</div>
|
|
1361
|
+
${m}
|
|
1362
|
+
<span class="msg-when">${v(Hn(t.at)||mt(t.at))}</span>
|
|
1363
|
+
</div>`}function Ya(){return`
|
|
1364
|
+
<div class="typing-row">
|
|
1365
|
+
<span class="av bot" aria-hidden="true">${B.sparkle}</span>
|
|
1366
|
+
<span class="msg-who auto">Assistant <span class="tag">AI</span></span>
|
|
1367
|
+
<div class="bubble typing" role="img" aria-label="The assistant is working on it"><i></i><i></i><i></i></div>
|
|
1368
|
+
</div>`}function qt({title:t,sub:e,id:n="",back:o=!1}){return`
|
|
1369
|
+
<div class="head">
|
|
1370
|
+
${o?`<button class="icon-btn back" type="button" data-act="back" aria-label="Back to your reports">${B.back}</button>`:""}
|
|
1371
|
+
<div class="mark" aria-hidden="true">${B.sparkle}</div>
|
|
1372
|
+
<div class="titles">
|
|
1373
|
+
<h2${n?` id="${n}"`:""}>${v(t)}</h2>
|
|
1374
|
+
<p>${v(e)}</p>
|
|
1375
|
+
</div>
|
|
1376
|
+
<button class="icon-btn" type="button" data-act="close" aria-label="Close">${B.close}</button>
|
|
1377
|
+
</div>`}function Dt(t,e){return`<div class="empty"><span class="empty-ico" aria-hidden="true">${e}</span>${v(t)}</div>`}function Xa(t){return Array.from({length:t},()=>`
|
|
1378
|
+
<div class="row" aria-hidden="true">
|
|
1379
|
+
<span class="row-ico"></span>
|
|
1380
|
+
<span class="who"><span class="skel" style="width:70%"></span><span class="skel" style="width:40%;height:9px"></span></span>
|
|
1381
|
+
</div>`).join("")+'<span class="sr-only">Loading…</span>'}function Ja(){return`
|
|
1382
|
+
<div aria-hidden="true" style="display:flex;flex-direction:column;gap:10px;padding-top:6px">
|
|
1383
|
+
<span class="skel" style="width:42%;height:34px;border-radius:16px;align-self:center"></span>
|
|
1384
|
+
<span class="skel" style="width:66%;height:52px;border-radius:18px"></span>
|
|
1385
|
+
<span class="skel" style="width:48%;height:38px;border-radius:18px;align-self:flex-end"></span>
|
|
1386
|
+
</div>
|
|
1387
|
+
<span class="sr-only">Loading…</span>`}const Nt=new WeakMap;function Za(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 _e(t){const e=new Date(t);return Number.isFinite(e.getTime())?`${e.getFullYear()}-${e.getMonth()}-${e.getDate()}`:null}function Ga(t){const e=new Date(t),n=new Date,o=new Date(n.getFullYear(),n.getMonth(),n.getDate()-1);if(_e(e)===_e(n))return"Today";if(_e(e)===_e(o))return"Yesterday";try{return e.toLocaleDateString(void 0,{weekday:"short",day:"numeric",month:"short",...e.getFullYear()!==n.getFullYear()?{year:"numeric"}:{}})}catch{return mt(t)}}function Hn(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 er(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>`:`
|
|
1034
1388
|
<a class="file" href="${n}" target="_blank" rel="noopener noreferrer" download>
|
|
1035
1389
|
<span class="nm">${e}</span>
|
|
1036
|
-
${t.size?`<span class="sz">${
|
|
1037
|
-
</a>`}function ao(t){return t.scrollHeight-t.scrollTop-t.clientHeight<40}function wt(t){t.scrollTop=t.scrollHeight}function vt(t){return{OPEN:"Open",IN_PROGRESS:"In progress",RESOLVED:"Fixed",CLOSED:"Closed"}[t]||String(t||"").toLowerCase()}function et(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 a=Math.floor(n/30);return a===1?"a month ago":`${a} months ago`}function x(t){return String(t).replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e])}const sn=8e3;function oo(t=sn){const e=window.AudioContext||window.webkitAudioContext;try{return new e({sampleRate:t})}catch{return new e}}async function ro(t){if(t.state==="suspended")try{await t.resume()}catch{}}function io(t,e,n){const a=e/n,i=new Float32Array(Math.floor(t.length/a));for(let r=0;r<i.length;r++){const s=Math.floor(r*a),l=Math.min(Math.floor((r+1)*a),t.length);let p=0;for(let f=s;f<l;f++)p+=t[f];i[r]=l>s?p/(l-s):0}return i}const so=132,cn=32635;function co(t){const e=new Uint8Array(t.length);for(let n=0;n<t.length;n++){const a=Math.max(-1,Math.min(1,t[n]));let i=Math.round(a*32767);const r=i<0?128:0;r&&(i=-i),i>cn&&(i=cn),i+=so;let s=7;for(let p=16384;!(i&p)&&s>0;s--,p>>=1);const l=i>>s+3&15;e[n]=~(r|s<<4|l)&255}return e}const lo=512;function uo(t,e,n,a=sn){const i=t.createMediaStreamSource(e),r=t.createScriptProcessor(lo,1,1);r.onaudioprocess=l=>{const p=l.inputBuffer.getChannelData(0),f=t.sampleRate===a?p:io(p,t.sampleRate,a);n(co(f))},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 po="voiceai.realtime.v1",ho=[{urls:"stun:stun.l.google.com:19302"}];function xt(){return navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:1}})}function kt(){var t;return typeof window<"u"&&typeof window.RTCPeerConnection=="function"&&!!((t=navigator.mediaDevices)!=null&&t.getUserMedia)}async function ln({realtimeUrl:t,stream:e,ringsUntil:n,onState:a,onAudioBlocked:i,onMicrophone:r}){const s=(m,v)=>{try{a==null||a(m,v)}catch{}},l=m=>{try{i==null||i(m)}catch{}},p=m=>{try{r==null||r(m)}catch{}};if(!kt()){const m=new Error("[BugCatch] this browser cannot make live calls.");throw s("failed",{reason:"unsupported"}),m}let f=e;if(!f)try{f=await xt()}catch(m){throw s("failed",{reason:"microphone",error:String((m==null?void 0:m.name)||m)}),m}let y;try{y=await fo(t)}catch(m){throw un(f),s("failed",{reason:"connect"}),m}let h=null,b=null,g=!1,S=null,A=null,ie=null,C=!1,$=null,T=null,L=!1;const X=[];let U=!1,j=!1,V=!1,E=null,se=null,ne=0;const De=2e3,xe=5e3,at=3e4,ke=4,K=(m,v)=>{if(!g){g=!0,S&&(clearTimeout(S),S=null),ce(),T==null||T(),T=null,ot(),$==null||$(),$=null;try{h==null||h.close()}catch{}un(f),b&&(b.srcObject=null,b.remove(),b=null);try{y.readyState===WebSocket.OPEN&&y.close(1e3,"call over")}catch{}s(m,v)}},ae=m=>{y.readyState===WebSocket.OPEN&&y.send(JSON.stringify(m))},Le=async()=>{if(L||ie||g||!A||!A.audio&&!A.transcript)return;L=!0;const m=A.encoding||"mulaw";if(m!=="mulaw"){dn(`this call is kept as ${m}, which this SDK cannot produce.`);return}const v=Number(A.sample_rate)||8e3;try{const I=oo(v);if(await ro(I),g){try{I.close()}catch{}return}const F=uo(I,f,W=>{y.readyState===WebSocket.OPEN&&y.send(W)},v);ie={context:I,tap:F}}catch(I){dn(`could not send the copy this call is keeping: ${String((I==null?void 0:I.message)||I)}`)}};function ot(){if(L=!1,!ie)return;const{context:m,tap:v}=ie;ie=null;try{v.stop()}catch{}try{m.close()}catch{}}function Ne(){var v;if(!b||g)return;let m;try{m=(v=b.play)==null?void 0:v.call(b)}catch{Be();return}if(!m||typeof m.then!="function"){me();return}m.then(me,Be)}function me(){$==null||$(),$=null,C&&(C=!1,l(!1))}function Be(){g||(C||(C=!0,l(!0)),St())}function St(){if($||typeof window>"u"||typeof window.addEventListener!="function")return;const m=()=>Ne(),v=["pointerdown","keydown","touchstart"];for(const I of v)window.addEventListener(I,m,{capture:!0,passive:!0});$=()=>{for(const I of v)try{window.removeEventListener(I,m,{capture:!0})}catch{}}}const Ae=m=>{h=new RTCPeerConnection({iceServers:m!=null&&m.length?m:ho});for(const v of f.getAudioTracks())h.addTrack(v,f);T=de(),h.onicecandidate=v=>{v.candidate&&ae({type:"signal",signal:{kind:"candidate",payload:v.candidate.toJSON()}})},h.ontrack=v=>{var F;const[I]=v.streams;I&&(b||(b=document.createElement("audio"),b.autoplay=!0,b.playsInline=!0,(F=b.setAttribute)==null||F.call(b,"playsinline",""),b.style.cssText="position:fixed;width:0;height:0;opacity:0;pointer-events:none",document.body.appendChild(b)),b.srcObject=I,Ne())},h.onconnectionstatechange=Se,h.oniceconnectionstatechange=Se};function Se(){if(!h||g)return;const m=h.connectionState||h.iceConnectionState;if(m==="connected"||m==="completed"){j=!0,ne=0,ce(),V=!1,s("connected");return}if(m==="disconnected"){if(!j)return;Ue(De);return}if(m==="failed"){if(!j){K("failed",{reason:"ice"});return}Ue(0)}}function Ue(m){g||(V||(V=!0,s("reconnecting"),se=setTimeout(()=>K("failed",{reason:"network"}),at)),!E&&(E=setTimeout(Re,m)))}function ce(){E&&(clearTimeout(E),E=null),se&&(clearTimeout(se),se=null)}async function Re(){if(E=null,g||!h)return;const m=h.connectionState||h.iceConnectionState;if(!(m==="connected"||m==="completed")){if(!U){ae({type:"signal",signal:{kind:"restart",payload:{}}}),E=setTimeout(Re,xe);return}ne>=ke||(ne+=1,await H(),E=setTimeout(Re,xe))}}async function H(){if(!(g||!h))try{const m=await h.createOffer({iceRestart:!0});await h.setLocalDescription(m),ae({type:"signal",signal:{kind:"offer",payload:h.localDescription.toJSON()}})}catch{}}function de(){let v=null,I=!1;const F=W=>{if(v&&(clearTimeout(v),v=null),W){if(!I)return;I=!1,p(!0);return}I||(v=setTimeout(()=>{v=null,!g&&(I=!0,p(!1))},600))};for(const W of f.getAudioTracks())W.onmute=()=>F(!1),W.onunmute=()=>F(!0),W.onended=()=>{v&&(clearTimeout(v),v=null),!I&&(I=!0,p(!1))};return()=>{v&&(clearTimeout(v),v=null);for(const W of f.getAudioTracks())W.onmute=null,W.onunmute=null,W.onended=null}}const rt=async()=>{try{const m=await h.createOffer();await h.setLocalDescription(m),ae({type:"signal",signal:{kind:"offer",payload:h.localDescription.toJSON()}})}catch{K("failed",{reason:"negotiation"})}},Ct=async m=>{try{await h.setRemoteDescription(new RTCSessionDescription(m));const v=await h.createAnswer();await h.setLocalDescription(v),ae({type:"signal",signal:{kind:"answer",payload:h.localDescription.toJSON()}}),await Qe()}catch{K("failed",{reason:"negotiation"})}},Qe=async()=>{for(;X.length;){const m=X.shift();try{await h.addIceCandidate(new RTCIceCandidate(m))}catch{}}};let Ce=Promise.resolve();y.onmessage=m=>{Ce=Ce.then(()=>$t(m)).catch(()=>{})};const $t=async m=>{var I,F,W,$e,it;if(typeof m.data!="string")return;let v;try{v=JSON.parse(m.data)}catch{return}switch(v.type){case"session.created":{if(Ae((I=v.data)==null?void 0:I.ice_servers),A=((F=v.data)==null?void 0:F.capture)||null,(W=v.data)!=null&&W.peer_present){s("connecting"),Le();break}s("ringing"),je();break}case"peer.joined":{S&&(clearTimeout(S),S=null),s("connecting"),U=!!(($e=v.data)!=null&&$e.should_offer),U&&await rt(),Le();break}case"signal":{if(!h)break;const{kind:Ee,payload:ge}=v.data||{};if(Ee==="offer")await Ct(ge);else if(Ee==="answer")try{await h.setRemoteDescription(new RTCSessionDescription(ge)),await Qe()}catch{K("failed",{reason:"negotiation"})}else if(Ee==="candidate")if(h.remoteDescription)try{await h.addIceCandidate(new RTCIceCandidate(ge))}catch{}else X.push(ge);else Ee==="restart"&&U&&await H();break}case"peer.left":K("ended",{reason:"peer_left"});break;case"error":K("failed",{reason:"server",detail:(it=v.error)==null?void 0:it.detail});break;case"session.ended":K("ended",{reason:"server"});break}};y.onclose=()=>K("ended",{reason:"disconnected"}),y.onerror=()=>K("failed",{reason:"socket"});function je(){if(!n)return;const m=new Date(n).getTime()-Date.now();Number.isFinite(m)&&(S=setTimeout(()=>K("missed"),Math.max(0,m)))}return{hangUp(){ae({type:"session.end"}),K("ended",{reason:"hung_up"})},setMuted(m){for(const v of f.getAudioTracks())v.enabled=!m;return m},isMuted(){return f.getAudioTracks().every(m=>!m.enabled)}}}function fo(t){return new Promise((e,n)=>{let a;try{a=new WebSocket(t,po)}catch(i){n(i);return}a.onopen=()=>{a.onopen=null,a.onerror=null,e(a)},a.onerror=()=>{a.onopen=null,a.onerror=null,n(new Error("[BugCatch] could not connect the call."))}})}function dn(t){try{console.warn(`[BugCatch] ${t}`)}catch{}}function un(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 tt={cycleMs:3e3,peak:.045,bursts:[{at:0,seconds:.4,hz:[440,660]},{at:.55,seconds:.4,hz:[440,660]}]};let pn=!1;if(typeof window<"u"&&window.addEventListener)for(const t of["pointerdown","keydown","touchstart"])window.addEventListener(t,()=>{pn=!0},{once:!0,capture:!0,passive:!0});function go(){var t;try{return!!((t=navigator.userActivation)!=null&&t.hasBeenActive)}catch{return!1}}function mo(){if(!pn&&!go())return()=>{};const t=typeof window<"u"&&(window.AudioContext||window.webkitAudioContext);if(!t)return()=>{};let e;try{e=new t}catch{return()=>{}}let n=!1,a=null;const i=[],r=({at:p,seconds:f,hz:y})=>{const h=e.currentTime+p,b=e.createGain();b.connect(e.destination),b.gain.setValueAtTime(0,h),b.gain.linearRampToValueAtTime(tt.peak,h+.02),b.gain.setValueAtTime(tt.peak,h+f-.06),b.gain.linearRampToValueAtTime(0,h+f);for(const g of y){const S=e.createOscillator();S.type="sine",S.frequency.value=g,S.connect(b),S.start(h),S.stop(h+f+.02),i.push(S)}},s=()=>{if(!n)try{tt.bursts.forEach(r)}catch{}},l=()=>{n||(s(),a=setInterval(s,tt.cycleMs))};return e.state==="running"?l():e.resume().then(l).catch(()=>{}),()=>{n=!0,a&&clearInterval(a);for(const p of i)try{p.stop()}catch{}try{e.close()}catch{}}}function bo(t){const e=Date.parse(t||"");return Number.isNaN(e)?0:Math.max(0,Math.round((e-Date.now())/1e3))}const hn=["flex:1","padding:9px 12px","border:0","border-radius:8px","font:600 13px/1.4 inherit","cursor:pointer"].join(";");function yo({call:t,accent:e="#5b6ef5",position:n="bottom-right",onAnswer:a,onDecline:i,onExpire:r}){if(typeof document>"u"||!document.body)return()=>{};const[s,l]=String(n).split("-"),p=document.createElement("div");p.setAttribute("role","alertdialog"),p.setAttribute("aria-live","assertive"),p.setAttribute("aria-label",`${t.from||"Support"} is calling about ${t.reference}`),p.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 f=document.createElement("div");f.style.cssText="font-weight:600;font-size:14px;margin-bottom:2px",f.textContent=`${t.from||"Support"} is calling`;const y=document.createElement("div");y.style.cssText="color:#a4abba;margin-bottom:10px",y.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 b=document.createElement("button");b.type="button",b.textContent="Answer",b.style.cssText=`${hn};background:${e};color:#fff`;const g=document.createElement("button");g.type="button",g.textContent="Not now",g.style.cssText=`${hn};background:#2b2f3a;color:#c8cedb`;const S=document.createElement("div");if(S.style.cssText="display:flex;gap:8px",S.append(b,g),p.append(f,y,h),t.recorded){const L=document.createElement("div");L.style.cssText="color:#7d8598;font-size:12px;margin-bottom:10px",L.textContent="This call will be recorded.",p.append(L)}p.append(S),document.body.appendChild(p);const A=mo();let ie=!1,C=null;const $=()=>{ie||(ie=!0,C&&clearInterval(C),A(),p.style.opacity="0",p.style.transform="translateY(8px)",setTimeout(()=>p.remove(),250))},T=()=>{const L=bo(t.ringsUntil);if(h.textContent=L?`Ringing — ${L}s`:"The call has stopped ringing.",!L){$();try{r==null||r()}catch{}}};return T(),C=setInterval(T,1e3),b.addEventListener("click",()=>{$();try{a==null||a()}catch{}}),g.addEventListener("click",()=>{$();try{i==null||i()}catch{}}),requestAnimationFrame(()=>{p.style.opacity="1",p.style.transform="translateY(0)"}),$}const fn={name:"@sphoro/bugcatch-web-sdk",version:"0.14.4"},nt={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};class gn{constructor(){zn(this,B);this.config={...nt},this.started=!1,this.teardown=[],this.consoleBuffer=Pe(nt.consoleLimit),this.networkBuffer=Pe(nt.networkLimit),this.stopWatching=null,this.notifier=null,this.call=null,this.callStarting=!1,this.ringing=null,this.listeners=new Map}init(e={}){if(this.started)return console.warn("[BugCatch] init() called twice — ignoring the second call."),this;if(this.config={...nt,...e},!this.config.apiKey)throw new Error("[BugCatch] apiKey is required.");if(!this.config.projectId)throw new Error("[BugCatch] projectId is required.");return this.consoleBuffer=Pe(this.config.consoleLimit),this.networkBuffer=Pe(this.config.networkLimit),this.config.captureConsole&&this.teardown.push(Wn(this.consoleBuffer)),this.config.captureNetwork&&this.teardown.push(Yn(this.networkBuffer)),this.config.captureErrors&&this.teardown.push(Xn(this.consoleBuffer)),this.config.widget&&this.teardown.push(Ja(this)),this.config.updates&&re()&&(this.startWatching(),queueMicrotask(()=>_(this,B,ye).call(this))),this.started=!0,this}startWatching(){if(this.stopWatching)return this;this.stopWatching=Ba({apiBase:this.config.apiBase,onUpdate:n=>{if(n!=null&&n.incomingCall){_(this,B,qn).call(this,n);return}if(n!=null&&n.callWithdrawn){_(this,B,dt).call(this,n.callWithdrawn.callId);return}const a=va(n);typeof this.config.onUpdate=="function"&&this.config.onUpdate(a),this.emit("update",a),_(this,B,ye).call(this)}}),this.teardown.push(()=>{var n;return(n=this.stopWatching)==null?void 0:n.call(this)}),this.teardown.push(()=>_(this,B,dt).call(this));const e=n=>{(n.key===Je||n.key===null)&&_(this,B,ye).call(this)};return window.addEventListener("storage",e),this.teardown.push(()=>window.removeEventListener("storage",e)),this}on(e,n){if(typeof n!="function")return()=>{};const a=this.listeners.get(e)||new Set;return a.add(n),this.listeners.set(e,a),()=>this.off(e,n)}off(e,n){var a;return(a=this.listeners.get(e))==null||a.delete(n),this}emit(e,n){for(const a of this.listeners.get(e)||[])try{a(n)}catch(i){console.error("[BugCatch] an update listener threw:",i)}}unreadUpdates(){return qe()}markUpdateRead(e){return xa(e),_(this,B,ye).call(this),this}markReportRead(e){return ka(e),_(this,B,ye).call(this),this}markUpdatesRead(){return Kt(),_(this,B,ye).call(this),this}async verifyFix(e,{result:n,resolvedAt:a,note:i}){const r=n==="STILL_HAPPENING"?await _(this,B,lt).call(this,{}):{};return Ua({apiBase:this.config.apiBase,bugId:e,result:n,resolvedAt:a,note:i,metadata:r.metadata,logs:r.logs})}myReport(e){return $a({apiBase:this.config.apiBase,bugId:e})}replyToReport(e,{body:n,attachments:a}={}){return Pt({apiBase:this.config.apiBase,bugId:e,body:n,attachments:a})}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 Ea({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.kinds)}canCall(e){return this.config.voice?_t({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 _(this,B,Hn).call(this,e)}finally{this.callStarting=!1}}startLiveCall(e){return this.startCall(e)}canCallTeam(e){return this.config.voice?_t({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 Ca({apiBase:this.config.apiBase})}hasReports(){return!!re()}forgetReports(){var e;return(e=this.stopWatching)==null||e.call(this),this.stopWatching=null,this.endCall(),ve(),_(this,B,ye).call(this),this}identify(e){return this.config.user=e,this}async uploadAttachment(e,n,a){if(!this.started)throw new Error("[BugCatch] call init() first.");return ia({apiBase:this.config.apiBase,apiKey:this.config.apiKey,blob:e,mime:n||(e==null?void 0:e.type),onProgress:a})}async report({title:e,description:n="",priority:a="MEDIUM",metadata:i={},attachments:r=[],references:s=[]}={}){if(!this.started)throw new Error("[BugCatch] call init() first.");if(!e||!e.trim())throw new Error("[BugCatch] a title is required.");const l=(r||[]).filter(b=>(b==null?void 0:b.storageKey)&&Fe(b.mime,_e)).map(b=>({type:b.type==="VIDEO"?"VIDEO":"SCREENSHOT",storageKey:b.storageKey,mime:Fe(b.mime,_e)}));let p=await _(this,B,lt).call(this,{title:e.trim(),description:n,priority:a,metadata:i,media:l,references:s});if(typeof this.config.beforeSend=="function"){const b=this.config.beforeSend(p);if(b===!1)return{cancelled:!0};b&&typeof b=="object"&&(p=b)}const f=fe(this.config.projectId),y=await fetch(`${this.config.apiBase}/ingest/bugs`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey,...f!=null&&f.token?{"X-BugCatch-Session":f.token}:{}},body:JSON.stringify(p)});if(y.status===401&&f&>(this.config.projectId),!y.ok){let b="";try{const g=await y.json();b=Array.isArray(g.message)?g.message.join(", "):g.message||""}catch{}throw new Error(`[BugCatch] report failed (HTTP ${y.status})${b?": "+b:""}`)}const h=await y.json();return h.watchToken&&(bt(h.watchToken),this.config.updates&&this.startWatching()),h}loginState(){var e;return fa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:(e=fe(this.config.projectId))==null?void 0:e.token})}referenceFields(){return this.refFields?this.refFields:(this.refFields=Fa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.refFields)}signedInAs(){const e=fe(this.config.projectId);return e?{email:e.email,name:e.name}:null}signIn(e,{name:n}={}){var a;return ga({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,name:n??((a=this.config.user)==null?void 0:a.name)??null})}async confirmSignIn(e,n){var i;const a=await ma({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:a.email,name:a.name||void 0}),this.emit("signin",{email:a.email,name:a.name}),{email:a.email,name:a.name}}async signOut(){var a;const e=fe(this.config.projectId),n=await ba({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:e==null?void 0:e.token});return e!=null&&e.email&&((a=this.config.user)==null?void 0:a.email)===e.email&&(this.config.user=null),this.emit("signout",{email:(e==null?void 0:e.email)||null}),n}knownIssues(){var e;return Ha({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,sessionToken:(e=fe(this.config.projectId))==null?void 0:e.token})}matchKnownIssues(e,{signal:n}={}){var a;return qa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,q:e||"",sessionToken:(a=fe(this.config.projectId))==null?void 0:a.token,signal:n})}async meToo(e,{email:n,name:a}={}){var s,l;const i=fe(this.config.projectId),r=await Ht();return Da({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:a||((l=this.config.user)==null?void 0:l.name),metadata:{...r,user:this.config.user||void 0,sdk:fn},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},sessionToken:i==null?void 0:i.token})}snapshot(){return{console:this.consoleBuffer.all(),network:this.networkBuffer.all(),environment:qt()}}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}}B=new WeakSet,ye=function(){const e=qe();if(this.emit("unread",{count:e.length,updates:e}),!!this.config.updateToast){if(!this.notifier){if(!e.length)return;this.notifier=Oa({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)}},lt=async function({title:e,description:n="",priority:a="MEDIUM",metadata:i={},media:r=[],references:s=[]}){var l,p;return{projectId:this.config.projectId,...e?{title:e}:{},description:n,priority:a,url:window.location.href,reporterEmail:(l=this.config.user)==null?void 0:l.email,reporterName:(p=this.config.user)==null?void 0:p.name,metadata:{...await Ht(),release:this.config.release,environment:this.config.environment,buildId:this.config.buildId,...i,user:this.config.user||void 0,sdk:fn},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},...r.length?{attachments:r}:{},...s.length?{references:s}:{}}},On=async function(){const e=fe(this.config.projectId);let n;try{n=await Ta({apiBase:this.config.apiBase,apiKey:this.config.apiKey,token:e==null?void 0:e.token,payload:await _(this,B,lt).call(this,{})})}catch(a){throw a.status===401&&e&>(this.config.projectId),a}return n.watchToken&&(bt(n.watchToken),this.config.updates&&this.startWatching()),n},Hn=async function(e){if(!kt())throw new Error("[BugCatch] this browser cannot make live calls.");const n=await xt();let a,i=null;try{if(e)a=await Ia({apiBase:this.config.apiBase,bugId:e});else if(i=await _(this,B,On).call(this),e=i.id,a=i.live_call,!(a!=null&&a.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 ln({realtimeUrl:a.realtime_url,stream:n,ringsUntil:a.rings_until,onState:(s,l)=>{(s==="ended"||s==="failed"||s==="missed")&&(this.call=null),s==="missed"&&Ma({apiBase:this.config.apiBase,bugId:e,callId:a.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:a.call_id,live:!0,filed:i})},qn=function(e){var i;const n=e.incomingCall;if(!this.config.voice||!kt()||((i=this.ringing)==null?void 0:i.callId)===n.callId||this.call||this.callStarting)return;_(this,B,dt).call(this);const a=yo({call:n,accent:this.config.accent,position:this.config.widgetPosition,onAnswer:()=>{_(this,B,Dn).call(this,e.bugId,n)},onDecline:()=>{this.ringing=null,Ft({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"})},dt=function(e){if(this.ringing&&!(e&&this.ringing.callId!==e)){try{this.ringing.dismiss()}catch{}this.ringing=null}},Dn=async function(e,n){if(this.ringing=null,this.call||this.callStarting)return;this.callStarting=!0;let a;try{a=await xt()}catch(r){this.callStarting=!1,Ft({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 La({apiBase:this.config.apiBase,bugId:e,callId:n.callId})}catch(r){for(const s of a.getTracks())s.stop();this.callStarting=!1,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"missed",error:r});return}try{const r=await ln({realtimeUrl:i.realtime_url,stream:a,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 a.getTracks())s.stop();this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r})}finally{this.callStarting=!1}};const mn=new gn;return mn.Client=gn,mn});
|
|
1390
|
+
${t.size?`<span class="sz">${v(Re(t.size))}</span>`:""}
|
|
1391
|
+
</a>`}function qn(t){return t.scrollHeight-t.scrollTop-t.clientHeight<40}function Ve(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 Ut(t){return{OPEN:"Open",IN_PROGRESS:"In progress",RESOLVED:"Fixed",CLOSED:"Closed"}[t]||String(t||"").toLowerCase()}function mt(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=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e])}const Dn=8e3;function tr(t=Dn){const e=window.AudioContext||window.webkitAudioContext;try{return new e({sampleRate:t})}catch{return new e}}async function nr(t){if(t.state==="suspended")try{await t.resume()}catch{}}function or(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 f=s;f<l;f++)g+=t[f];i[r]=l>s?g/(l-s):0}return i}const ar=132,Nn=32635;function rr(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>Nn&&(i=Nn),i+=ar;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 ir=512;function sr(t,e,n,o=Dn){const i=t.createMediaStreamSource(e),r=t.createScriptProcessor(ir,1,1);r.onaudioprocess=l=>{const g=l.inputBuffer.getChannelData(0),f=t.sampleRate===o?g:or(g,t.sampleRate,o);n(rr(f))},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 cr="voiceai.realtime.v1",lr=[{urls:"stun:stun.l.google.com:19302"}];function Kt(){return navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:1}})}function Pt(){var t;return typeof window<"u"&&typeof window.RTCPeerConnection=="function"&&!!((t=navigator.mediaDevices)!=null&&t.getUserMedia)}async function Un({realtimeUrl:t,stream:e,ringsUntil:n,onState:o,onAudioBlocked:i,onMicrophone:r}){const s=(y,E)=>{try{o==null||o(y,E)}catch{}},l=y=>{try{i==null||i(y)}catch{}},g=y=>{try{r==null||r(y)}catch{}};if(!Pt()){const y=new Error("[BugCatch] this browser cannot make live calls.");throw s("failed",{reason:"unsupported"}),y}let f=e;if(!f)try{f=await Kt()}catch(y){throw s("failed",{reason:"microphone",error:String((y==null?void 0:y.name)||y)}),y}let m;try{m=await dr(t)}catch(y){throw Pn(f),s("failed",{reason:"connect"}),y}let b=null,h=null,u=!1,k=null,R=null,z=null,S=!1,M=null,I=null,A=!1;const H=[];let U=!1,T=!1,Q=!1,$=null,Y=null,ze=0;const fe=2e3,Oe=5e3,Qe=3e4,xt=4,G=(y,E)=>{if(!u){u=!0,k&&(clearTimeout(k),k=null),Ge(),I==null||I(),I=null,Ye(),M==null||M(),M=null;try{b==null||b.close()}catch{}Pn(f),h&&(h.srcObject=null,h.remove(),h=null);try{m.readyState===WebSocket.OPEN&&m.close(1e3,"call over")}catch{}s(y,E)}},ge=y=>{m.readyState===WebSocket.OPEN&&m.send(JSON.stringify(y))},xe=async()=>{if(A||z||u||!R||!R.audio&&!R.transcript)return;A=!0;const y=R.encoding||"mulaw";if(y!=="mulaw"){Kn(`this call is kept as ${y}, which this SDK cannot produce.`);return}const E=Number(R.sample_rate)||8e3;try{const q=tr(E);if(await nr(q),u){try{q.close()}catch{}return}const K=sr(q,f,_=>{m.readyState===WebSocket.OPEN&&m.send(_)},E);z={context:q,tap:K}}catch(q){Kn(`could not send the copy this call is keeping: ${String((q==null?void 0:q.message)||q)}`)}};function Ye(){if(A=!1,!z)return;const{context:y,tap:E}=z;z=null;try{E.stop()}catch{}try{y.close()}catch{}}function Xe(){var E;if(!h||u)return;let y;try{y=(E=h.play)==null?void 0:E.call(h)}catch{Ee();return}if(!y||typeof y.then!="function"){Je();return}y.then(Je,Ee)}function Je(){M==null||M(),M=null,S&&(S=!1,l(!1))}function Ee(){u||(S||(S=!0,l(!0)),Ze())}function Ze(){if(M||typeof window>"u"||typeof window.addEventListener!="function")return;const y=()=>Xe(),E=["pointerdown","keydown","touchstart"];for(const q of E)window.addEventListener(q,y,{capture:!0,passive:!0});M=()=>{for(const q of E)try{window.removeEventListener(q,y,{capture:!0})}catch{}}}const Ft=y=>{b=new RTCPeerConnection({iceServers:y!=null&&y.length?y:lr});for(const E of f.getAudioTracks())b.addTrack(E,f);I=ee(),b.onicecandidate=E=>{E.candidate&&ge({type:"signal",signal:{kind:"candidate",payload:E.candidate.toJSON()}})},b.ontrack=E=>{var K;const[q]=E.streams;q&&(h||(h=document.createElement("audio"),h.autoplay=!0,h.playsInline=!0,(K=h.setAttribute)==null||K.call(h,"playsinline",""),h.style.cssText="position:fixed;width:0;height:0;opacity:0;pointer-events:none",document.body.appendChild(h)),h.srcObject=q,Xe())},b.onconnectionstatechange=Be,b.oniceconnectionstatechange=Be};function Be(){if(!b||u)return;const y=b.connectionState||b.iceConnectionState;if(y==="connected"||y==="completed"){T=!0,ze=0,Ge(),Q=!1,s("connected");return}if(y==="disconnected"){if(!T)return;je(fe);return}if(y==="failed"){if(!T){G("failed",{reason:"ice"});return}je(0)}}function je(y){u||(Q||(Q=!0,s("reconnecting"),Y=setTimeout(()=>G("failed",{reason:"network"}),Qe)),!$&&($=setTimeout(be,y)))}function Ge(){$&&(clearTimeout($),$=null),Y&&(clearTimeout(Y),Y=null)}async function be(){if($=null,u||!b)return;const y=b.connectionState||b.iceConnectionState;if(!(y==="connected"||y==="completed")){if(!U){ge({type:"signal",signal:{kind:"restart",payload:{}}}),$=setTimeout(be,Oe);return}ze>=xt||(ze+=1,await et(),$=setTimeout(be,Oe))}}async function et(){if(!(u||!b))try{const y=await b.createOffer({iceRestart:!0});await b.setLocalDescription(y),ge({type:"signal",signal:{kind:"offer",payload:b.localDescription.toJSON()}})}catch{}}function ee(){let E=null,q=!1;const K=_=>{if(E&&(clearTimeout(E),E=null),_){if(!q)return;q=!1,g(!0);return}q||(E=setTimeout(()=>{E=null,!u&&(q=!0,g(!1))},600))};for(const _ of f.getAudioTracks())_.onmute=()=>K(!1),_.onunmute=()=>K(!0),_.onended=()=>{E&&(clearTimeout(E),E=null),!q&&(q=!0,g(!1))};return()=>{E&&(clearTimeout(E),E=null);for(const _ of f.getAudioTracks())_.onmute=null,_.onunmute=null,_.onended=null}}const kt=async()=>{try{const y=await b.createOffer();await b.setLocalDescription(y),ge({type:"signal",signal:{kind:"offer",payload:b.localDescription.toJSON()}})}catch{G("failed",{reason:"negotiation"})}},St=async y=>{try{await b.setRemoteDescription(new RTCSessionDescription(y));const E=await b.createAnswer();await b.setLocalDescription(E),ge({type:"signal",signal:{kind:"answer",payload:b.localDescription.toJSON()}}),await me()}catch{G("failed",{reason:"negotiation"})}},me=async()=>{for(;H.length;){const y=H.shift();try{await b.addIceCandidate(new RTCIceCandidate(y))}catch{}}};let tt=Promise.resolve();m.onmessage=y=>{tt=tt.then(()=>Yt(y)).catch(()=>{})};const Yt=async y=>{var q,K,_,te,se;if(typeof y.data!="string")return;let E;try{E=JSON.parse(y.data)}catch{return}switch(E.type){case"session.created":{if(Ft((q=E.data)==null?void 0:q.ice_servers),R=((K=E.data)==null?void 0:K.capture)||null,(_=E.data)!=null&&_.peer_present){s("connecting"),xe();break}s("ringing"),nt();break}case"peer.joined":{k&&(clearTimeout(k),k=null),s("connecting"),U=!!((te=E.data)!=null&&te.should_offer),U&&await kt(),xe();break}case"signal":{if(!b)break;const{kind:ae,payload:X}=E.data||{};if(ae==="offer")await St(X);else if(ae==="answer")try{await b.setRemoteDescription(new RTCSessionDescription(X)),await me()}catch{G("failed",{reason:"negotiation"})}else if(ae==="candidate")if(b.remoteDescription)try{await b.addIceCandidate(new RTCIceCandidate(X))}catch{}else H.push(X);else ae==="restart"&&U&&await et();break}case"peer.left":G("ended",{reason:"peer_left"});break;case"error":G("failed",{reason:"server",detail:(se=E.error)==null?void 0:se.detail});break;case"session.ended":G("ended",{reason:"server"});break}};m.onclose=()=>G("ended",{reason:"disconnected"}),m.onerror=()=>G("failed",{reason:"socket"});function nt(){if(!n)return;const y=new Date(n).getTime()-Date.now();Number.isFinite(y)&&(k=setTimeout(()=>G("missed"),Math.max(0,y)))}return{hangUp(){ge({type:"session.end"}),G("ended",{reason:"hung_up"})},setMuted(y){for(const E of f.getAudioTracks())E.enabled=!y;return y},isMuted(){return f.getAudioTracks().every(y=>!y.enabled)}}}function dr(t){return new Promise((e,n)=>{let o;try{o=new WebSocket(t,cr)}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 Kn(t){try{console.warn(`[BugCatch] ${t}`)}catch{}}function Pn(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 yt={cycleMs:3e3,peak:.045,bursts:[{at:0,seconds:.4,hz:[440,660]},{at:.55,seconds:.4,hz:[440,660]}]};let _n=!1;if(typeof window<"u"&&window.addEventListener)for(const t of["pointerdown","keydown","touchstart"])window.addEventListener(t,()=>{_n=!0},{once:!0,capture:!0,passive:!0});function ur(){var t;try{return!!((t=navigator.userActivation)!=null&&t.hasBeenActive)}catch{return!1}}function pr(){if(!_n&&!ur())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:f,hz:m})=>{const b=e.currentTime+g,h=e.createGain();h.connect(e.destination),h.gain.setValueAtTime(0,b),h.gain.linearRampToValueAtTime(yt.peak,b+.02),h.gain.setValueAtTime(yt.peak,b+f-.06),h.gain.linearRampToValueAtTime(0,b+f);for(const u of m){const k=e.createOscillator();k.type="sine",k.frequency.value=u,k.connect(h),k.start(b),k.stop(b+f+.02),i.push(k)}},s=()=>{if(!n)try{yt.bursts.forEach(r)}catch{}},l=()=>{n||(s(),o=setInterval(s,yt.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 hr(t){const e=Date.parse(t||"");return Number.isNaN(e)?0:Math.max(0,Math.round((e-Date.now())/1e3))}const Vn=["flex:1","padding:9px 12px","border:0","border-radius:8px","font:600 13px/1.4 inherit","cursor:pointer"].join(";");function fr({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 f=document.createElement("div");f.style.cssText="font-weight:600;font-size:14px;margin-bottom:2px",f.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 b=document.createElement("div");b.style.cssText="color:#7d8598;font-size:12px;margin-bottom:10px";const h=document.createElement("button");h.type="button",h.textContent="Answer",h.style.cssText=`${Vn};background:${e};color:#fff`;const u=document.createElement("button");u.type="button",u.textContent="Not now",u.style.cssText=`${Vn};background:#2b2f3a;color:#c8cedb`;const k=document.createElement("div");if(k.style.cssText="display:flex;gap:8px",k.append(h,u),g.append(f,m,b),t.recorded){const A=document.createElement("div");A.style.cssText="color:#7d8598;font-size:12px;margin-bottom:10px",A.textContent="This call will be recorded.",g.append(A)}g.append(k),document.body.appendChild(g);const R=pr();let z=!1,S=null;const M=()=>{z||(z=!0,S&&clearInterval(S),R(),g.style.opacity="0",g.style.transform="translateY(8px)",setTimeout(()=>g.remove(),250))},I=()=>{const A=hr(t.ringsUntil);if(b.textContent=A?`Ringing — ${A}s`:"The call has stopped ringing.",!A){M();try{r==null||r()}catch{}}};return I(),S=setInterval(I,1e3),h.addEventListener("click",()=>{M();try{o==null||o()}catch{}}),u.addEventListener("click",()=>{M();try{i==null||i()}catch{}}),requestAnimationFrame(()=>{g.style.opacity="1",g.style.transform="translateY(0)"}),M}const wt=120,gr=500,vt=30,br=1e3,_t={loanId:["loanId","loanNumber"],applicationId:["applicationId"],customerNumber:["customerNumber","customerId"]},mr=/[\u0000-\u001f\u007f]/;function yr(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>wt||mr.test(e)?null:e}function $e(t){return t===null?"null":Array.isArray(t)?"an array":typeof t=="object"?"an object":`a ${typeof t}`}function Vt(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=yr(t[r]);if(s)return s;const l=t[r];i(typeof l=="string"&&l.trim().length>wt?`${o}.${r} is longer than ${wt} characters and was dropped — pass the identifier itself, not a sentence or a URL.`:`${o}.${r} should be a string or number, got ${$e(l)} — it was dropped.`)}return null}function Wn(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,gr)};if(typeof t=="object")try{const e=JSON.stringify(t);if(e&&e.length<=br)return{ok:!0,value:JSON.parse(e)}}catch{}return{ok:!1}}const Fn=["email","name","phone"],wr=new Set(["id","userId",...Fn,...Object.values(_t).flat()]);function Yn(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 ${$e(t)} — ignored.`),null;const n={};try{const o=Vt(t,"id",["id","userId"],"identify()",e);o&&(n.id=o);for(const r of Fn){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 ${$e(s)} — it was dropped.`);continue}const l=s.trim().slice(0,r==="name"?200:wt);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(_t)){const l=Vt(t,r,s,"identify()",e);l&&(n[r]=l)}let i=0;for(const[r,s]of Object.entries(t)){if(wr.has(r)||s===void 0||typeof s=="function")continue;if(i>=vt){e(`identify() was given more than ${vt} extra fields; the rest were dropped.`);break}const l=Wn(s);if(!l.ok){e(`identify().${r} could not be sent (${$e(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 Xn(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 ${$e(e)} — ignored.`),o;try{const i=new Map;for(const[r,s]of Object.entries(_t))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 f=Vt({[r]:s},l,[r],"setContext()",n);f?o[l]=f:delete o[l];continue}if(typeof s=="function")continue;if(!(r in o)&&Object.keys(o).length>=vt){n(`setContext() holds at most ${vt} keys; "${r}" was dropped.`);continue}const g=Wn(s);if(!g.ok){n(`setContext().${r} could not be sent (${$e(s)} too large or not serialisable) — dropped.`);continue}o[r]=g.value}}catch{}return o}const Jn=["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","release","environment","buildId"],vr=["beforeSend","onUpdate","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError"];function xr(t){const e=t.toLowerCase().replace(/[-_]/g,"");return Jn.find(n=>n.toLowerCase()===e)||null}const Zn=/^(your[_-]|<|xxx|todo|changeme)/i,kr=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Sr(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 ${$e(i)}.`):Zn.test(i)?n.push(`apiKey is still the placeholder "${i}" — paste the key you saved when you created it.`):kr.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 ${$e(r)}.`):Zn.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(Jn.includes(l))continue;const g=xr(l);o.push(g?`unknown option "${l}" — did you mean "${g}"?`:`unknown option "${l}" is ignored.`)}for(const l of vr){const g=t[l];g!=null&&typeof g!="function"&&o.push(`${l} should be a function, got ${$e(g)} — it will not be called.`)}return{errors:n,warnings:o}}function $r(t){return typeof t=="string"?t.replace(/\/+$/,""):t}const We={name:"@sphoro/bugcatch-web-sdk",version:"0.16.0"},Fe={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 Gn{constructor(){Co(this,L);this.config={...Fe},this.started=!1,this.teardown=[],this.consoleBuffer=st(Fe.consoleLimit),this.networkBuffer=st(Fe.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}=Sr(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={...Fe,...e,apiBase:$r(e.apiBase??Fe.apiBase)},this.config.user=e.user!==void 0?Yn(e.user,r=>this.warn(r.replace("identify()","init({ user })"))):i||null,e.context&&this.setContext(e.context),this.consoleBuffer=st(this.config.consoleLimit),this.networkBuffer=st(this.config.networkLimit),this.config.captureConsole&&this.teardown.push(qo(this.consoleBuffer)),this.config.captureNetwork&&this.teardown.push(Do(this.networkBuffer)),this.config.captureErrors&&this.teardown.push(No(this.consoleBuffer)),da(this.config.projectId),this.config.widget&&this.teardown.push(Pa(this)),this.config.updates&&he()&&(this.startWatching(),queueMicrotask(()=>N(this,L,Me).call(this))),this.started=!0,N(this,L,To).call(this),this.debugLog("initialised",{version:We.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=Sa({apiBase:this.config.apiBase,onUpdate:n=>{if(n!=null&&n.incomingCall){N(this,L,Lo).call(this,n);return}if(n!=null&&n.callWithdrawn){N(this,L,It).call(this,n.callWithdrawn.callId);return}const o=pa(n);typeof this.config.onUpdate=="function"&&this.config.onUpdate(o),this.emit("update",o),N(this,L,Me).call(this)}}),this.teardown.push(()=>{var n;return(n=this.stopWatching)==null?void 0:n.call(this)}),this.teardown.push(()=>N(this,L,It).call(this));const e=n=>{(n.key===gt||n.key===null)&&N(this,L,Me).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 Pe()}markUpdateRead(e){return ha(e),N(this,L,Me).call(this),this}markReportRead(e){return fa(e),N(this,L,Me).call(this),this}markUpdatesRead(){return wn(),N(this,L,Me).call(this),this}async verifyFix(e,{result:n,resolvedAt:o,note:i}){const r=n==="STILL_HAPPENING"?await N(this,L,Et).call(this,{}):{};return Ra({apiBase:this.config.apiBase,bugId:e,result:n,resolvedAt:o,note:i,metadata:r.metadata,logs:r.logs})}async myReport(e){const n=await ma({apiBase:this.config.apiBase,bugId:e});return N(this,L,Eo).call(this,e,n==null?void 0:n.assistant),n}replyToReport(e,{body:n,attachments:o}={}){return vn({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 ya({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.kinds)}canCall(e){return this.config.voice?xn({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 N(this,L,Mo).call(this,e)}finally{this.callStarting=!1}}startLiveCall(e){return this.startCall(e)}canCallTeam(e){return this.config.voice?xn({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 ba({apiBase:this.config.apiBase})}hasReports(){return!!he()}forgetReports(){var e;return(e=this.stopWatching)==null||e.call(this),this.stopWatching=null,this.endCall(),Ae(),N(this,L,Me).call(this),this}identify(e){const n=Yn(e,o=>this.warn(o));return e!=null&&!n?this:(this.config.user=n,this.debugLog("identify",n),this)}setContext(e){return this.context=Xn(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 Jo({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}={}){var R,z,S,M,I,A,H,U;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 g=(r||[]).filter(T=>(T==null?void 0:T.storageKey)&<(T.mime,ct)).map(T=>({type:T.type==="VIDEO"?"VIDEO":"SCREENSHOT",storageKey:T.storageKey,mime:lt(T.mime,ct)})),f=(Array.isArray(s)?s:[]).filter(T=>T&&typeof T.key=="string"&&T.key&&(typeof T.value=="string"||typeof T.value=="number")).map(T=>({key:T.key.slice(0,64),value:String(T.value).trim().slice(0,120)})).filter(T=>T.value);Array.isArray(s)&&f.length<s.length&&this.warn("report({ references }) takes [{ key, value }] with string values; malformed entries were dropped.");let m=await N(this,L,Et).call(this,{title:e.trim(),description:typeof n=="string"?n:String(n??""),priority:String(o).toUpperCase(),metadata:i,media:g,references:f,context:l});const b=N(this,L,sn).call(this,m);if(b===!1)return{cancelled:!0};m=b,this.debugLog("sending report",{title:m.title,user:(R=m.metadata)==null?void 0:R.user,context:(z=m.metadata)==null?void 0:z.context,references:m.references,console:(I=(M=(S=m.logs)==null?void 0:S.console)==null?void 0:M.entries)==null?void 0:I.length,network:(U=(H=(A=m.logs)==null?void 0:A.network)==null?void 0:H.entries)==null?void 0:U.length});const h=Se(this.config.projectId);let u;try{u=await fetch(`${this.config.apiBase}/ingest/bugs`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey,...h!=null&&h.token?{"X-BugCatch-Session":h.token}:{}},body:JSON.stringify(m)})}catch(T){const Q=new Error(`[BugCatch] could not reach ${this.config.apiBase} — check apiBase, and that this site's origin is allowed by the API.`);throw Q.cause=T,N(this,L,Tt).call(this,"report",Q),Q}if(u.status===401&&h&&jt(this.config.projectId),!u.ok){let T="";try{const $=await u.json();T=Array.isArray($.message)?$.message.join(", "):$.message||""}catch{}u.status===401&&!h&&(T||(T="the apiKey was not accepted")),u.status===403&&(T||(T="the apiKey is not allowed to report to this projectId"));const Q=new Error(`[BugCatch] report failed (HTTP ${u.status})${T?": "+T:""}`);throw Q.status=u.status,N(this,L,Tt).call(this,"report",Q),Q}const k=await u.json();return N(this,L,rn).call(this,k,{title:m.title??e.trim(),via:"form"}),k.watchToken&&(Ot(k.watchToken),this.config.updates&&this.startWatching()),k}loginState(){var e;return ra({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:(e=Se(this.config.projectId))==null?void 0:e.token})}referenceFields(){return this.refFields?this.refFields:(this.refFields=qa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.refFields)}signedInAs(){const e=Se(this.config.projectId);return e?{email:e.email,name:e.name}:null}signIn(e,{name:n}={}){var o;return ia({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 sa({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=Se(this.config.projectId),n=await ca({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 La({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,sessionToken:(e=Se(this.config.projectId))==null?void 0:e.token})}matchKnownIssues(e,{signal:n}={}){var o;return Aa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,q:e||"",sessionToken:(o=Se(this.config.projectId))==null?void 0:o.token,signal:n})}async meToo(e,{email:n,name:o}={}){var s,l;const i=Se(this.config.projectId),r=await hn();return Ba({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:We},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:fn()}}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,Ct=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)}},To=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:We.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))},Me=function(){const e=Pe();if(this.emit("unread",{count:e.length,updates:e}),!!this.config.updateToast){if(!this.notifier){if(!e.length)return;this.notifier=Ma({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)}},Eo=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),N(this,L,Ct).call(this,"onAssistantPhase",i),this.emit("assistant",i)},rn=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),N(this,L,Ct).call(this,"onReportSubmitted",i),this.emit("report",i)},Tt=function(e,n){N(this,L,Ct).call(this,"onError",{action:e,error:n}),this.emit("error",{action:e,error:n})},Et=async function({title:e,description:n="",priority:o="MEDIUM",metadata:i={},media:r=[],references:s=[],context:l=null}){var f,m;const g=l?Xn(this.context,l,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:(f=this.config.user)==null?void 0:f.email,reporterName:(m=this.config.user)==null?void 0:m.name,metadata:{...await hn(),release:this.config.release,environment:this.config.environment,buildId:this.config.buildId,...i,user:this.config.user||void 0,...Object.keys(g).length?{context:g}:{},sdk:We},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},...r.length?{attachments:r}:{},...s.length?{references:s}:{}}},sn=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},Io=async function(){const e=Se(this.config.projectId),n=N(this,L,sn).call(this,await N(this,L,Et).call(this,{}));if(n===!1)return this.debugLog("call cancelled by beforeSend"),{cancelled:!0};let o;try{o=await wa({apiBase:this.config.apiBase,apiKey:this.config.apiKey,token:e==null?void 0:e.token,payload:n})}catch(i){throw i.status===401&&e&&jt(this.config.projectId),N(this,L,Tt).call(this,"call",i),i}return N(this,L,rn).call(this,o,{title:null,via:"call"}),o.watchToken&&(Ot(o.watchToken),this.config.updates&&this.startWatching()),o},Mo=async function(e){if(!Pt())throw new Error("[BugCatch] this browser cannot make live calls.");const n=await Kt();let o,i=null;try{if(e)o=await va({apiBase:this.config.apiBase,bugId:e});else if(i=await N(this,L,Io).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 Un({realtimeUrl:o.realtime_url,stream:n,ringsUntil:o.rings_until,onState:(s,l)=>{(s==="ended"||s==="failed"||s==="missed")&&(this.call=null),s==="missed"&&xa({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})},Lo=function(e){var i;const n=e.incomingCall;if(!this.config.voice||!Pt()||((i=this.ringing)==null?void 0:i.callId)===n.callId||this.call||this.callStarting)return;N(this,L,It).call(this);const o=fr({call:n,accent:this.config.accent,position:this.config.widgetPosition,onAnswer:()=>{N(this,L,Ao).call(this,e.bugId,n)},onDecline:()=>{this.ringing=null,kn({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"})},It=function(e){if(this.ringing&&!(e&&this.ringing.callId!==e)){try{this.ringing.dismiss()}catch{}this.ringing=null}},Ao=async function(e,n){if(this.ringing=null,this.call||this.callStarting)return;this.callStarting=!0;let o;try{o=await Kt()}catch(r){this.callStarting=!1,kn({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 ka({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 Un({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 Wt=new Gn;return Wt.Client=Gn,Wt.version=We.version,Wt});
|
|
1038
1392
|
//# sourceMappingURL=bugcatch.umd.js.map
|