@espejo/browser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,481 @@
1
+ import{E as oo,F as mt,G as le,H as ce,I as ue,a as Te,b as T,c as K,d as je,e as it,f as z,g as st,h as at,i as dt,j as lt,k as ct,l as ut,m as Ae,n as pt,o as ft,p as gt,q as ht}from"./chunk-MJUVVQNB.js";var P=class{constructor(t){this.options=t;this.slots=[];this.total=0;this.dropped=0}push(t){let o;try{o=JSON.stringify(t)?.length??0}catch{this.dropped++;return}if(o>this.options.maxBytes){this.dropped++;return}this.slots.push({item:t,bytes:o}),this.total+=o,this.evict()}evict(){for(;this.total>this.options.maxBytes||this.slots.length>this.options.maxItems;){let t=this.slots.shift();if(!t)break;this.total-=t.bytes,this.dropped++}}toArray(){return this.slots.map(t=>t.item)}get droppedCount(){return this.dropped}get byteLength(){return this.total}get length(){return this.slots.length}clear(){this.slots=[],this.total=0,this.dropped=0}};var vt={networkBytes:12e5,consoleBytes:4e5,interactionBytes:2e5,navigationBytes:5e4,maxItemsPerKind:5e3},q=class{constructor(t={}){let o={...vt,...t},n=o.maxItemsPerKind;this.network=new P({maxBytes:o.networkBytes,maxItems:n}),this.console=new P({maxBytes:o.consoleBytes,maxItems:n}),this.interactions=new P({maxBytes:o.interactionBytes,maxItems:n}),this.navigations=new P({maxBytes:o.navigationBytes,maxItems:n}),this.startedAtIso=new Date().toISOString(),this.clockStart=bt()}elapsed(){return Math.round(bt()-this.clockStart)}get dropped(){return{network:this.network.droppedCount,console:this.console.droppedCount,interactions:this.interactions.droppedCount,navigations:this.navigations.droppedCount}}get byteLength(){return this.network.byteLength+this.console.byteLength+this.interactions.byteLength+this.navigations.byteLength}snapshot(){return at({startedAt:this.startedAtIso,durationMs:this.elapsed(),screen:no(),network:this.network.toArray(),consoleEntries:this.console.toArray(),interactions:this.interactions.toArray(),navigations:this.navigations.toArray()})}clear(){this.network.clear(),this.console.clear(),this.interactions.clear(),this.navigations.clear()}};function bt(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function no(){return typeof window>"u"?{width:0,height:0}:{width:window.innerWidth||0,height:window.innerHeight||0}}var ro=512*1024,yt=/^(application\/(json|xml|javascript|x-www-form-urlencoded|.*\+json)|text\/)/i;function kt(e,t={}){let o=(t.ignoreOrigins??[]).filter(Boolean),n=t.maxBodyReadBytes??ro,s=[],d=r=>r?o.some(l=>r.startsWith(l)):!0,i=r=>{try{e.network.push(r)}catch{}try{t.onEntry?.(r)}catch{}};if(typeof globalThis.fetch=="function"){let r=globalThis.fetch,l=function(...h){let m="",c="GET",p=null,w=e.elapsed();try{let[f,b]=h;m=typeof f=="string"?f:f instanceof URL?f.href:f.url,c=(b?.method||(typeof f=="object"&&"method"in f?f.method:"GET")||"GET").toUpperCase(),typeof b?.body=="string"&&(p=b.body)}catch{}let k=r.apply(this,h);return d(m)?k:k.then(f=>{try{let b=pe(w,e.elapsed(),c,m,p);b.status=f.status,b.response_headers=Te(so(f.headers)),i(b),io(f,b,n)}catch{}return f},f=>{try{let b=pe(w,e.elapsed(),c,m,p);b.error=String(f?.message||f||"error"),i(b)}catch{}throw f})};globalThis.fetch=l,s.push(()=>{globalThis.fetch=r})}if(typeof XMLHttpRequest=="function"){let r=XMLHttpRequest.prototype,l=r.open,h=r.send;r.open=function(m,c,...p){try{this.__espejo={method:String(m||"GET").toUpperCase(),url:typeof c=="string"?c:c.href,t0:e.elapsed(),body:null}}catch{}return l.apply(this,[m,c,...p])},r.send=function(m){let c=this.__espejo;return c&&(typeof m=="string"&&(c.body=m),c.t0=e.elapsed(),d(c.url)||this.addEventListener("loadend",()=>{try{let p=pe(c.t0,e.elapsed(),c.method,c.url,c.body,"xhr");this.status===0?p.error="network error o abortado":p.status=this.status,p.response_headers=Te(lo(ao(this)));let w=co(this);w!=null&&xt(p,w),i(p)}catch{}})),h.apply(this,[m])},s.push(()=>{r.open=l,r.send=h})}if(typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){let r=navigator.sendBeacon.bind(navigator);navigator.sendBeacon=function(l,h){let m=typeof l=="string"?l:l.href,c=r(l,h);try{if(!d(m)){let p=pe(e.elapsed(),e.elapsed(),"POST",m,typeof h=="string"?h:null,"beacon");c||(p.error="sendBeacon devolvi\xF3 false (encolado rechazado)"),i(p)}}catch{}return c},s.push(()=>{navigator.sendBeacon=r})}return()=>{for(let r of s.splice(0))try{r()}catch{}}}function pe(e,t,o,n,s,d="fetch"){let i={requestId:null,t_ms:e,method:o,url:T(n),status:null,duration_ms:Math.max(0,t-e),type:d,tabId:null,error:null,response_body:null,request_headers:null,response_headers:null};if(s!=null){let{text:r,truncated:l}=z(K(s),je);i.request_body=r,l&&(i.request_body_truncated=!0)}return i}function xt(e,t){let{text:o,truncated:n}=z(K(t),je);e.response_body=o,n&&(e.response_body_truncated=!0)}function io(e,t,o){try{if(!e.body)return;let n=e.headers.get("content-type")||"";if(!yt.test(n)||Number(e.headers.get("content-length")||"0")>o)return;e.clone().text().then(i=>{i.length>o||xt(t,i)}).catch(()=>{})}catch{}}function so(e){let t={};try{e.forEach((o,n)=>{t[n]=o})}catch{}return t}function ao(e){try{return e.getAllResponseHeaders()||""}catch{return""}}function lo(e){let t={};for(let o of e.split(/\r?\n/)){let n=o.indexOf(":");n<=0||(t[o.slice(0,n).trim()]=o.slice(n+1).trim())}return t}function co(e){try{if(e.responseType&&e.responseType!=="text"&&e.responseType!=="json")return null;let t=e.getResponseHeader("content-type")||"";return t&&!yt.test(t)?null:typeof e.responseText=="string"?e.responseText:null}catch{return null}}var uo=["error","warn"];function wt(e,t={}){let o=[],n=(s,d)=>{try{e.console.push(s)}catch{}try{t.onEntry?.(s,d)}catch{}};if(typeof console<"u")for(let s of uo){let d=console[s]?.bind(console);d&&(console[s]=(...i)=>{try{n({t_ms:e.elapsed(),level:s,...Ce(i.map(fo).join(" ")),url:Le(),stack:po(i)},"console")}catch{}d(...i)},o.push(()=>{console[s]=d}))}if(typeof window<"u"){let s=i=>{n({t_ms:e.elapsed(),level:"error",...Ce(i.message||"window.onerror"),url:i.filename||Le(),line:i.lineno??null,stack:i.error?.stack??null},"window_error")},d=i=>{let r=i.reason;n({t_ms:e.elapsed(),level:"error",...Ce(`unhandledrejection: ${r?.message||String(r)}`),url:Le(),stack:r?.stack??null},"unhandled_rejection")};window.addEventListener("error",s),window.addEventListener("unhandledrejection",d),o.push(()=>{window.removeEventListener("error",s),window.removeEventListener("unhandledrejection",d)})}return()=>{for(let s of o.splice(0))try{s()}catch{}}}function po(e){for(let t of e)if(t instanceof Error&&typeof t.stack=="string")return t.stack;return null}function fo(e){if(typeof e=="string")return e;if(e instanceof Error)return`${e.name}: ${e.message}`;try{return JSON.stringify(e)??String(e)}catch{return String(e)}}function Ce(e){let t=e.trimStart(),n=t.startsWith("{")||t.startsWith("[")?String(K(e)):e.replace(/https?:\/\/\S+/g,i=>T(i)),{text:s,truncated:d}=z(n,it);return d?{text:s,truncated:!0}:{text:s}}function Le(){try{return typeof location<"u"?T(location.href):null}catch{return null}}function St(e,t){if(typeof window>"u"||typeof history>"u")return()=>{};let o="",n=()=>{try{let i=T(location.href);if(i===o)return;if(o=i,e.navigations.push({t_ms:e.elapsed(),url:i}),t)try{t(i)}catch{}}catch{}};n();let s=history.pushState,d=history.replaceState;return history.pushState=function(...i){let r=s.apply(this,i);return n(),r},history.replaceState=function(...i){let r=d.apply(this,i);return n(),r},window.addEventListener("popstate",n),window.addEventListener("hashchange",n),()=>{history.pushState=s,history.replaceState=d,window.removeEventListener("popstate",n),window.removeEventListener("hashchange",n)}}var J=class{constructor(t){this.options=t;this.segments=[];this.total=0;this.droppedSegments=0;this.overflowed=!1}startSegment(){this.segments.push({items:[],bytes:0}),this.overflowed=!1,this.evict()}push(t){if(this.segments.length===0||this.overflowed)return;let o;try{o=JSON.stringify(t)?.length??0}catch{return}let n=this.segments[this.segments.length-1];n.items.push(t),n.bytes+=o,this.total+=o,this.evict()}evict(){for(;this.total>this.options.maxBytes&&this.segments.length>1;){let t=this.segments.shift();this.total-=t.bytes,this.droppedSegments++}if(this.total>this.options.maxBytes&&this.segments.length===1){let t=this.segments.pop();this.total-=t.bytes,this.droppedSegments++,this.overflowed=!0}}toArray(){let t=[];for(let o of this.segments)t.push(...o.items);return t}get byteLength(){return this.total}get segmentCount(){return this.segments.length}get droppedCount(){return this.droppedSegments}get isOverflowed(){return this.overflowed}clear(){this.segments=[],this.total=0,this.droppedSegments=0,this.overflowed=!1}};var go=3e4,fe="[data-espejo-block]",ge="[data-espejo-mask]";function Et(e,t,o={}){let n=new J({maxBytes:o.maxBytes??3e6}),s=t({emit(d,i){try{(i||n.segmentCount===0)&&n.startSegment(),n.push(d)}catch{}},checkoutEveryNms:go,maskAllInputs:!0,blockSelector:fe,maskTextSelector:ge,recordCanvas:!1,collectFonts:!1});return{stop(){try{s?.()}catch{}},events:()=>n.toArray(),buffer:n}}async function Rt(){try{return(await import("rrweb")).record??null}catch{return null}}async function Tt(){try{return(await import("rrweb-snapshot")).snapshot??null}catch{return null}}var ho=800;function jt(e){let t=new Set,o=null,n=()=>{try{let d=e.getSnapshot();if(!d||typeof document>"u"||typeof location>"u")return;let i=T(location.href);if(t.has(i))return;let r=d(document,{maskAllInputs:!0,blockSelector:fe,maskTextSelector:ge,inlineStylesheet:!0,recordCanvas:!1});if(!r)return;t.add(i),mo(e,i,r).catch(()=>{t.delete(i)})}catch{}};return{onRoute:()=>{typeof setTimeout>"u"||(o&&clearTimeout(o),o=setTimeout(n,ho))},stop(){o&&(clearTimeout(o),o=null)}}}async function mo(e,t,o){let n=`${bo(e.ingestUrl)}/v1/ingest/route-snapshot`,s=await e.fetchImpl(n,{method:"POST",headers:{"x-espejo-key":e.key,"content-type":"application/json"},body:JSON.stringify({key:e.key,url:t,width:typeof window<"u"?window.innerWidth:0,height:typeof window<"u"?window.innerHeight:0,snapshot:o}),credentials:"omit",keepalive:!1});if(!s.ok)throw new Error(`route-snapshot rejected: HTTP ${s.status}`)}function bo(e){return e.endsWith("/")?e.slice(0,-1):e}var vo=["video/webm;codecs=vp9,opus","video/webm;codecs=vp8,opus","video/webm"],_e=240;async function yo(){try{return(await navigator.permissions?.query({name:"microphone"}))?.state==="denied"}catch{return!1}}function Y(){return typeof navigator<"u"&&!!navigator.mediaDevices?.getDisplayMedia&&typeof MediaRecorder<"u"}function ko(){return typeof MediaRecorder>"u"?null:vo.find(e=>MediaRecorder.isTypeSupported(e))??null}async function At(e={}){if(!Y())throw new Error("This browser can't record the screen.");let t=ko();if(!t)throw new Error("This browser doesn't support MediaRecorder for webm.");let o=[],n=()=>{for(let E of o)for(let _ of E.getTracks())_.stop();o.length=0},s=e.maxHeight??1080,d=e.frameRate??24,i=e.surface,r=await navigator.mediaDevices.getDisplayMedia({video:{height:{max:s},frameRate:{ideal:d,max:d},...i?{displaySurface:i}:{}},audio:!0,...i==="current-tab"?{preferCurrentTab:!0}:{}});o.push(r),await r.getVideoTracks()[0]?.applyConstraints({height:{max:s},frameRate:{max:d}}).catch(()=>{});let l=r.getAudioTracks()[0],h=new AudioContext;h.resume().catch(()=>{});let m=h.createMediaStreamDestination(),c=h.createConstantSource();c.offset.value=0,c.connect(m),c.start(),l&&h.createMediaStreamSource(new MediaStream([l])).connect(m);let p=null,w=!1,k=!1,f=async E=>{if(E===w)return{on:w,denied:!1};if(!E)return p&&(p.gain.value=0),w=!1,{on:!1,denied:!1};if(!p)try{let _=await navigator.mediaDevices.getUserMedia({audio:!0});o.push(_),p=h.createGain(),p.gain.value=0,h.createMediaStreamSource(_).connect(p),p.connect(m)}catch{return{on:!1,denied:await yo()}}return p.gain.value=1,w=!0,k=!0,{on:!0,denied:!1}},b=m.stream.getAudioTracks()[0],j=[...r.getVideoTracks()];b&&j.push(b),e.microphone&&await f(!0);let x=()=>typeof performance<"u"?performance.now():Date.now(),F=0,A=0,C=[],y=new MediaRecorder(new MediaStream(j),{mimeType:t,videoBitsPerSecond:e.videoBitsPerSecond??6e5,audioBitsPerSecond:64e3});y.ondataavailable=E=>{E.data.size>0&&C.push(E.data)};let g=t.split(";")[0],M=new Promise(E=>{y.onstop=()=>{A||(A=x()),n(),h.close().catch(()=>{}),E(new Blob(C,{type:g}))}});r.getVideoTracks()[0]?.addEventListener("ended",()=>{y.state!=="inactive"&&y.stop(),e.onUserStopped?.()});let L=Math.min(e.maxSeconds??240,240),Q=setTimeout(()=>{y.state!=="inactive"&&y.stop(),e.onLimitReached?.()},L*1e3);return y.start(1e3),F=x(),{mimeType:t,contentType:g,limitSeconds:L,get durationMs(){return F?(A||x())-F:0},setMicrophone:f,get microphoneOn(){return w},get hasAudio(){return!!l||k},async stop(){clearTimeout(Q),y.state!=="inactive"&&y.stop();let E=await M;return E.size>0?E:null}}}var xo=2e3;async function Be(e,t,o){let n={config:null,videoSurface:null,hasTriggers:!1};try{if(typeof o!="function")return n;let s=`${So(e)}/v1/ingest/config?key=${encodeURIComponent(t)}`,d=await o(s,{method:"GET",credentials:"omit",signal:wo(xo)});if(!d.ok)return n;let i=await d.json(),r=i?.data??i,l=r?.video_surface;return{config:Lt(r),videoSurface:typeof l=="string"?l:null,hasTriggers:r?.has_triggers===!0}}catch{return n}}function Lt(e){if(!e||typeof e!="object")return null;let t=e;if(t.enabled!==!0)return null;let o=Array.isArray(t.triggers)?t.triggers.filter(dt):[];if(!o.length)return null;let n=Ct(t.max_per_page,1,lt),s=Ct(t.cooldown_ms,0,ct);return n===null||s===null?null:{enabled:!0,triggers:o,max_per_page:n,cooldown_ms:s}}function Ct(e,t,o){if(typeof e!="number"||!Number.isFinite(e))return null;let n=Math.trunc(e);return n<t?null:Math.min(n,o)}function wo(e){try{return typeof AbortSignal<"u"&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e):void 0}catch{return}}function So(e){return e.endsWith("/")?e.slice(0,-1):e}var Eo=2e3;async function Me(e,t,o){try{if(typeof o!="function")return null;let n=`${To(e)}/v1/ingest/feedback-config?key=${encodeURIComponent(t)}`,s=await o(n,{method:"GET",credentials:"omit",signal:Ro(Eo)});if(!s.ok)return null;let d=await s.json(),i=d?.data??d;return _t(i)}catch{return null}}function _t(e){if(!e||typeof e!="object")return null;let t=e;return t.enabled!==!0||!ft(t.kind)?null:{enabled:!0,kind:t.kind,cadence_days:ht(t.cadence_days)}}function Ro(e){try{return typeof AbortSignal<"u"&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e):void 0}catch{return}}function To(e){return e.endsWith("/")?e.slice(0,-1):e}var he=new Map;function me(e,t,o,n=document){let s=globalThis[o];if(s)return Promise.resolve(s);let d=he.get(o);if(d)return d;let i=new Promise(r=>{try{let l=n.createElement("script");l.src=`${e.replace(/\/+$/,"")}/sdk/${t}`,l.async=!0,l.crossOrigin="anonymous",l.addEventListener("load",()=>{r(globalThis[o]??null)}),l.addEventListener("error",()=>{he.delete(o),r(null)}),n.head.appendChild(l)}catch{he.delete(o),r(null)}});return he.set(o,i),i}var Bt="__espejoInstallTriggers";async function Mt(e,t=fetch){let o=JSON.stringify({manifest:e.manifest,events:e.events,dom:e.dom??null}),n=e.video?Ao(o,e.video):null,s=n??o,d={"x-espejo-key":e.key};e.video||(d["content-type"]="application/json");let i=`${Dt(e.ingestUrl)}/v1/ingest/sessions`;if(n&&e.onProgress)return jo(i,d,n,e.onProgress);let r=await t(i,{method:"POST",headers:d,body:s,credentials:"omit",keepalive:!1});if(!r.ok){let h=await r.text().catch(()=>"");throw new Error(`ingest rejected the session: HTTP ${r.status} ${h.slice(0,200)}`)}let l=await r.json();return l.data??l}function jo(e,t,o,n){return new Promise((s,d)=>{let i=new XMLHttpRequest;i.open("POST",e,!0),i.withCredentials=!1;for(let[r,l]of Object.entries(t))i.setRequestHeader(r,l);i.upload.onprogress=r=>{if(!(!r.lengthComputable||r.total<=0))try{n(Math.min(1,r.loaded/r.total))}catch{}},i.onload=()=>{if(i.status<200||i.status>=300){d(new Error(`ingest rejected the session: HTTP ${i.status} ${String(i.responseText).slice(0,200)}`));return}try{let r=JSON.parse(i.responseText);s(r.data??r)}catch{d(new Error("ingest returned a body that is not JSON"))}},i.onerror=()=>d(new Error("ingest unreachable: the upload could not be completed")),i.onabort=()=>d(new Error("the upload was cancelled")),i.send(o)})}async function De(e,t=fetch){let o=`${Dt(e.ingestUrl)}/v1/ingest/feedback`,n=await t(o,{method:"POST",headers:{"x-espejo-key":e.key,"content-type":"application/json"},body:JSON.stringify({kind:e.kind,value:e.value,page_host:e.pageHost??null,user_ref:e.userRef??null}),credentials:"omit",keepalive:!1});if(!n.ok){let s=await n.text().catch(()=>"");throw new Error(`ingest rejected the vote: HTTP ${n.status} ${s.slice(0,200)}`)}}function Ao(e,t){let o=new FormData;return o.append("payload",e),o.append("video",new Blob([t.blob],{type:t.contentType}),"video.webm"),o}function Dt(e){return e.endsWith("/")?e.slice(0,-1):e}var Ht=ue+`
2
+ :host {
3
+ all: initial !important;
4
+ position: fixed !important;
5
+ z-index: 2147483647 !important;
6
+ display: block !important;
7
+ }
8
+
9
+ .espejo-root {
10
+ font-family:
11
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
12
+ font-size: 14px;
13
+ line-height: 1.4;
14
+ color: var(--e-fg);
15
+ position: relative;
16
+ display: block;
17
+ }
18
+
19
+ .espejo-trigger {
20
+ width: 40px;
21
+ height: 40px;
22
+ border-radius: 999px;
23
+ border: 1px solid var(--e-border);
24
+ background: var(--e-bg);
25
+ color: var(--e-fg);
26
+ box-shadow: var(--e-shadow);
27
+ cursor: pointer;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ font-size: 18px;
32
+ padding: 0;
33
+ touch-action: none;
34
+ }
35
+
36
+ /* Host arrastrado: se ancla por el eje hacia donde cay\xF3 (bordes --e-x/--e-r/--e-y/--e-b),
37
+ no siempre top-left, para que lo que se abra crezca HACIA la pantalla y no se vaya afuera. */
38
+ :host([data-dragged][data-ax="l"]){left:var(--e-x)!important;right:auto!important}
39
+ :host([data-dragged][data-ax="r"]){right:var(--e-r)!important;left:auto!important}
40
+ :host([data-dragged][data-ay="t"]){top:var(--e-y)!important;bottom:auto!important}
41
+ :host([data-dragged][data-ay="b"]){bottom:var(--e-b)!important;top:auto!important}
42
+
43
+ .espejo-trigger[hidden] {
44
+ display: none;
45
+ }
46
+
47
+ .espejo-trigger:hover {
48
+ filter: brightness(0.97);
49
+ }
50
+
51
+ .espejo-panel {
52
+ position: absolute;
53
+ width: 260px;
54
+ max-width: calc(100vw - 32px);
55
+ background: var(--e-bg);
56
+ color: var(--e-fg);
57
+ border: 1px solid var(--e-border);
58
+ border-radius: 10px;
59
+ box-shadow: var(--e-shadow);
60
+ padding: 12px;
61
+ }
62
+
63
+ .espejo-panel[hidden] {
64
+ display: none;
65
+ }
66
+
67
+ /* El panel se abre en la direcci\xF3n on-screen: se ancla al mismo eje que el host
68
+ (data-ax/data-ay, del rinc\xF3n o de la posici\xF3n real arrastrada), as\xED abre hacia adentro. */
69
+ :host([data-ay="b"]) .espejo-panel{bottom:calc(100% + 8px);top:auto}
70
+ :host([data-ay="t"]) .espejo-panel{top:calc(100% + 8px);bottom:auto}
71
+ :host([data-ax="r"]) .espejo-panel{right:0;left:auto}
72
+ :host([data-ax="l"]) .espejo-panel{left:0;right:auto}
73
+
74
+ .espejo-title {
75
+ margin: 0 0 8px;
76
+ font-size: 13px;
77
+ font-weight: 600;
78
+ }
79
+
80
+ .espejo-label {
81
+ display: block;
82
+ font-size: 12px;
83
+ color: var(--e-muted);
84
+ margin-bottom: 4px;
85
+ }
86
+
87
+ .espejo-textarea {
88
+ width: 100%;
89
+ min-height: 64px;
90
+ resize: vertical;
91
+ border: 1px solid var(--e-border);
92
+ border-radius: 6px;
93
+ padding: 6px 8px;
94
+ font: inherit;
95
+ color: inherit;
96
+ background: var(--e-bg);
97
+ }
98
+
99
+ .espejo-status {
100
+ min-height: 16px;
101
+ font-size: 12px;
102
+ margin-top: 6px;
103
+ }
104
+
105
+ .espejo-status[data-variant='error'] {
106
+ color: var(--e-danger);
107
+ }
108
+
109
+ .espejo-status[data-variant='success'] {
110
+ color: var(--e-accent);
111
+ }
112
+
113
+ .espejo-actions {
114
+ display: flex;
115
+ justify-content: flex-end;
116
+ gap: 8px;
117
+ margin-top: 10px;
118
+ }
119
+
120
+ .espejo-actions button {
121
+ font: inherit;
122
+ border-radius: 6px;
123
+ padding: 6px 10px;
124
+ cursor: pointer;
125
+ border: 1px solid var(--e-border);
126
+ background: var(--e-bg);
127
+ color: var(--e-fg);
128
+ }
129
+
130
+ .espejo-actions button[type='submit'] {
131
+ background: var(--e-accent);
132
+ border-color: var(--e-accent);
133
+ color: var(--e-accent-fg);
134
+ }
135
+
136
+ .espejo-actions button:disabled {
137
+ opacity: 0.6;
138
+ cursor: default;
139
+ }
140
+
141
+ .espejo-video {
142
+ margin-top: 8px;
143
+ }
144
+
145
+ .espejo-video [hidden] {
146
+ display: none;
147
+ }
148
+
149
+ .espejo-record {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 6px;
153
+ width: 100%;
154
+ font: inherit;
155
+ border-radius: 6px;
156
+ padding: 7px 10px;
157
+ cursor: pointer;
158
+ border: 1px solid var(--e-border);
159
+ background: var(--e-bg);
160
+ color: var(--e-fg);
161
+ }
162
+
163
+ .espejo-record:hover:not(:disabled) {
164
+ border-color: var(--e-accent);
165
+ }
166
+
167
+ .espejo-record:disabled {
168
+ opacity: 0.6;
169
+ cursor: default;
170
+ }
171
+
172
+ .espejo-record-dot {
173
+ width: 8px;
174
+ height: 8px;
175
+ border-radius: 999px;
176
+ background: var(--e-danger);
177
+ flex: none;
178
+ }
179
+
180
+ .espejo-record-hint {
181
+ margin: 4px 0 0;
182
+ font-size: 11px;
183
+ color: var(--e-muted);
184
+ }
185
+
186
+ .espejo-attached {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 6px;
190
+ margin: 0;
191
+ padding: 7px 10px;
192
+ border-radius: 6px;
193
+ border: 1px solid var(--e-accent);
194
+ font-size: 12px;
195
+ }
196
+
197
+ .espejo-attached-text {
198
+ flex: 1;
199
+ }
200
+
201
+ .espejo-attached button {
202
+ font: inherit;
203
+ font-size: 11px;
204
+ border: 0;
205
+ background: none;
206
+ padding: 2px 4px;
207
+ color: var(--e-muted);
208
+ text-decoration: underline;
209
+ cursor: pointer;
210
+ }
211
+
212
+ .espejo-attached button:disabled {
213
+ opacity: 0.6;
214
+ cursor: default;
215
+ }
216
+
217
+ .espejo-pill {
218
+ display: flex;
219
+ align-items: center;
220
+ gap: 8px;
221
+ padding: 8px 10px;
222
+ border-radius: 999px;
223
+ background: var(--e-bg);
224
+ color: var(--e-fg);
225
+ border: 1px solid var(--e-border);
226
+ box-shadow: var(--e-shadow);
227
+ font-size: 12px;
228
+ white-space: nowrap;
229
+ touch-action: none;
230
+ cursor: grab;
231
+ }
232
+
233
+ .espejo-pill[hidden] {
234
+ display: none;
235
+ }
236
+
237
+ .espejo-pill-dot {
238
+ width: 9px;
239
+ height: 9px;
240
+ border-radius: 999px;
241
+ background: var(--e-danger);
242
+ flex: none;
243
+ animation: espejo-blink 1.4s ease-in-out infinite;
244
+ }
245
+
246
+ @media (prefers-reduced-motion: reduce) {
247
+ .espejo-pill-dot {
248
+ animation: none;
249
+ }
250
+ }
251
+
252
+ @keyframes espejo-blink {
253
+ 0%, 100% { opacity: 1; }
254
+ 50% { opacity: 0.25; }
255
+ }
256
+
257
+ .espejo-pill-time {
258
+ color: var(--e-muted);
259
+ font-variant-numeric: tabular-nums;
260
+ }
261
+
262
+ .espejo-pill button {
263
+ font: inherit;
264
+ font-size: 12px;
265
+ border-radius: 999px;
266
+ padding: 4px 10px;
267
+ cursor: pointer;
268
+ touch-action: auto;
269
+ border: 1px solid var(--e-accent);
270
+ background: var(--e-accent);
271
+ color: var(--e-accent-fg);
272
+ }
273
+
274
+ .espejo-mic {
275
+ border: 1px solid var(--e-border) !important;
276
+ background: transparent !important;
277
+ color: var(--e-muted) !important;
278
+ padding: 4px 8px !important;
279
+ line-height: 1;
280
+ }
281
+
282
+ .espejo-mic:hover:not([data-blocked]) {
283
+ border-color: var(--e-accent) !important;
284
+ color: var(--e-fg) !important;
285
+ }
286
+
287
+ .espejo-mic[data-on] {
288
+ border-color: var(--e-accent) !important;
289
+ background: var(--e-accent) !important;
290
+ color: var(--e-accent-fg) !important;
291
+ }
292
+
293
+ .espejo-mic[data-blocked] {
294
+ cursor: not-allowed;
295
+ color: var(--e-muted) !important;
296
+ opacity: 0.7;
297
+ }
298
+
299
+ .espejo-markup[hidden] {
300
+ display: none !important;
301
+ }
302
+
303
+ .espejo-mic-slash {
304
+ display: none;
305
+ }
306
+
307
+ .espejo-mic[data-blocked] .espejo-mic-slash {
308
+ display: inline;
309
+ }
310
+
311
+ .espejo-mic svg {
312
+ display: block;
313
+ }
314
+ `;var Ot={triggerLabel:"Reportar un problema",title:"Reportar un problema",fieldLabel:"\xBFQu\xE9 pas\xF3?",placeholder:"Contanos qu\xE9 esperabas y qu\xE9 viste\u2026",cancel:"Cancelar",submit:"Enviar",sending:"Enviando\u2026",success:"Enviado. \xA1Gracias!",errorPrefix:"No se pudo enviar: ",record:"Grabar mi pantalla",recordHint:"Mostranos el error en vez de describirlo.",recording:"Grabando tu pantalla",stopRecording:"Frenar y adjuntar",micOn:"Micr\xF3fono prendido \xB7 clic para silenciar",micOff:"Micr\xF3fono apagado \xB7 clic para narrar",micBlocked:"Micr\xF3fono bloqueado \xB7 habilitalo en la configuraci\xF3n del navegador",micStateOn:"micr\xF3fono activo",micStateOff:"micr\xF3fono en silencio",micStateBlocked:"sin micr\xF3fono",countdown:"Grabando en",markupOn:"Marcado prendido \xB7 tu app no responde al clic mientras dibuj\xE1s",markupOff:"Dibuj\xE1 sobre la pantalla para se\xF1alar algo",videoAttached:"Grabaci\xF3n de pantalla adjuntada",videoRemove:"Sacar",limitReached:"Grabaci\xF3n detenida: se lleg\xF3 al l\xEDmite de tiempo.",recordErrorPrefix:"No se pudo empezar a grabar: "},Co='button, textarea, [href], input, select, [tabindex]:not([tabindex="-1"])';function Pt(e){let t=e.doc??(typeof document<"u"?document:null);if(!t)return null;try{return _o(t,e.report,{...Ot,...e.text},e.video,e.theme??"auto",e.position??"bottom-right",e.icon)}catch{return null}}function Lo(e){let t=(e??"").trim();return t.startsWith("https://")||t.startsWith("data:image/")?`<img src="${t}" alt="" width="22" height="22" />`:`<span aria-hidden="true">${t.slice(0,8)||"\u{1F41E}"}</span>`}function He(e){let t=Math.floor(e/60),o=Math.floor(e%60);return`${t}:${String(o).padStart(2,"0")}`}function It(e){let t=e?.name;return t==="NotAllowedError"||t==="AbortError"}function _o(e,t,o,n,s,d,i){let{host:r,shadow:l}=le(e,"data-espejo-widget",d,s);r.setAttribute("data-ax",d.endsWith("left")?"l":"r"),r.setAttribute("data-ay",d.startsWith("top")?"t":"b");let m=!!n?.supported()?`<div class="espejo-video" id="espejo-video">
315
+ <button type="button" class="espejo-record" id="espejo-record">
316
+ <span class="espejo-record-dot" aria-hidden="true"></span>${o.record}
317
+ </button>
318
+ <p class="espejo-record-hint" id="espejo-record-hint">${o.recordHint}</p>
319
+ <p class="espejo-attached" id="espejo-attached" hidden>
320
+ <span aria-hidden="true">\u{1F3AC}</span>
321
+ <span class="espejo-attached-text">${o.videoAttached}</span>
322
+ <button type="button" id="espejo-remove">${o.videoRemove}</button>
323
+ </p>
324
+ </div>`:"";l.innerHTML=`<style>${Ht}</style>
325
+ <div class="espejo-root">
326
+ <button
327
+ type="button"
328
+ class="espejo-trigger"
329
+ id="espejo-trigger"
330
+ aria-haspopup="dialog"
331
+ aria-expanded="false"
332
+ aria-label="${o.triggerLabel}"
333
+ >${Lo(i)}</button>
334
+ <div class="espejo-panel" id="espejo-panel" role="dialog" aria-modal="true" aria-labelledby="espejo-title" hidden>
335
+ <form id="espejo-form">
336
+ <p class="espejo-title" id="espejo-title">${o.title}</p>
337
+ <label class="espejo-label" for="espejo-textarea">${o.fieldLabel}</label>
338
+ <textarea
339
+ class="espejo-textarea"
340
+ id="espejo-textarea"
341
+ placeholder="${o.placeholder}"
342
+ required
343
+ ></textarea>
344
+ ${m}
345
+ <div class="espejo-status" id="espejo-status" role="status" aria-live="polite"></div>
346
+ <div class="espejo-actions">
347
+ <button type="button" id="espejo-cancel">${o.cancel}</button>
348
+ <button type="submit" id="espejo-submit">${o.submit}</button>
349
+ </div>
350
+ </form>
351
+ </div>
352
+ <div class="espejo-pill" id="espejo-pill" role="status" aria-live="polite" hidden>
353
+ <span class="espejo-pill-dot" aria-hidden="true"></span>
354
+ <span class="espejo-pill-label" id="espejo-pill-label">${o.recording}</span>
355
+ <span class="espejo-pill-time" id="espejo-pill-time">0:00</span>
356
+ <button
357
+ type="button"
358
+ class="espejo-mic"
359
+ id="espejo-mic"
360
+ aria-pressed="false"
361
+ title="${o.micOff}"
362
+ aria-label="${o.micOff}"
363
+ ><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
364
+ <rect x="9" y="2" width="6" height="11" rx="3" />
365
+ <path d="M5 11a7 7 0 0 0 14 0" />
366
+ <path d="M12 18v3" />
367
+ <path class="espejo-mic-slash" d="M4 20L20 4" />
368
+ </svg></button>
369
+ <button
370
+ type="button"
371
+ class="espejo-mic espejo-markup"
372
+ id="espejo-markup"
373
+ aria-pressed="false"
374
+ title="${o.markupOff}"
375
+ aria-label="${o.markupOff}"
376
+ ><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
377
+ <path d="M12 19l7-7-4-4-7 7-1 5z" />
378
+ <path d="M15 8l1-1a2.8 2.8 0 1 1 4 4l-1 1" />
379
+ </svg></button>
380
+ <button type="button" id="espejo-stop">${o.stopRecording}</button>
381
+ </div>
382
+ </div>`;let c=l.getElementById("espejo-trigger"),p=l.getElementById("espejo-panel"),w=l.getElementById("espejo-form"),k=l.getElementById("espejo-textarea"),f=l.getElementById("espejo-cancel"),b=l.getElementById("espejo-submit"),j=l.getElementById("espejo-status"),x=l.getElementById("espejo-pill"),F=l.getElementById("espejo-pill-label"),A=l.getElementById("espejo-pill-time"),C=l.getElementById("espejo-stop"),y=l.getElementById("espejo-mic"),g=l.getElementById("espejo-markup"),M=l.querySelector(".espejo-root"),L=l.getElementById("espejo-record"),Q=l.getElementById("espejo-attached"),E=l.getElementById("espejo-record-hint"),_=l.getElementById("espejo-remove"),U=!1,Ue=!1,be=0,D=!1,H=!1,I=!1,B=!1,$=!1,W=null,Z=0,Gt=()=>Array.from(p.querySelectorAll(Co)).filter(a=>!a.hasAttribute("disabled")&&!a.closest("[hidden]")),O=(a,u)=>{j.textContent=a,u?j.setAttribute("data-variant",u):j.removeAttribute("data-variant")},ve=()=>{!L||!Q||!E||(L.hidden=D,E.hidden=D,Q.hidden=!D)},$e=()=>{if(D){D=!1;try{n?.discard()}catch{}ve()}},ye=(a=!0)=>{U||(U=!0,p.hidden=!1,c.setAttribute("aria-expanded","true"),a&&(O(""),k.value=""),ve(),k.focus(),e.addEventListener("keydown",ee,!0))},We=()=>{U&&(U=!1,p.hidden=!0,c.setAttribute("aria-expanded","false"),e.removeEventListener("keydown",ee,!0),c.focus())},X=()=>{$e(),We()},ee=a=>{if(!U)return;if(a.key==="Escape"){a.stopPropagation(),a.preventDefault(),X();return}if(a.key!=="Tab")return;let u=Gt();if(u.length===0)return;let v=u[0],S=u[u.length-1],V=l.activeElement;a.shiftKey&&V===v?(a.preventDefault(),S.focus()):!a.shiftKey&&V===S&&(a.preventDefault(),v.focus())},ke=()=>{y.setAttribute("aria-pressed",String(I)),y.toggleAttribute("data-on",I),y.toggleAttribute("data-blocked",$);let a=$?o.micBlocked:I?o.micOn:o.micOff;y.setAttribute("title",a),y.setAttribute("aria-label",a),Ne()},Kt=()=>$?o.micStateBlocked:I?o.micStateOn:o.micStateOff,Ne=()=>{let a=Kt();F.textContent=Z>0?`${o.countdown} ${Z} \xB7 ${a}`:`${o.recording} \xB7 ${a}`},zt=async()=>{if(!n)return;let a;try{a=await n.setMicrophone(!0)}catch{return}H&&(I=a.on,$=a.denied,ke())},qt=async()=>{if(!n||!H||$)return;let a=await n.setMicrophone(!I);I=a.on,$=a.denied,ke()},te=()=>{g.setAttribute("aria-pressed",String(B)),g.toggleAttribute("data-on",B);let a=B?o.markupOn:o.markupOff;g.setAttribute("title",a),g.setAttribute("aria-label",a)},Jt=async a=>{!n?.setMarkup||!H||(B=await n.setMarkup(a,()=>{B=!1,te()}),te())},Yt=()=>{H=!0,I=!1,$=!1,B=!1,g.hidden=!n?.setMarkup,ke(),te(),U=!1,p.hidden=!0,c.hidden=!0,c.setAttribute("aria-expanded","false"),e.removeEventListener("keydown",ee,!0),x.hidden=!1,R&&G(R.x,R.y),zt();let a=Date.now(),u=n?.limitSeconds??0,v=()=>{let S=(Date.now()-a)/1e3;Z=Math.max(0,5-Math.floor(S)),Ne(),A.textContent=u?`${He(S)} / ${He(u)}`:He(S)};v(),W=e.defaultView?.setInterval(v,1e3)??null,C.focus()},xe=async a=>{if(H){H=!1,Z=0,W!==null&&(e.defaultView?.clearInterval(W),W=null),x.hidden=!0,c.hidden=!1,R&&G(R.x,R.y),B&&(B=!1,await n?.setMarkup?.(!1,()=>{}),te());try{await n?.stop(),D=!0}catch{D=!1}ye(!1),a&&O(a)}},Ve=()=>{if(!n||H)return;O("");let a;try{a=n.start({onUserStopped:()=>{xe()},onLimitReached:()=>{xe(o.limitReached)}})}catch(u){It(u)||O(`${o.recordErrorPrefix}${String(u)}`,"error");return}a.then(()=>Yt(),u=>{if(It(u))return;let v=u instanceof Error?u.message:String(u);O(`${o.recordErrorPrefix}${v}`,"error")})},we=a=>{Ue=a,b.disabled=a,f.disabled=a,k.disabled=a,L&&(L.disabled=a),_&&(_.disabled=a),b.textContent=a?o.sending:o.submit},Qt=async a=>{if(a.preventDefault(),Ue)return;let u=k.value.trim();if(!u){k.focus();return}we(!0),be=0,O("");try{await t(u,v=>{let S=Math.round(v*100);S<=be||(be=S,b.textContent=`${o.sending} ${S}%`)}),D=!1,ve(),O(o.success,"success"),we(!1),e.defaultView?.setTimeout(()=>We(),1200)}catch(v){we(!1);let S=v instanceof Error?v.message:String(v);O(`${o.errorPrefix}${S}`,"error"),k.focus()}},Ge="espejo:widget:pos",N=e.defaultView,Zt=5,oe=!1,ne=!1,Se=!1,Ke=0,ze=0,qe=0,Je=0,R=null,re=null,Ye=(a,u)=>Math.max(0,Math.min(a,Math.max(0,u))),G=(a,u)=>{let v=N?.innerWidth??0,S=N?.innerHeight??0,V=r.offsetWidth,Re=r.offsetHeight,ae=Ye(a,v-V),de=Ye(u,S-Re);return ae+V/2>v/2?(r.setAttribute("data-ax","r"),r.style.setProperty("--e-r",`${v-ae-V}px`)):(r.setAttribute("data-ax","l"),r.style.setProperty("--e-x",`${ae}px`)),de+Re/2>S/2?(r.setAttribute("data-ay","b"),r.style.setProperty("--e-b",`${S-de-Re}px`)):(r.setAttribute("data-ay","t"),r.style.setProperty("--e-y",`${de}px`)),r.setAttribute("data-dragged",""),{x:ae,y:de}},Xt=a=>{try{N?.localStorage.setItem(Ge,JSON.stringify(a))}catch{}},eo=()=>{try{let a=N?.localStorage.getItem(Ge);if(!a)return;let u=JSON.parse(a);typeof u.x=="number"&&typeof u.y=="number"&&(R=G(u.x,u.y))}catch{}},Ee=a=>{if(a.button>0)return;oe=!0,ne=!1;let u=r.getBoundingClientRect();qe=u.left,Je=u.top,Ke=a.clientX,ze=a.clientY,re=a.currentTarget,re.setPointerCapture?.(a.pointerId)},Qe=a=>{a.target?.closest("button")||Ee(a)},ie=a=>{if(!oe)return;let u=a.clientX-Ke,v=a.clientY-ze;!ne&&Math.hypot(u,v)<Zt||(ne=!0,R=G(qe+u,Je+v))},se=a=>{oe&&(oe=!1,re?.releasePointerCapture?.(a.pointerId),re=null,ne&&R&&(Se=!0,Xt(R)))},Ze=()=>{R&&(R=G(R.x,R.y))},Xe=()=>{if(Se){Se=!1;return}U?X():ye()},et=a=>{Qt(a)},tt=()=>{xe()},ot=()=>{qt()},nt=()=>{Jt(!B)},rt=()=>{$e(),k.focus()},to=ce(M);return c.addEventListener("click",Xe),c.addEventListener("pointerdown",Ee),c.addEventListener("pointermove",ie),c.addEventListener("pointerup",se),x.addEventListener("pointerdown",Qe),x.addEventListener("pointermove",ie),x.addEventListener("pointerup",se),N?.addEventListener("resize",Ze),f.addEventListener("click",X),w.addEventListener("submit",et),C.addEventListener("click",tt),y.addEventListener("click",ot),g.addEventListener("click",nt),L?.addEventListener("click",Ve),_?.addEventListener("click",rt),e.body.appendChild(r),eo(),{open:()=>{H||ye()},destroy(){W!==null&&(e.defaultView?.clearInterval(W),W=null),e.removeEventListener("keydown",ee,!0),to(),c.removeEventListener("click",Xe),c.removeEventListener("pointerdown",Ee),c.removeEventListener("pointermove",ie),c.removeEventListener("pointerup",se),x.removeEventListener("pointerdown",Qe),x.removeEventListener("pointermove",ie),x.removeEventListener("pointerup",se),N?.removeEventListener("resize",Ze),f.removeEventListener("click",X),w.removeEventListener("submit",et),C.removeEventListener("click",tt),y.removeEventListener("click",ot),g.removeEventListener("click",nt),L?.removeEventListener("click",Ve),_?.removeEventListener("click",rt),r.remove()},shadowRootForTesting:l}}var Ft=ue+`
383
+ :host {
384
+ all: initial !important;
385
+ position: fixed !important;
386
+ z-index: 2147483647 !important;
387
+ display: block !important;
388
+ }
389
+
390
+ .ef-root {
391
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
392
+ font-size: 14px;
393
+ line-height: 1.4;
394
+ color: var(--e-fg);
395
+ background: var(--e-bg);
396
+ border: 1px solid var(--e-border);
397
+ border-radius: 10px;
398
+ box-shadow: var(--e-shadow);
399
+ padding: 12px 14px;
400
+ width: 248px;
401
+ max-width: calc(100vw - 32px);
402
+ }
403
+
404
+ .ef-title {
405
+ margin: 0 0 10px;
406
+ font-size: 13px;
407
+ font-weight: 600;
408
+ }
409
+
410
+ .ef-options {
411
+ display: flex;
412
+ gap: 6px;
413
+ flex-wrap: wrap;
414
+ }
415
+
416
+ .ef-opt {
417
+ flex: 1 1 auto;
418
+ min-width: 34px;
419
+ min-height: 34px;
420
+ display: flex;
421
+ align-items: center;
422
+ justify-content: center;
423
+ font: inherit;
424
+ font-size: 18px;
425
+ line-height: 1;
426
+ border: 1px solid var(--e-border);
427
+ border-radius: 8px;
428
+ background: var(--e-bg);
429
+ color: var(--e-fg);
430
+ cursor: pointer;
431
+ padding: 6px;
432
+ }
433
+
434
+ .ef-options[data-kind='scale7'] .ef-opt {
435
+ font-size: 14px;
436
+ font-weight: 600;
437
+ min-width: 26px;
438
+ padding: 6px 2px;
439
+ }
440
+
441
+ .ef-opt:hover:not(:disabled) {
442
+ border-color: var(--e-accent);
443
+ }
444
+
445
+ .ef-opt:disabled {
446
+ opacity: 0.6;
447
+ cursor: default;
448
+ }
449
+
450
+ .ef-close {
451
+ position: absolute;
452
+ top: 6px;
453
+ right: 8px;
454
+ border: 0;
455
+ background: none;
456
+ color: var(--e-muted);
457
+ font-size: 16px;
458
+ line-height: 1;
459
+ cursor: pointer;
460
+ padding: 2px 4px;
461
+ }
462
+
463
+ .ef-thanks {
464
+ margin: 0;
465
+ font-size: 13px;
466
+ font-weight: 600;
467
+ color: var(--e-accent);
468
+ }
469
+
470
+ .ef-thanks[hidden],
471
+ .ef-title[hidden],
472
+ .ef-options[hidden] {
473
+ display: none;
474
+ }
475
+ `;var Ut={title:"\xBFC\xF3mo estuvo tu experiencia?",thanks:"\xA1Gracias por tu feedback!",dismiss:"Cerrar",emojiSad:"Mala",emojiNeutral:"Normal",emojiHappy:"Buena",thumbsDown:"No me gust\xF3",thumbsUp:"Me gust\xF3",scaleLabel:"Puntaje"};function Ie(e){let t=e.doc??(typeof document<"u"?document:null);if(!t)return null;try{return Mo(t,e.config,e.vote,{...Ut,...e.text},e.theme??"auto")}catch{return null}}function Bo(e,t){if(e==="emoji3")return[{value:0,glyph:"\u{1F61E}",label:t.emojiSad},{value:1,glyph:"\u{1F610}",label:t.emojiNeutral},{value:2,glyph:"\u{1F60A}",label:t.emojiHappy}];if(e==="thumbs")return[{value:0,glyph:"\u{1F44E}",label:t.thumbsDown},{value:1,glyph:"\u{1F44D}",label:t.thumbsUp}];let{min:o,max:n}=gt.scale7,s=[];for(let d=o;d<=n;d++)s.push({value:d,glyph:String(d),label:`${t.scaleLabel} ${d}`});return s}function Mo(e,t,o,n,s){let{host:d,shadow:i}=le(e,"data-espejo-feedback","bottom-left",s),l=Bo(t.kind,n).map(g=>`<button type="button" class="ef-opt" data-value="${g.value}" title="${g.label}" aria-label="${g.label}"><span aria-hidden="true">${g.glyph}</span></button>`).join("");i.innerHTML=`<style>${Ft}</style>
476
+ <div class="ef-root" role="dialog" aria-label="${n.title}">
477
+ <button type="button" class="ef-close" id="ef-close" aria-label="${n.dismiss}">\xD7</button>
478
+ <p class="ef-title" id="ef-title">${n.title}</p>
479
+ <div class="ef-options" id="ef-options" role="group" aria-label="${n.title}" data-kind="${t.kind}">${l}</div>
480
+ <p class="ef-thanks" id="ef-thanks" role="status" aria-live="polite" hidden>${n.thanks}</p>
481
+ </div>`;let h=i.querySelector(".ef-root"),m=i.getElementById("ef-close"),c=i.getElementById("ef-title"),p=i.getElementById("ef-options"),w=i.getElementById("ef-thanks"),k=Array.from(p.querySelectorAll(".ef-opt")),f=!1,b=null,j=()=>{b!==null&&(e.defaultView?.clearTimeout(b),b=null)},x=()=>{j(),e.removeEventListener("keydown",A,!0),d.remove()},F=g=>{if(!f){f=!0;for(let M of k)M.disabled=!0;o(g).catch(()=>{}),c.hidden=!0,p.hidden=!0,w.hidden=!1,b=e.defaultView?.setTimeout(x,1600)??null}},A=g=>{g.key==="Escape"&&(g.stopPropagation(),x())},C=g=>{let M=g.currentTarget??null;M&&F(Number(M.dataset.value))};for(let g of k)g.addEventListener("click",C);m.addEventListener("click",x);let y=ce(h);return e.addEventListener("keydown",A,!0),e.body.appendChild(d),{destroy(){j();for(let g of k)g.removeEventListener("click",C);m.removeEventListener("click",x),y(),e.removeEventListener("keydown",A,!0),d.remove()},shadowRootForTesting:i}}var Do="espejo:feedback:lastShown:";function $t(e){return`${Do}${e}`}function Oe(e,t,o){try{let n=Wt(o);if(!n)return!0;let s=n.getItem($t(e));if(!s)return!0;let d=Number(s);return Number.isFinite(d)?Date.now()-d>=t*864e5:!0}catch{return!0}}function Pe(e,t){try{Wt(t)?.setItem($t(e),String(Date.now()))}catch{}}function Wt(e){return(e?.defaultView??(typeof window<"u"?window:void 0))?.localStorage??null}var Ho="__espejoInstallAnnotator";function Nt(e,t=document){return me(e,"espejo.draw.js",Ho,t)}var Io="@espejo/browser",Oo="0.1.0",Po="https://app.espejo.dev",Vt={source:"manual"},Fe=class{constructor(t){this.recorder=null;this.uninstall=[];this.dom=null;this.routeSnapshot=null;this.snapshotFn=null;this.widget=null;this.lastReport=null;this.feedback=null;this.annotator=null;this.markupHost=null;this.video=null;this.videoBlob=null;this.videoContentType="video/webm";this.videoHasAudio=!1;this.videoMs=0;this.autoConfig=null;this.autoGuard=null;this.autoKilled=!1;this.autoInFlight=!1;this.triggerSink=null;this.triggerBuf=[];this.triggersStop=null;if(!t?.key)throw new Error("Espejo: missing data-key (the project's public key)");this.config={...t,ingestUrl:t.ingestUrl||Po,mode:t.mode||"ring"},this.pristineFetch=typeof globalThis.fetch=="function"?globalThis.fetch.bind(globalThis):void 0}get recording(){return this.recorder!==null}start(){if(this.recorder)return;let t=new q(this.config.budgets);this.recorder=t,this.autoKilled=this.config.autoReport==="off",this.config.capture==="dom"&&(this.routeSnapshot=jt({ingestUrl:this.config.ingestUrl,key:this.config.key,getSnapshot:()=>this.snapshotFn,fetchImpl:this.pristineFetch})),this.uninstall=[kt(t,{ignoreOrigins:[this.config.ingestUrl],onEntry:o=>this.onNetworkEntry(o)}),wt(t,{onEntry:(o,n)=>this.onConsoleEntry(o,n)}),mt(t,{onEntry:(o,n)=>this.pushTriggerSignal({kind:o,tMs:n})}),St(t,o=>{this.routeSnapshot?.onRoute(),this.pushTriggerSignal({kind:"nav",tMs:t.elapsed(),url:o})})],this.config.capture==="dom"&&this.enableDom(t),this.config.button!=="off"&&(this.widget=Pt({report:(o,n)=>this.report(o,Vt,n),text:this.config.buttonText,theme:this.config.theme,position:this.config.position,icon:this.config.buttonIcon,video:{supported:()=>Y(),start:o=>this.startVideo(o),setMicrophone:o=>this.setMicrophone(o),stop:async()=>{await this.stopVideo()},discard:()=>this.discardVideo(),setMarkup:(o,n)=>this.setMarkup(o,n),limitSeconds:240}})),this.loadAutoReport(),this.config.feedback!=="off"&&this.loadFeedback()}pushTriggerSignal(t){try{this.triggerSink?this.triggerSink(t):this.triggerBuf.length<300&&this.triggerBuf.push(t)}catch{}}async loadTriggers(){if(!this.recorder||this.triggersStop)return;let t=await me(this.config.ingestUrl,"espejo.triggers.js",Bt);if(!(!t||!this.recorder||this.triggersStop))try{let o=t({ingestUrl:this.config.ingestUrl,key:this.config.key,fetchImpl:this.pristineFetch,now:()=>this.recorder?.elapsed()??0,subscribe:n=>{this.triggerSink=n;for(let s of this.triggerBuf.splice(0))n(s)},theme:this.config.theme});o&&(this.triggersStop=o.stop)}catch{}}async loadFeedback(){let t=await Me(this.config.ingestUrl,this.config.key,this.pristineFetch);t?.enabled&&(!this.recorder||this.feedback||Oe(this.config.key,t.cadence_days)&&(Pe(this.config.key),this.feedback=Ie({config:t,vote:o=>De({ingestUrl:this.config.ingestUrl,key:this.config.key,kind:t.kind,value:o,pageHost:typeof location<"u"?location.host:void 0,userRef:this.config.userRef},this.pristineFetch),text:this.config.feedbackText,theme:this.config.theme})))}async loadAutoReport(){let{config:t,videoSurface:o,hasTriggers:n}=await Be(this.config.ingestUrl,this.config.key,this.pristineFetch);o&&(this.config.videoSurface=o),n&&this.loadTriggers(),t&&(this.autoConfig=t,this.autoGuard=new ut(t))}setAutoReport(t){this.autoKilled=!t}identify(t){this.config.userRef=t||void 0}canOpenReport(){return!!this.widget&&!this.video}openReport(){let t=this.canOpenReport();return t&&this.widget.open(),t}get lastReportId(){return this.lastReport}get autoReportActive(){return!!this.autoConfig&&!this.autoKilled}autoEnabled(t){return this.autoKilled||!this.autoConfig||!this.recorder?!1:this.autoConfig.triggers.includes(t)}onConsoleEntry(t,o){if(o==="console"&&t.level!=="error")return;this.pushTriggerSignal({kind:"console_error",tMs:this.recorder?.elapsed()??0});let n=o==="window_error"?"auto:uncaught":o==="unhandled_rejection"?"auto:unhandled_rejection":"auto:console_error";this.maybeAutoReport(n,Ae(n,t.text,t.stack))}onNetworkEntry(t){let o=pt(t.status,!!t.error);o&&(this.pushTriggerSignal({kind:"http_error",tMs:this.recorder?.elapsed()??0}),this.maybeAutoReport(o,Ae(o,`${t.method} ${t.url}`,String(t.status??t.error))))}maybeAutoReport(t,o){try{if(!this.autoEnabled(t)||this.autoInFlight||!this.autoGuard?.check(o).allowed)return;this.autoInFlight=!0,this.report(void 0,{source:"auto",trigger:t}).catch(()=>{}).finally(()=>{this.autoInFlight=!1})}catch{this.autoInFlight=!1}}async enableDom(t){let o=this.config.rrwebRecord??await Rt();if(!o||this.recorder!==t)return;this.dom=Et(t,o);let n=this.config.rrwebSnapshot??await Tt();this.recorder===t&&(this.snapshotFn=n,n&&this.routeSnapshot?.onRoute())}stop(){for(let t of this.uninstall.splice(0))try{t()}catch{}this.dom?.stop(),this.dom=null,this.routeSnapshot?.stop(),this.routeSnapshot=null,this.snapshotFn=null,this.widget?.destroy(),this.widget=null,this.feedback?.destroy(),this.feedback=null;try{this.triggersStop?.()}catch{}this.triggersStop=null,this.triggerSink=null,this.triggerBuf=[],this.teardownMarkup(),this.recorder=null}async startVideo(t={}){if(!Y())throw new Error("This browser can't record the screen.");this.video||(this.video=await At({...t,surface:t.surface??this.config.videoSurface}),this.videoContentType=this.video.contentType)}async setMicrophone(t){return await this.video?.setMicrophone(t)??{on:!1,denied:!1}}get microphoneOn(){return this.video?.microphoneOn??!1}async stopVideo(){return this.video?(this.videoHasAudio=this.video.hasAudio,this.videoBlob=await this.video.stop(),this.videoMs=this.video.durationMs,this.video=null,this.videoBlob):null}get recordingVideo(){return this.video!==null}async setMarkup(t,o=()=>{}){if(!t)return this.teardownMarkup(),!1;if(this.annotator)return!0;if(typeof document>"u")return!1;let n=await Nt(this.config.ingestUrl);if(!n)return!1;let s=document.createElement("div");s.setAttribute("data-espejo-markup","");let d=s.attachShadow({mode:"closed"});return document.body.appendChild(s),this.markupHost=s,this.annotator=n({root:d,onExit:()=>{this.teardownMarkup(),o()}}),this.annotator?!0:(this.teardownMarkup(),!1)}teardownMarkup(){this.annotator?.destroy(),this.annotator=null,this.markupHost?.remove(),this.markupHost=null}discardVideo(){this.videoBlob=null,this.videoHasAudio=!1,this.videoMs=0}snapshot(){return this.recorder?.snapshot()??null}async report(t,o=Vt,n){let s=this.recorder;if(!s)return null;let d=o.source==="auto";this.video&&!d&&await this.stopVideo();let i=s.snapshot(),r=this.dom?.events(),l=!d&&this.videoBlob?{blob:this.videoBlob,contentType:this.videoContentType}:void 0,h=await Mt({ingestUrl:this.config.ingestUrl,key:this.config.key,manifest:this.manifest(s,t,{dom:!!r?.length,video:!!l,audio:!!l&&this.videoHasAudio},o),events:i,dom:r?.length?r:void 0,video:l,onProgress:d?void 0:n},this.pristineFetch);return s.clear(),this.dom?.buffer.clear(),d||(this.videoBlob=null,this.videoHasAudio=!1,this.videoMs=0,this.lastReport=h.session_id),h.url}manifest(t,o,n,s){return{bundle_version:st,source:"web-sdk",mode:n.video?"video":n.dom?"dom":"events",trigger:s.source==="auto"?s.trigger:this.config.mode==="always"?"always":"manual",report_source:s.source,description:o??null,started_at:t.startedAtIso,duration_ms:t.elapsed(),page:{host:typeof location<"u"?location.host:null,url:typeof location<"u"?T(location.href):null},app:{release:this.config.release??null,env:this.config.env??null},user_ref:this.config.userRef??null,sdk:{name:Io,version:Oo},dropped:{...t.dropped,dom_segments:this.dom?.buffer.droppedCount??0},has_dom:n.dom,has_video:n.video,...n.video?{video_ms:this.videoMs}:{},has_audio:n.audio}}};function Kn(e={}){if(typeof document>"u")return null;let t=document.currentScript,o=t?.dataset.key;if(!o)return null;let n=new Fe({...e,key:o,ingestUrl:t?.dataset.ingest,mode:t?.dataset.mode==="always"?"always":"ring",capture:t?.dataset.capture==="dom"?"dom":e.capture??"events",button:t?.dataset.button==="off"?"off":e.button??"on",autoReport:t?.dataset.autoReport==="off"?"off":e.autoReport??"on",feedback:t?.dataset.feedback==="off"?"off":e.feedback??"on",feedbackText:$o(t?.dataset.feedbackText)??e.feedbackText,theme:Fo(t?.dataset.theme)??e.theme??"auto",position:Uo(t?.dataset.position)??e.position,buttonIcon:t?.dataset.buttonIcon??e.buttonIcon,videoSurface:t?.dataset.videoSurface,release:t?.dataset.release,env:t?.dataset.env,userRef:t?.dataset.userRef??e.userRef});return n.start(),globalThis.espejo=n,n}function Fo(e){return e==="light"||e==="dark"||e==="auto"?e:void 0}function Uo(e){return e==="bottom-right"||e==="bottom-left"||e==="top-right"||e==="top-left"?e:void 0}function $o(e){if(e)try{let t=JSON.parse(e);return t&&typeof t=="object"?t:void 0}catch{return}}export{oo as BLOCK_ATTRIBUTE,fe as BLOCK_SELECTOR,vt as DEFAULT_BUDGETS,Fe as Espejo,Ut as FEEDBACK_DEFAULT_TEXT,ge as MASK_SELECTOR,_e as MAX_VIDEO_SECONDS,P as RingBuffer,Io as SDK_NAME,Oo as SDK_VERSION,J as SegmentRing,q as SessionRecorder,Ot as WIDGET_DEFAULT_TEXT,Kn as autoStart,Oe as canShowFeedback,Be as fetchAutoReportConfig,Me as fetchFeedbackConfig,Ie as installFeedbackWidget,Lt as normalizeAutoReportConfig,_t as normalizeFeedbackConfig,Pe as sealFeedbackShown,De as sendVote,Y as videoSupported};
@@ -0,0 +1,4 @@
1
+ type WidgetTheme = 'auto' | 'light' | 'dark';
2
+ type WidgetCorner = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
3
+
4
+ export type { WidgetCorner as W, WidgetTheme as a };
@@ -0,0 +1,4 @@
1
+ type WidgetTheme = 'auto' | 'light' | 'dark';
2
+ type WidgetCorner = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
3
+
4
+ export type { WidgetCorner as W, WidgetTheme as a };
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@espejo/browser",
3
+ "version": "0.1.0",
4
+ "description": "Espejo browser SDK: session capture, bug reports, the support agent layer and the support chat widget.",
5
+ "keywords": [
6
+ "espejo",
7
+ "bug-report",
8
+ "session-replay",
9
+ "support-chat",
10
+ "ai-agent",
11
+ "browser-sdk"
12
+ ],
13
+ "homepage": "https://docs.espejo.dev",
14
+ "license": "SEE LICENSE IN LICENSE",
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./agent": {
24
+ "types": "./dist/agent.d.ts",
25
+ "default": "./dist/agent.js"
26
+ },
27
+ "./chat": {
28
+ "types": "./dist/chat.d.ts",
29
+ "default": "./dist/chat.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "!dist/espejo*.js",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "rrweb": "^2.1.1",
43
+ "rrweb-snapshot": "^2.1.1"
44
+ },
45
+ "devDependencies": {
46
+ "@espejo/core": "0.0.0"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup && node scripts/assert-bundle-size.mjs",
50
+ "typecheck": "tsc -p tsconfig.json --noEmit"
51
+ }
52
+ }