@firstlook-uat/sdk 0.4.2 → 0.4.3

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.
@@ -1,10 +1,10 @@
1
- (function(x,w){typeof exports=="object"&&typeof module<"u"?w(exports):typeof define=="function"&&define.amd?define(["exports"],w):(x=typeof globalThis<"u"?globalThis:x||self,w(x.FirstLook={}))})(this,function(x){"use strict";const w=['input[type="password"]',"[data-sensitive]","[data-mask]",".uat-mask"];function I(a){var t,e,s,i,n,o,l,c,h,d;if(!a.endpoint)throw new Error("[FirstLook] 'endpoint' is required. Set your Supabase ingest-session URL.");return{projectId:a.projectId,apiKey:a.apiKey,userId:a.userId,role:a.role,context:a.context??{},endpoint:a.endpoint,triggers:{tapCount:((t=a.triggers)==null?void 0:t.tapCount)??5,deepLink:((e=a.triggers)==null?void 0:e.deepLink)??!0,keyboard:((s=a.triggers)==null?void 0:s.keyboard)??!0,customCheck:(i=a.triggers)==null?void 0:i.customCheck},security:{watermark:((n=a.security)==null?void 0:n.watermark)??!0,maskSelectors:((o=a.security)==null?void 0:o.maskSelectors)??w},recording:{domSnapshot:((l=a.recording)==null?void 0:l.domSnapshot)??!0,voice:((c=a.recording)==null?void 0:c.voice)??!0,maxDuration:((h=a.recording)==null?void 0:h.maxDuration)??600,snapshotInterval:((d=a.recording)==null?void 0:d.snapshotInterval)??1e3}}}function T(){return{userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language,screenWidth:window.screen.width,screenHeight:window.screen.height,pixelRatio:window.devicePixelRatio,touchSupport:"ontouchstart"in window||navigator.maxTouchPoints>0}}class M{constructor(){this.listeners=new Map}on(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>this.off(t,e)}off(t,e){var s;(s=this.listeners.get(t))==null||s.delete(e)}emit(t){var e,s;(e=this.listeners.get(t.type))==null||e.forEach(i=>{try{i(t)}catch(n){console.error("[FirstLook] Event listener error:",n)}}),(s=this.listeners.get("*"))==null||s.forEach(i=>{try{i(t)}catch(n){console.error("[FirstLook] Event listener error:",n)}})}removeAll(){this.listeners.clear()}}class L{constructor(t){this.events=t,this.quests=[],this.results=[],this.currentIndex=-1,this.sessionStartTime=0,this.blocked=!1,this.pendingFeedbacks=[]}loadQuests(t){this.quests=[...t].sort((e,s)=>e.order-s.order),this.results=[],this.currentIndex=-1,this.blocked=!1}startSession(t){this.sessionStartTime=t,this.advance()}getCurrentQuest(){return this.blocked||this.currentIndex<0||this.currentIndex>=this.quests.length?null:this.quests[this.currentIndex]}getStatus(){return{total:this.quests.length,completed:this.results.filter(t=>t.status==="COMPLETED").length,failed:this.results.filter(t=>t.status==="FAILED").length,current:this.currentIndex,isBlocked:this.blocked,isFinished:this.currentIndex>=this.quests.length}}getQuestStatuses(){return this.quests.map((t,e)=>{const s=this.results.find(i=>i.questId===t.id);return s?s.status:e===this.currentIndex&&!this.blocked?"ACTIVE":this.blocked&&e>=this.currentIndex?"BLOCKED":"PENDING"})}completeCurrentQuest(t,e){const s=this.getCurrentQuest();if(!s)return null;const i={questId:s.id,status:"COMPLETED",timestamp:Date.now(),relativeTime:Date.now()-this.sessionStartTime,voiceMemoBlob:e==null?void 0:e.voiceMemo,logs:t,comment:e==null?void 0:e.comment,concern:(e==null?void 0:e.concern)??!1,feedbacks:this.drainFeedbacks(),checklist:e==null?void 0:e.checklist};return this.results.push(i),this.events.emit({type:"quest:completed",questId:s.id}),this.advance(),i}failCurrentQuest(t,e,s,i){const n=this.getCurrentQuest();if(!n)return null;const o={questId:n.id,status:"FAILED",timestamp:Date.now(),relativeTime:Date.now()-this.sessionStartTime,voiceMemoBlob:s,logs:t,comment:e,concern:!1,feedbacks:this.drainFeedbacks(),checklist:i};return this.results.push(o),this.events.emit({type:"quest:failed",questId:n.id}),n.blocking?(this.blocked=!0,this.events.emit({type:"quest:blocked",questId:n.id})):this.advance(),o}addFeedback(t){this.pendingFeedbacks.push(t)}getResults(){return[...this.results]}drainFeedbacks(){const t=this.pendingFeedbacks;return this.pendingFeedbacks=[],t}advance(){this.currentIndex++,this.currentIndex<this.quests.length&&this.events.emit({type:"quest:started",questId:this.quests[this.currentIndex].id})}}function r(a,t,e){const s=document.createElement(a);if(t)for(const[i,n]of Object.entries(t))i==="className"?s.className=n:s.setAttribute(i,n);if(e)for(const i of e)typeof i=="string"?s.appendChild(document.createTextNode(i)):s.appendChild(i);return s}function E(){return`fl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,8)}`}function R(a){const t=Math.floor(a/60),e=a%60;return`${t.toString().padStart(2,"0")}:${e.toString().padStart(2,"0")}`}function A(a){const t=a.split(`
2
- `),e=[],s=[];let i=!1;for(const n of t){const o=n.trim(),l=o.match(/^(?:(\d+)[\.\)]\s+|[-*]\s+)(.+)$/);l?(i=!0,s.push({index:s.length,text:l[2],checked:!1})):!i&&o&&e.push(o)}return{preamble:e.join(`
3
- `),items:s}}class D{constructor(t,e){this.shadowRoot=t,this.callbacks=e,this.minimized=!1,this.timerInterval=null,this.startTime=0,this.checklist=[],this.root=document.createElement("div"),this.root.className="fl-quest-overlay",this.root.style.pointerEvents="auto";for(const s of["click","mousedown","mouseup","pointerdown","pointerup","touchstart","touchend"])this.root.addEventListener(s,i=>i.stopPropagation());this.shadowRoot.appendChild(this.root)}renderQuest(t,e,s){this.minimized=!1,this.root.className="fl-quest-overlay",this.root.innerHTML="";const i=e.indexOf("ACTIVE"),n=r("div",{className:"fl-quest-header"},[r("div",{className:"fl-quest-header-left"},[r("span",{className:"fl-quest-badge"},[`Q${i+1}/${e.length}`]),r("span",{className:"fl-quest-title"},[t.title])]),this.createMinimizeBtn()]);this.root.appendChild(n);const o=r("div",{className:"fl-quest-content"}),l=r("div",{className:"fl-quest-progress"});for(const f of e){const p=f==="COMPLETED"?"fl-completed":f==="FAILED"?"fl-failed":f==="ACTIVE"?"fl-active":"";l.appendChild(r("div",{className:`fl-quest-progress-dot ${p}`}))}const c=A(t.description);this.checklist=c.items;const h=r("div",{className:"fl-quest-body"});if(h.appendChild(l),c.items.length>0){const f=r("div",{className:"fl-checklist-counter"}),p=()=>{const b=this.checklist.filter(k=>k.checked).length;f.textContent=`✓ ${b}/${this.checklist.length} 確認済み`};p(),h.appendChild(f),c.preamble&&h.appendChild(r("p",{className:"fl-quest-description"},[c.preamble]));const g=r("ul",{className:"fl-checklist"});for(const b of this.checklist){const k=r("li",{className:"fl-checklist-item"}),v=document.createElement("input");v.type="checkbox",v.className="fl-checklist-checkbox",v.checked=b.checked;const G=r("span",{className:"fl-checklist-text"},[b.text]);v.onchange=()=>{b.checked=v.checked,k.classList.toggle("fl-checked",b.checked),p()},k.onclick=J=>{J.target!==v&&(v.checked=!v.checked,b.checked=v.checked,k.classList.toggle("fl-checked",b.checked),p())},k.appendChild(v),k.appendChild(G),g.appendChild(k)}h.appendChild(g)}else h.appendChild(r("p",{className:"fl-quest-description"},[t.description]));h.appendChild(r("div",{className:"fl-quest-memo-row"},[this.createButton("fl-btn fl-btn-memo","📝 メモを残す",()=>this.renderFeedbackModal(t.title))])),h.appendChild(r("div",{className:"fl-quest-actions"},[this.createButton("fl-btn fl-btn-ok","✓ OK",()=>this.renderFeedbackModal(t.title,"ok")),this.createButton("fl-btn fl-btn-concern","⚠ 気になる",()=>this.renderFeedbackModal(t.title,"concern")),this.createButton("fl-btn fl-btn-ng","✗ NG",this.callbacks.onNg)])),o.appendChild(h),s&&o.appendChild(r("div",{className:"fl-voice-indicator"},[r("span",{className:"fl-voice-dot"}),"Recording..."]));const d=r("div",{className:"fl-status-bar"});s&&d.appendChild(r("span",{className:"fl-status-recording"},[r("span",{className:"fl-voice-dot"}),"REC"]));const m=r("span",{className:"fl-status-timer"},["00:00"]);d.appendChild(m),o.appendChild(d),this.root.appendChild(o),this.startTimer(m),this.root.onclick=()=>{this.minimized&&(this.minimized=!1,this.root.className="fl-quest-overlay",this.root.innerHTML="",this.root.appendChild(n),this.root.appendChild(o),this.startTimer(m))}}renderFeedbackModal(t,e="memo"){const s=r("div",{className:"fl-feedback-modal"}),i={ng:"fl-quest-header fl-quest-header-ng",ok:"fl-quest-header fl-quest-header-ok",concern:"fl-quest-header fl-quest-header-concern",memo:"fl-quest-header"},n={ng:"NG",ok:"✓ OK",concern:"⚠ 気になる",memo:"📝"},o={ng:"fl-quest-badge fl-badge-ng",ok:"fl-quest-badge fl-badge-ok",concern:"fl-quest-badge fl-badge-concern",memo:"fl-quest-badge"},l=r("div",{className:i[e]},[r("div",{className:"fl-quest-header-left"},[r("span",{className:o[e]},[n[e]]),r("span",{className:"fl-quest-title"},[t])])]);s.appendChild(l);const c=r("div",{className:"fl-quest-content"}),h={ng:"何がうまくいかなかったか教えてください...",ok:"コメント(任意)",concern:"気になった点を教えてください...",memo:"気づいたことをメモ...📝"},d=document.createElement("textarea");d.className="fl-feedback-textarea fl-feedback-textarea-modal",d.placeholder=h[e],d.rows=3,c.appendChild(d);const m="送信",f=e==="ok"?"スキップ":"キャンセル",p=r("div",{className:"fl-quest-actions fl-feedback-modal-actions"},[this.createButton("fl-btn fl-btn-skip",f,()=>{e==="ng"?this.callbacks.onNgWithFeedback(""):e==="ok"?this.callbacks.onOkWithFeedback(""):e==="concern"&&this.callbacks.onConcern(""),s.remove()}),this.createButton("fl-btn fl-btn-finish",m,()=>{const g=d.value.trim();if(e==="ng")this.callbacks.onNgWithFeedback(g);else if(e==="ok")this.callbacks.onOkWithFeedback(g);else if(e==="concern")this.callbacks.onConcern(g);else{if(!g){s.remove();return}this.callbacks.onMemo(g)}s.remove()})]);c.appendChild(p),s.appendChild(c),this.root.appendChild(s),d.focus()}renderSummary(t,e,s){this.stopTimer(),this.root.className="fl-quest-overlay",this.root.innerHTML="";const i=r("div",{className:"fl-summary"},[r("div",{className:"fl-summary-icon"},[t===s?"🎉":"📋"]),r("div",{className:"fl-summary-title"},[t===s?"All Quests Complete!":"Session Complete"]),r("div",{className:"fl-summary-subtitle"},[`${t+e} of ${s} quests attempted`]),r("div",{className:"fl-summary-stats"},[this.createStat(t.toString(),"Passed","fl-ok"),this.createStat(e.toString(),"Failed","fl-ng")]),this.createButton("fl-btn fl-btn-finish","Finish & Upload",this.callbacks.onFinish)]);this.root.appendChild(i)}renderBlocked(t){this.stopTimer(),this.root.className="fl-quest-overlay",this.root.innerHTML="";const e=r("div",{className:"fl-summary"},[r("div",{className:"fl-summary-icon"},["🛑"]),r("div",{className:"fl-summary-title"},["Blocked"]),r("div",{className:"fl-summary-subtitle"},[`Quest "${t}" is blocking. Session halted.`]),this.createButton("fl-btn fl-btn-finish","Finish & Upload",this.callbacks.onFinish)]);this.root.appendChild(e)}getChecklist(){return this.checklist.length>0?[...this.checklist]:void 0}destroy(){this.stopTimer(),this.root.remove()}createMinimizeBtn(){const t=r("button",{className:"fl-quest-minimize-btn"},["−"]);return t.onclick=e=>{e.stopPropagation(),this.minimized=!0,this.root.className="fl-quest-overlay fl-minimized",this.root.innerHTML="";const s=r("span",{className:"fl-minimize-icon"},["🔍"]);this.root.appendChild(s),this.callbacks.onMinimize()},t}createButton(t,e,s){const i=r("button",{className:t});return i.textContent=e,i.onclick=n=>{n.stopPropagation(),s()},i}createStat(t,e,s){return r("div",{className:"fl-stat"},[r("div",{className:`fl-stat-value ${s}`},[t]),r("div",{className:"fl-stat-label"},[e])])}startTimer(t){this.stopTimer(),this.startTime||(this.startTime=Date.now()),this.timerInterval=setInterval(()=>{const e=Math.floor((Date.now()-this.startTime)/1e3);t.textContent=R(e)},1e3)}stopTimer(){this.timerInterval&&(clearInterval(this.timerInterval),this.timerInterval=null)}}const N=100,F=2*1024*1024;class P{constructor(t,e,s){this.config=t,this.events=e,this.maskSelector=s,this.actionLogs=[],this.snapshots=[],this.snapshotTimer=null,this.startTime=0,this.running=!1,this.handleClick=this.onClick.bind(this),this.handleScroll=this.throttle(this.onScroll.bind(this),500),this.handleInput=this.onInput.bind(this)}start(){this.running||(this.running=!0,this.startTime=Date.now(),this.actionLogs=[],this.snapshots=[],document.addEventListener("click",this.handleClick,{capture:!0,passive:!0}),document.addEventListener("scroll",this.handleScroll,{capture:!0,passive:!0}),document.addEventListener("input",this.handleInput,{capture:!0,passive:!0}),this.config.recording.domSnapshot&&(this.captureSnapshot(),this.snapshotTimer=setInterval(()=>this.captureSnapshot(),this.config.recording.snapshotInterval)),this.events.emit({type:"recording:started"}))}stop(){this.running&&(this.running=!1,document.removeEventListener("click",this.handleClick,{capture:!0}),document.removeEventListener("scroll",this.handleScroll,{capture:!0}),document.removeEventListener("input",this.handleInput,{capture:!0}),this.snapshotTimer&&(clearInterval(this.snapshotTimer),this.snapshotTimer=null),this.captureSnapshot(),this.events.emit({type:"recording:stopped"}))}getActionLogs(){return[...this.actionLogs]}getSnapshots(){return[...this.snapshots]}getElapsedMs(){return this.running?Date.now()-this.startTime:0}addLog(t){this.actionLogs.push({...t,timestamp:Date.now()-this.startTime})}onClick(t){const e=t.target;this.addLog({type:"click",target:y(e)})}onScroll(){this.addLog({type:"scroll",value:`${window.scrollX},${window.scrollY}`})}onInput(t){const e=t.target;if(e.hasAttribute("data-fl-masked"))this.addLog({type:"input",target:y(e),value:"[MASKED]"});else{const s=e.value;this.addLog({type:"input",target:y(e),value:s==null?void 0:s.slice(0,100)})}}captureSnapshot(){try{const t=document.documentElement.cloneNode(!0);if(this.maskSelector){const n=t.querySelectorAll(this.maskSelector);for(const o of n)(o instanceof HTMLInputElement||o instanceof HTMLTextAreaElement)&&(o.value="***"),o.textContent="***"}const e=t.querySelectorAll("script");for(const n of e)n.remove();const s=t.querySelector("#firstlook-sdk-root");s==null||s.remove();const i=t.outerHTML;if(i.length>F)return;this.snapshots.length>=N&&this.snapshots.shift(),this.snapshots.push({type:"dom-snapshot",timestamp:Date.now()-this.startTime,data:i})}catch{}}throttle(t,e){let s=0;return(...i)=>{const n=Date.now();n-s>=e&&(s=n,t(...i))}}}function y(a){var n;const t=a.tagName.toLowerCase(),e=a.id?`#${a.id}`:"",s=a.className&&typeof a.className=="string"?`.${a.className.trim().split(/\s+/).slice(0,2).join(".")}`:"",i=((n=a.textContent)==null?void 0:n.trim().slice(0,30))||"";return`${t}${e}${s}${i?` "${i}"`:""}`}class z{constructor(t){this.events=t,this.mediaRecorder=null,this.stream=null,this.chunks=[],this._recording=!1}get recording(){return this._recording}async start(){if(!this._recording)try{this.stream=await navigator.mediaDevices.getUserMedia({audio:!0});const t=this.getSupportedMimeType(),e=t?{mimeType:t}:{};this.mediaRecorder=new MediaRecorder(this.stream,e),this.chunks=[],this.mediaRecorder.ondataavailable=s=>{s.data.size>0&&this.chunks.push(s.data)},this.mediaRecorder.start(1e3),this._recording=!0}catch(t){console.warn("[FirstLook] Voice recording unavailable:",t),this.events.emit({type:"error",error:new Error(`Voice recording failed: ${t.message}`)})}}async stopAsync(){return!this._recording||!this.mediaRecorder?null:new Promise(t=>{this.mediaRecorder.onstop=()=>{var s;const e=this.chunks.length>0?new Blob(this.chunks,{type:((s=this.mediaRecorder)==null?void 0:s.mimeType)||"audio/webm"}):null;this.cleanup(),t(e)},this.mediaRecorder.stop()})}async captureSnippet(t=1e4){return await this.start(),new Promise(e=>{setTimeout(async()=>{const s=await this.stopAsync();e(s)},t)})}cleanup(){if(this._recording=!1,this.stream){for(const t of this.stream.getTracks())t.stop();this.stream=null}this.mediaRecorder=null,this.chunks=[]}getSupportedMimeType(){const t=["audio/webm;codecs=opus","audio/webm","audio/ogg;codecs=opus","audio/mp4"];for(const e of t)if(typeof MediaRecorder<"u"&&MediaRecorder.isTypeSupported(e))return e;return""}}class O{constructor(t,e){this.shadowRoot=t,this.config=e,this.container=null,this.refreshInterval=null,this.cachedIp=null}show(){this.container||(this.container=document.createElement("div"),this.container.className="fl-watermark",this.renderTiles(),this.shadowRoot.appendChild(this.container),this.fetchIp().then(()=>this.renderTiles()),this.refreshInterval=setInterval(()=>this.renderTiles(),6e4))}hide(){this.container&&(this.container.remove(),this.container=null),this.refreshInterval&&(clearInterval(this.refreshInterval),this.refreshInterval=null)}async fetchIp(){if(!this.cachedIp)try{const t=await fetch("https://api.ipify.org?format=text");t.ok&&(this.cachedIp=await t.text())}catch{this.cachedIp="N/A"}}renderTiles(){if(!this.container)return;this.container.innerHTML="";const t=this.cachedIp??"",e=new Date().toISOString().slice(0,19),s=t?`${this.config.userId} | ${e} | ${t}`:`${this.config.userId} | ${e}`,i=320,n=80,o=Math.ceil(window.innerWidth/i)+1,l=Math.ceil(window.innerHeight/n)+1;for(let c=0;c<l;c++)for(let h=0;h<o;h++){const d=document.createElement("span");d.className="fl-watermark-tile",d.textContent=s,d.style.left=`${h*i}px`,d.style.top=`${c*n}px`,this.container.appendChild(d)}}}class Q{constructor(t){this.selectors=t,this.observer=null,this.maskedElements=new Set}start(){this.scanAndMark(),this.observer=new MutationObserver(()=>this.scanAndMark()),this.observer.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["type","class","data-sensitive","data-mask"]})}stop(){var t;(t=this.observer)==null||t.disconnect(),this.observer=null;for(const e of this.maskedElements)e.removeAttribute("data-fl-masked");this.maskedElements.clear()}isMasked(t){return t.hasAttribute("data-fl-masked")}getCombinedSelector(){return this.selectors.join(", ")}scanAndMark(){const t=this.getCombinedSelector();if(t)try{const e=document.querySelectorAll(t);for(const s of e)this.maskedElements.has(s)||(s.setAttribute("data-fl-masked","true"),this.maskedElements.add(s));for(const s of this.maskedElements)document.contains(s)||this.maskedElements.delete(s)}catch{}}}const B="firstlook_uat",j=1,u={sessions:"sessions",recordings:"recordings",annotations:"annotations",uploadQueue:"upload_queue"};class ${constructor(){this.db=null}async open(){if(!this.db)return new Promise((t,e)=>{const s=indexedDB.open(B,j);s.onupgradeneeded=()=>{const i=s.result;i.objectStoreNames.contains(u.sessions)||i.createObjectStore(u.sessions,{keyPath:"sessionId"}),i.objectStoreNames.contains(u.recordings)||i.createObjectStore(u.recordings,{autoIncrement:!0}).createIndex("sessionId","sessionId",{unique:!1}),i.objectStoreNames.contains(u.annotations)||i.createObjectStore(u.annotations,{autoIncrement:!0}).createIndex("sessionId","sessionId",{unique:!1}),i.objectStoreNames.contains(u.uploadQueue)||i.createObjectStore(u.uploadQueue,{autoIncrement:!0})},s.onsuccess=()=>{this.db=s.result,t()},s.onerror=()=>e(s.error)})}async saveSession(t){await this.put(u.sessions,t)}async getSession(t){return this.get(u.sessions,t)}async saveRecording(t,e){await this.put(u.recordings,{sessionId:t,...e})}async saveAnnotation(t,e){await this.put(u.annotations,{sessionId:t,...e})}async getAnnotations(t){return this.getAllByIndex(u.annotations,"sessionId",t)}async enqueueUpload(t){await this.put(u.uploadQueue,{payload:t,createdAt:Date.now(),attempts:0})}async getPendingUploads(){const t=this.ensureDb();return new Promise((e,s)=>{const o=t.transaction(u.uploadQueue,"readonly").objectStore(u.uploadQueue).openCursor(),l=[];o.onsuccess=()=>{const c=o.result;c?(l.push({key:c.key,payload:c.value.payload,attempts:c.value.attempts,lastAttemptAt:c.value.lastAttemptAt}),c.continue()):e(l)},o.onerror=()=>s(o.error)})}async removeFromQueue(t){const e=this.ensureDb();return new Promise((s,i)=>{const o=e.transaction(u.uploadQueue,"readwrite").objectStore(u.uploadQueue).delete(t);o.onsuccess=()=>s(),o.onerror=()=>i(o.error)})}async incrementAttempts(t){const e=this.ensureDb();return new Promise((s,i)=>{const n=e.transaction(u.uploadQueue,"readwrite"),o=n.objectStore(u.uploadQueue),l=o.get(t);l.onsuccess=()=>{const c=l.result;c&&(c.attempts=(c.attempts??0)+1,c.lastAttemptAt=Date.now(),o.put(c,t))},n.oncomplete=()=>s(),n.onerror=()=>i(n.error)})}async clearSession(t){const s=this.ensureDb().transaction([u.sessions,u.recordings,u.annotations],"readwrite");s.objectStore(u.sessions).delete(t);for(const i of[u.recordings,u.annotations]){const l=s.objectStore(i).index("sessionId").openCursor(IDBKeyRange.only(t));l.onsuccess=()=>{const c=l.result;c&&(c.delete(),c.continue())}}return new Promise((i,n)=>{s.oncomplete=()=>i(),s.onerror=()=>n(s.error)})}close(){var t;(t=this.db)==null||t.close(),this.db=null}ensureDb(){if(!this.db)throw new Error("[FirstLook] Storage not opened. Call open() first.");return this.db}async put(t,e){const s=this.ensureDb();return new Promise((i,n)=>{const o=s.transaction(t,"readwrite");o.objectStore(t).put(e),o.oncomplete=()=>i(),o.onerror=()=>n(o.error)})}async get(t,e){const s=this.ensureDb();return new Promise((i,n)=>{const l=s.transaction(t,"readonly").objectStore(t).get(e);l.onsuccess=()=>i(l.result),l.onerror=()=>n(l.error)})}async getAllByIndex(t,e,s){const i=this.ensureDb();return new Promise((n,o)=>{const h=i.transaction(t,"readonly").objectStore(t).index(e).getAll(s);h.onsuccess=()=>n(h.result),h.onerror=()=>o(h.error)})}}const C=["#e17055","#6c5ce7","#00b894","#fdcb6e","#ffffff"];class U{constructor(t){this.shadowRoot=t,this.overlay=null,this.canvas=null,this.ctx=null,this.drawing=!1,this.currentPath=[],this.paths=[],this.selectedColor=C[0],this.screenshotDataUrl=""}async open(){return this.screenshotDataUrl=await this.captureScreenshot(),new Promise(t=>{this.overlay=r("div",{className:"fl-annotation-overlay"});const e=r("div",{className:"fl-annotation-canvas-wrap"});this.canvas=document.createElement("canvas"),this.canvas.className="fl-annotation-canvas",e.appendChild(this.canvas),this.overlay.appendChild(e);const s=r("input",{className:"fl-annotation-comment",type:"text",placeholder:"Add a comment..."}),i=r("div",{className:"fl-color-picker"});for(const c of C){const h=r("div",{className:`fl-color-swatch ${c===this.selectedColor?"fl-selected":""}`});h.style.background=c,h.onclick=()=>{this.selectedColor=c,i.querySelectorAll(".fl-color-swatch").forEach(d=>d.classList.remove("fl-selected")),h.classList.add("fl-selected")},i.appendChild(h)}const n=r("button",{className:"fl-btn fl-btn-ok"},["Submit"]);n.onclick=()=>{const c={screenshotDataUrl:this.getAnnotatedImage(),drawings:[...this.paths],comment:s.value,timestamp:Date.now()};this.close(),t(c)};const o=r("button",{className:"fl-btn fl-btn-ng"},["Cancel"]);o.onclick=()=>{this.close(),t(null)};const l=r("div",{className:"fl-annotation-toolbar"},[i,s,n,o]);this.overlay.appendChild(l),this.shadowRoot.appendChild(this.overlay),this.initCanvas()})}close(){var t;(t=this.overlay)==null||t.remove(),this.overlay=null,this.canvas=null,this.ctx=null,this.paths=[],this.currentPath=[]}async captureScreenshot(){try{const t=window.innerWidth,e=window.innerHeight,s=document.documentElement.cloneNode(!0),i=s.querySelector("#firstlook-sdk-root");i==null||i.remove();const n=s.querySelectorAll('[data-fl-masked], input[type="password"]');for(const p of n)(p instanceof HTMLInputElement||p instanceof HTMLTextAreaElement)&&(p.value="[MASKED]"),p.textContent="[MASKED]";for(const p of s.querySelectorAll("script"))p.remove();const o=s.querySelector("body");if(o){const p=window.getComputedStyle(document.body);o.style.backgroundColor=p.backgroundColor,o.style.color=p.color,o.style.fontFamily=p.fontFamily,o.style.margin="0",o.style.overflow="hidden"}const l=new XMLSerializer().serializeToString(s),c=`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${e}">
1
+ (function(x,w){typeof exports=="object"&&typeof module<"u"?w(exports):typeof define=="function"&&define.amd?define(["exports"],w):(x=typeof globalThis<"u"?globalThis:x||self,w(x.FirstLook={}))})(this,function(x){"use strict";const w=['input[type="password"]',"[data-sensitive]","[data-mask]",".uat-mask"];function L(c){var e,t,s,i,n,o,l,a,h,d;if(!c.endpoint)throw new Error("[FirstLook] 'endpoint' is required. Set your Supabase ingest-session URL.");return{projectId:c.projectId,apiKey:c.apiKey,userId:c.userId,role:c.role,context:c.context??{},endpoint:c.endpoint,triggers:{tapCount:((e=c.triggers)==null?void 0:e.tapCount)??5,deepLink:((t=c.triggers)==null?void 0:t.deepLink)??!0,keyboard:((s=c.triggers)==null?void 0:s.keyboard)??!0,customCheck:(i=c.triggers)==null?void 0:i.customCheck},security:{watermark:((n=c.security)==null?void 0:n.watermark)??!0,maskSelectors:((o=c.security)==null?void 0:o.maskSelectors)??w},recording:{domSnapshot:((l=c.recording)==null?void 0:l.domSnapshot)??!0,voice:((a=c.recording)==null?void 0:a.voice)??!0,maxDuration:((h=c.recording)==null?void 0:h.maxDuration)??600,snapshotInterval:((d=c.recording)==null?void 0:d.snapshotInterval)??1e3}}}function C(){return{userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language,screenWidth:window.screen.width,screenHeight:window.screen.height,pixelRatio:window.devicePixelRatio,touchSupport:"ontouchstart"in window||navigator.maxTouchPoints>0}}class A{constructor(){this.listeners=new Map}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}off(e,t){var s;(s=this.listeners.get(e))==null||s.delete(t)}emit(e){var t,s;(t=this.listeners.get(e.type))==null||t.forEach(i=>{try{i(e)}catch(n){console.error("[FirstLook] Event listener error:",n)}}),(s=this.listeners.get("*"))==null||s.forEach(i=>{try{i(e)}catch(n){console.error("[FirstLook] Event listener error:",n)}})}removeAll(){this.listeners.clear()}}class R{constructor(e){this.events=e,this.quests=[],this.results=[],this.currentIndex=-1,this.sessionStartTime=0,this.blocked=!1,this.pendingFeedbacks=[]}loadQuests(e){this.quests=[...e].sort((t,s)=>t.order-s.order),this.results=[],this.currentIndex=-1,this.blocked=!1}startSession(e){this.sessionStartTime=e,this.advance()}getCurrentQuest(){return this.blocked||this.currentIndex<0||this.currentIndex>=this.quests.length?null:this.quests[this.currentIndex]}getStatus(){return{total:this.quests.length,completed:this.results.filter(e=>e.status==="COMPLETED").length,failed:this.results.filter(e=>e.status==="FAILED").length,current:this.currentIndex,isBlocked:this.blocked,isFinished:this.currentIndex>=this.quests.length}}getQuestStatuses(){return this.quests.map((e,t)=>{const s=this.results.find(i=>i.questId===e.id);return s?s.status:t===this.currentIndex&&!this.blocked?"ACTIVE":this.blocked&&t>=this.currentIndex?"BLOCKED":"PENDING"})}completeCurrentQuest(e,t){const s=this.getCurrentQuest();if(!s)return null;const i={questId:s.id,status:"COMPLETED",timestamp:Date.now(),relativeTime:Date.now()-this.sessionStartTime,voiceMemoBlob:t==null?void 0:t.voiceMemo,logs:e,comment:t==null?void 0:t.comment,concern:(t==null?void 0:t.concern)??!1,feedbacks:this.drainFeedbacks(),checklist:t==null?void 0:t.checklist};return this.results.push(i),this.events.emit({type:"quest:completed",questId:s.id}),this.advance(),i}failCurrentQuest(e,t,s,i){const n=this.getCurrentQuest();if(!n)return null;const o={questId:n.id,status:"FAILED",timestamp:Date.now(),relativeTime:Date.now()-this.sessionStartTime,voiceMemoBlob:s,logs:e,comment:t,concern:!1,feedbacks:this.drainFeedbacks(),checklist:i};return this.results.push(o),this.events.emit({type:"quest:failed",questId:n.id}),n.blocking?(this.blocked=!0,this.events.emit({type:"quest:blocked",questId:n.id})):this.advance(),o}addFeedback(e){this.pendingFeedbacks.push(e)}getResults(){return[...this.results]}drainFeedbacks(){const e=this.pendingFeedbacks;return this.pendingFeedbacks=[],e}advance(){this.currentIndex++,this.currentIndex<this.quests.length&&this.events.emit({type:"quest:started",questId:this.quests[this.currentIndex].id})}}function r(c,e,t){const s=document.createElement(c);if(e)for(const[i,n]of Object.entries(e))i==="className"?s.className=n:s.setAttribute(i,n);if(t)for(const i of t)typeof i=="string"?s.appendChild(document.createTextNode(i)):s.appendChild(i);return s}function E(){return`fl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,8)}`}function D(c){const e=Math.floor(c/60),t=c%60;return`${e.toString().padStart(2,"0")}:${t.toString().padStart(2,"0")}`}function N(c){const e=c.split(`
2
+ `),t=[],s=[];let i=!1;for(const n of e){const o=n.trim(),l=o.match(/^(?:(\d+)[\.\)]\s+|[-*]\s+)(.+)$/);l?(i=!0,s.push({index:s.length,text:l[2],checked:!1})):!i&&o&&t.push(o)}return{preamble:t.join(`
3
+ `),items:s}}class F{constructor(e,t){this.shadowRoot=e,this.callbacks=t,this.minimized=!1,this.timerInterval=null,this.startTime=0,this.checklist=[],this.root=document.createElement("div"),this.root.className="fl-quest-overlay",this.root.style.pointerEvents="auto";for(const s of["click","mousedown","mouseup","pointerdown","pointerup","touchstart","touchend"])this.root.addEventListener(s,i=>i.stopPropagation());this.shadowRoot.appendChild(this.root)}renderQuest(e,t,s){this.minimized=!1,this.root.className="fl-quest-overlay",this.root.innerHTML="";const i=t.indexOf("ACTIVE"),n=r("div",{className:"fl-quest-header"},[r("div",{className:"fl-quest-header-left"},[r("span",{className:"fl-quest-badge"},[`Q${i+1}/${t.length}`]),r("span",{className:"fl-quest-title"},[e.title])]),this.createMinimizeBtn()]);this.root.appendChild(n);const o=r("div",{className:"fl-quest-content"}),l=r("div",{className:"fl-quest-progress"});for(const f of t){const p=f==="COMPLETED"?"fl-completed":f==="FAILED"?"fl-failed":f==="ACTIVE"?"fl-active":"";l.appendChild(r("div",{className:`fl-quest-progress-dot ${p}`}))}const a=N(e.description);this.checklist=a.items;const h=r("div",{className:"fl-quest-body"});if(h.appendChild(l),a.items.length>0){const f=r("div",{className:"fl-checklist-counter"}),p=()=>{const b=this.checklist.filter(k=>k.checked).length;f.textContent=`✓ ${b}/${this.checklist.length} 確認済み`};p(),h.appendChild(f),a.preamble&&h.appendChild(r("p",{className:"fl-quest-description"},[a.preamble]));const g=r("ul",{className:"fl-checklist"});for(const b of this.checklist){const k=r("li",{className:"fl-checklist-item"}),v=document.createElement("input");v.type="checkbox",v.className="fl-checklist-checkbox",v.checked=b.checked;const J=r("span",{className:"fl-checklist-text"},[b.text]);v.onchange=()=>{b.checked=v.checked,k.classList.toggle("fl-checked",b.checked),p()},k.onclick=Z=>{Z.target!==v&&(v.checked=!v.checked,b.checked=v.checked,k.classList.toggle("fl-checked",b.checked),p())},k.appendChild(v),k.appendChild(J),g.appendChild(k)}h.appendChild(g)}else h.appendChild(r("p",{className:"fl-quest-description"},[e.description]));h.appendChild(r("div",{className:"fl-quest-memo-row"},[this.createButton("fl-btn fl-btn-memo","📝 メモを残す",()=>this.renderFeedbackModal(e.title))])),h.appendChild(r("div",{className:"fl-quest-actions"},[this.createButton("fl-btn fl-btn-ok","✓ OK",()=>this.renderFeedbackModal(e.title,"ok")),this.createButton("fl-btn fl-btn-concern","⚠ 気になる",()=>this.renderFeedbackModal(e.title,"concern")),this.createButton("fl-btn fl-btn-ng","✗ NG",this.callbacks.onNg)])),o.appendChild(h),s&&o.appendChild(r("div",{className:"fl-voice-indicator"},[r("span",{className:"fl-voice-dot"}),"Recording..."]));const d=r("div",{className:"fl-status-bar"});s&&d.appendChild(r("span",{className:"fl-status-recording"},[r("span",{className:"fl-voice-dot"}),"REC"]));const m=r("span",{className:"fl-status-timer"},["00:00"]);d.appendChild(m),o.appendChild(d),this.root.appendChild(o),this.startTimer(m),this.root.onclick=()=>{this.minimized&&(this.minimized=!1,this.root.className="fl-quest-overlay",this.root.innerHTML="",this.root.appendChild(n),this.root.appendChild(o),this.startTimer(m))}}renderFeedbackModal(e,t="memo"){const s=r("div",{className:"fl-feedback-modal"}),i={ng:"fl-quest-header fl-quest-header-ng",ok:"fl-quest-header fl-quest-header-ok",concern:"fl-quest-header fl-quest-header-concern",memo:"fl-quest-header"},n={ng:"NG",ok:"✓ OK",concern:"⚠ 気になる",memo:"📝"},o={ng:"fl-quest-badge fl-badge-ng",ok:"fl-quest-badge fl-badge-ok",concern:"fl-quest-badge fl-badge-concern",memo:"fl-quest-badge"},l=r("div",{className:i[t]},[r("div",{className:"fl-quest-header-left"},[r("span",{className:o[t]},[n[t]]),r("span",{className:"fl-quest-title"},[e])])]);s.appendChild(l);const a=r("div",{className:"fl-quest-content"}),h={ng:"何がうまくいかなかったか教えてください...",ok:"コメント(任意)",concern:"気になった点を教えてください...",memo:"気づいたことをメモ...📝"},d=document.createElement("textarea");d.className="fl-feedback-textarea fl-feedback-textarea-modal",d.placeholder=h[t],d.rows=3,a.appendChild(d);const m="送信",f=t==="ok"?"スキップ":"キャンセル",p=r("div",{className:"fl-quest-actions fl-feedback-modal-actions"},[this.createButton("fl-btn fl-btn-skip",f,()=>{t==="ng"?this.callbacks.onNgWithFeedback(""):t==="ok"?this.callbacks.onOkWithFeedback(""):t==="concern"&&this.callbacks.onConcern(""),s.remove()}),this.createButton("fl-btn fl-btn-finish",m,()=>{const g=d.value.trim();if(t==="ng")this.callbacks.onNgWithFeedback(g);else if(t==="ok")this.callbacks.onOkWithFeedback(g);else if(t==="concern")this.callbacks.onConcern(g);else{if(!g){s.remove();return}this.callbacks.onMemo(g)}s.remove()})]);a.appendChild(p),s.appendChild(a),this.root.appendChild(s),d.focus()}renderSummary(e,t,s){this.stopTimer(),this.root.className="fl-quest-overlay",this.root.innerHTML="";const i=r("div",{className:"fl-summary"},[r("div",{className:"fl-summary-icon"},[e===s?"🎉":"📋"]),r("div",{className:"fl-summary-title"},[e===s?"All Quests Complete!":"Session Complete"]),r("div",{className:"fl-summary-subtitle"},[`${e+t} of ${s} quests attempted`]),r("div",{className:"fl-summary-stats"},[this.createStat(e.toString(),"Passed","fl-ok"),this.createStat(t.toString(),"Failed","fl-ng")]),this.createButton("fl-btn fl-btn-finish","Finish & Upload",this.callbacks.onFinish)]);this.root.appendChild(i)}renderBlocked(e){this.stopTimer(),this.root.className="fl-quest-overlay",this.root.innerHTML="";const t=r("div",{className:"fl-summary"},[r("div",{className:"fl-summary-icon"},["🛑"]),r("div",{className:"fl-summary-title"},["Blocked"]),r("div",{className:"fl-summary-subtitle"},[`Quest "${e}" is blocking. Session halted.`]),this.createButton("fl-btn fl-btn-finish","Finish & Upload",this.callbacks.onFinish)]);this.root.appendChild(t)}getChecklist(){return this.checklist.length>0?[...this.checklist]:void 0}destroy(){this.stopTimer(),this.root.remove()}createMinimizeBtn(){const e=r("button",{className:"fl-quest-minimize-btn"},["−"]);return e.onclick=t=>{t.stopPropagation(),this.minimized=!0,this.root.className="fl-quest-overlay fl-minimized",this.root.innerHTML="";const s=r("span",{className:"fl-minimize-icon"},["🔍"]);this.root.appendChild(s),this.callbacks.onMinimize()},e}createButton(e,t,s){const i=r("button",{className:e});return i.textContent=t,i.onclick=n=>{n.stopPropagation(),s()},i}createStat(e,t,s){return r("div",{className:"fl-stat"},[r("div",{className:`fl-stat-value ${s}`},[e]),r("div",{className:"fl-stat-label"},[t])])}startTimer(e){this.stopTimer(),this.startTime||(this.startTime=Date.now()),this.timerInterval=setInterval(()=>{const t=Math.floor((Date.now()-this.startTime)/1e3);e.textContent=D(t)},1e3)}stopTimer(){this.timerInterval&&(clearInterval(this.timerInterval),this.timerInterval=null)}}const P=100,O=2*1024*1024;class z{constructor(e,t,s){this.config=e,this.events=t,this.maskSelector=s,this.actionLogs=[],this.snapshots=[],this.snapshotTimer=null,this.startTime=0,this.running=!1,this.handleClick=this.onClick.bind(this),this.handleScroll=this.throttle(this.onScroll.bind(this),500),this.handleInput=this.onInput.bind(this)}start(){this.running||(this.running=!0,this.startTime=Date.now(),this.actionLogs=[],this.snapshots=[],document.addEventListener("click",this.handleClick,{capture:!0,passive:!0}),document.addEventListener("scroll",this.handleScroll,{capture:!0,passive:!0}),document.addEventListener("input",this.handleInput,{capture:!0,passive:!0}),this.config.recording.domSnapshot&&(this.captureSnapshot(),this.snapshotTimer=setInterval(()=>this.captureSnapshot(),this.config.recording.snapshotInterval)),this.events.emit({type:"recording:started"}))}stop(){this.running&&(this.running=!1,document.removeEventListener("click",this.handleClick,{capture:!0}),document.removeEventListener("scroll",this.handleScroll,{capture:!0}),document.removeEventListener("input",this.handleInput,{capture:!0}),this.snapshotTimer&&(clearInterval(this.snapshotTimer),this.snapshotTimer=null),this.captureSnapshot(),this.events.emit({type:"recording:stopped"}))}getActionLogs(){return[...this.actionLogs]}getSnapshots(){return[...this.snapshots]}getElapsedMs(){return this.running?Date.now()-this.startTime:0}addLog(e){this.actionLogs.push({...e,timestamp:Date.now()-this.startTime})}onClick(e){const t=e.target;this.addLog({type:"click",target:S(t)})}onScroll(){this.addLog({type:"scroll",value:`${window.scrollX},${window.scrollY}`})}onInput(e){const t=e.target;if(t.hasAttribute("data-fl-masked"))this.addLog({type:"input",target:S(t),value:"[MASKED]"});else{const s=t.value;this.addLog({type:"input",target:S(t),value:s==null?void 0:s.slice(0,100)})}}captureSnapshot(){try{const e=document.documentElement.cloneNode(!0);if(this.maskSelector){const n=e.querySelectorAll(this.maskSelector);for(const o of n)(o instanceof HTMLInputElement||o instanceof HTMLTextAreaElement)&&(o.value="***"),o.textContent="***"}const t=e.querySelectorAll("script");for(const n of t)n.remove();const s=e.querySelector("#firstlook-sdk-root");s==null||s.remove();const i=e.outerHTML;if(i.length>O)return;this.snapshots.length>=P&&this.snapshots.shift(),this.snapshots.push({type:"dom-snapshot",timestamp:Date.now()-this.startTime,data:i})}catch{}}throttle(e,t){let s=0;return(...i)=>{const n=Date.now();n-s>=t&&(s=n,e(...i))}}}function S(c){var n;const e=c.tagName.toLowerCase(),t=c.id?`#${c.id}`:"",s=c.className&&typeof c.className=="string"?`.${c.className.trim().split(/\s+/).slice(0,2).join(".")}`:"",i=((n=c.textContent)==null?void 0:n.trim().slice(0,30))||"";return`${e}${t}${s}${i?` "${i}"`:""}`}class Q{constructor(e){this.events=e,this.mediaRecorder=null,this.stream=null,this.chunks=[],this._recording=!1}get recording(){return this._recording}async start(){if(!this._recording)try{this.stream=await navigator.mediaDevices.getUserMedia({audio:!0});const e=this.getSupportedMimeType(),t=e?{mimeType:e}:{};this.mediaRecorder=new MediaRecorder(this.stream,t),this.chunks=[],this.mediaRecorder.ondataavailable=s=>{s.data.size>0&&this.chunks.push(s.data)},this.mediaRecorder.start(1e3),this._recording=!0}catch(e){console.warn("[FirstLook] Voice recording unavailable:",e),this.events.emit({type:"error",error:new Error(`Voice recording failed: ${e.message}`)})}}async stopAsync(){return!this._recording||!this.mediaRecorder?null:new Promise(e=>{this.mediaRecorder.onstop=()=>{var s;const t=this.chunks.length>0?new Blob(this.chunks,{type:((s=this.mediaRecorder)==null?void 0:s.mimeType)||"audio/webm"}):null;this.cleanup(),e(t)},this.mediaRecorder.stop()})}async captureSnippet(e=1e4){return await this.start(),new Promise(t=>{setTimeout(async()=>{const s=await this.stopAsync();t(s)},e)})}cleanup(){if(this._recording=!1,this.stream){for(const e of this.stream.getTracks())e.stop();this.stream=null}this.mediaRecorder=null,this.chunks=[]}getSupportedMimeType(){const e=["audio/webm;codecs=opus","audio/webm","audio/ogg;codecs=opus","audio/mp4"];for(const t of e)if(typeof MediaRecorder<"u"&&MediaRecorder.isTypeSupported(t))return t;return""}}class B{constructor(e,t){this.shadowRoot=e,this.config=t,this.container=null,this.refreshInterval=null,this.cachedIp=null}show(){this.container||(this.container=document.createElement("div"),this.container.className="fl-watermark",this.renderTiles(),this.shadowRoot.appendChild(this.container),this.fetchIp().then(()=>this.renderTiles()),this.refreshInterval=setInterval(()=>this.renderTiles(),6e4))}hide(){this.container&&(this.container.remove(),this.container=null),this.refreshInterval&&(clearInterval(this.refreshInterval),this.refreshInterval=null)}async fetchIp(){if(!this.cachedIp)try{const e=await fetch("https://api.ipify.org?format=text");e.ok&&(this.cachedIp=await e.text())}catch{this.cachedIp="N/A"}}renderTiles(){if(!this.container)return;this.container.innerHTML="";const e=this.cachedIp??"",t=new Date().toISOString().slice(0,19),s=e?`${this.config.userId} | ${t} | ${e}`:`${this.config.userId} | ${t}`,i=320,n=80,o=Math.ceil(window.innerWidth/i)+1,l=Math.ceil(window.innerHeight/n)+1;for(let a=0;a<l;a++)for(let h=0;h<o;h++){const d=document.createElement("span");d.className="fl-watermark-tile",d.textContent=s,d.style.left=`${h*i}px`,d.style.top=`${a*n}px`,this.container.appendChild(d)}}}class U{constructor(e){this.selectors=e,this.observer=null,this.maskedElements=new Set}start(){this.scanAndMark(),this.observer=new MutationObserver(()=>this.scanAndMark()),this.observer.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["type","class","data-sensitive","data-mask"]})}stop(){var e;(e=this.observer)==null||e.disconnect(),this.observer=null;for(const t of this.maskedElements)t.removeAttribute("data-fl-masked");this.maskedElements.clear()}isMasked(e){return e.hasAttribute("data-fl-masked")}getCombinedSelector(){return this.selectors.join(", ")}scanAndMark(){const e=this.getCombinedSelector();if(e)try{const t=document.querySelectorAll(e);for(const s of t)this.maskedElements.has(s)||(s.setAttribute("data-fl-masked","true"),this.maskedElements.add(s));for(const s of this.maskedElements)document.contains(s)||this.maskedElements.delete(s)}catch{}}}const j="firstlook_uat",$=1,u={sessions:"sessions",recordings:"recordings",annotations:"annotations",uploadQueue:"upload_queue"};class K{constructor(){this.db=null}async open(){if(!this.db)return new Promise((e,t)=>{const s=indexedDB.open(j,$);s.onupgradeneeded=()=>{const i=s.result;i.objectStoreNames.contains(u.sessions)||i.createObjectStore(u.sessions,{keyPath:"sessionId"}),i.objectStoreNames.contains(u.recordings)||i.createObjectStore(u.recordings,{autoIncrement:!0}).createIndex("sessionId","sessionId",{unique:!1}),i.objectStoreNames.contains(u.annotations)||i.createObjectStore(u.annotations,{autoIncrement:!0}).createIndex("sessionId","sessionId",{unique:!1}),i.objectStoreNames.contains(u.uploadQueue)||i.createObjectStore(u.uploadQueue,{autoIncrement:!0})},s.onsuccess=()=>{this.db=s.result,e()},s.onerror=()=>t(s.error)})}async saveSession(e){await this.put(u.sessions,e)}async getSession(e){return this.get(u.sessions,e)}async saveRecording(e,t){await this.put(u.recordings,{sessionId:e,...t})}async saveAnnotation(e,t){await this.put(u.annotations,{sessionId:e,...t})}async getAnnotations(e){return this.getAllByIndex(u.annotations,"sessionId",e)}async enqueueUpload(e){await this.put(u.uploadQueue,{payload:e,createdAt:Date.now(),attempts:0})}async getPendingUploads(){const e=this.ensureDb();return new Promise((t,s)=>{const o=e.transaction(u.uploadQueue,"readonly").objectStore(u.uploadQueue).openCursor(),l=[];o.onsuccess=()=>{const a=o.result;a?(l.push({key:a.key,payload:a.value.payload,attempts:a.value.attempts,lastAttemptAt:a.value.lastAttemptAt}),a.continue()):t(l)},o.onerror=()=>s(o.error)})}async removeFromQueue(e){const t=this.ensureDb();return new Promise((s,i)=>{const o=t.transaction(u.uploadQueue,"readwrite").objectStore(u.uploadQueue).delete(e);o.onsuccess=()=>s(),o.onerror=()=>i(o.error)})}async incrementAttempts(e){const t=this.ensureDb();return new Promise((s,i)=>{const n=t.transaction(u.uploadQueue,"readwrite"),o=n.objectStore(u.uploadQueue),l=o.get(e);l.onsuccess=()=>{const a=l.result;a&&(a.attempts=(a.attempts??0)+1,a.lastAttemptAt=Date.now(),o.put(a,e))},n.oncomplete=()=>s(),n.onerror=()=>i(n.error)})}async clearSession(e){const s=this.ensureDb().transaction([u.sessions,u.recordings,u.annotations],"readwrite");s.objectStore(u.sessions).delete(e);for(const i of[u.recordings,u.annotations]){const l=s.objectStore(i).index("sessionId").openCursor(IDBKeyRange.only(e));l.onsuccess=()=>{const a=l.result;a&&(a.delete(),a.continue())}}return new Promise((i,n)=>{s.oncomplete=()=>i(),s.onerror=()=>n(s.error)})}close(){var e;(e=this.db)==null||e.close(),this.db=null}ensureDb(){if(!this.db)throw new Error("[FirstLook] Storage not opened. Call open() first.");return this.db}async put(e,t){const s=this.ensureDb();return new Promise((i,n)=>{const o=s.transaction(e,"readwrite");o.objectStore(e).put(t),o.oncomplete=()=>i(),o.onerror=()=>n(o.error)})}async get(e,t){const s=this.ensureDb();return new Promise((i,n)=>{const l=s.transaction(e,"readonly").objectStore(e).get(t);l.onsuccess=()=>i(l.result),l.onerror=()=>n(l.error)})}async getAllByIndex(e,t,s){const i=this.ensureDb();return new Promise((n,o)=>{const h=i.transaction(e,"readonly").objectStore(e).index(t).getAll(s);h.onsuccess=()=>n(h.result),h.onerror=()=>o(h.error)})}}const I=["#e17055","#6c5ce7","#00b894","#fdcb6e","#ffffff"];class H{constructor(e){this.shadowRoot=e,this.overlay=null,this.canvas=null,this.ctx=null,this.drawing=!1,this.currentPath=[],this.paths=[],this.selectedColor=I[0],this.screenshotDataUrl=""}async open(){return this.screenshotDataUrl=await this.captureScreenshot(),new Promise(e=>{this.overlay=r("div",{className:"fl-annotation-overlay"});const t=r("div",{className:"fl-annotation-canvas-wrap"});this.canvas=document.createElement("canvas"),this.canvas.className="fl-annotation-canvas",t.appendChild(this.canvas),this.overlay.appendChild(t);const s=r("input",{className:"fl-annotation-comment",type:"text",placeholder:"Add a comment..."}),i=r("div",{className:"fl-color-picker"});for(const a of I){const h=r("div",{className:`fl-color-swatch ${a===this.selectedColor?"fl-selected":""}`});h.style.background=a,h.onclick=()=>{this.selectedColor=a,i.querySelectorAll(".fl-color-swatch").forEach(d=>d.classList.remove("fl-selected")),h.classList.add("fl-selected")},i.appendChild(h)}const n=r("button",{className:"fl-btn fl-btn-ok"},["Submit"]);n.onclick=()=>{const a={screenshotDataUrl:this.getAnnotatedImage(),drawings:[...this.paths],comment:s.value,timestamp:Date.now()};this.close(),e(a)};const o=r("button",{className:"fl-btn fl-btn-ng"},["Cancel"]);o.onclick=()=>{this.close(),e(null)};const l=r("div",{className:"fl-annotation-toolbar"},[i,s,n,o]);this.overlay.appendChild(l),this.shadowRoot.appendChild(this.overlay),this.initCanvas()})}close(){var e;(e=this.overlay)==null||e.remove(),this.overlay=null,this.canvas=null,this.ctx=null,this.paths=[],this.currentPath=[]}async captureScreenshot(){try{const e=window.innerWidth,t=window.innerHeight,s=document.documentElement.cloneNode(!0),i=s.querySelector("#firstlook-sdk-root");i==null||i.remove();const n=s.querySelectorAll('[data-fl-masked], input[type="password"]');for(const p of n)(p instanceof HTMLInputElement||p instanceof HTMLTextAreaElement)&&(p.value="[MASKED]"),p.textContent="[MASKED]";for(const p of s.querySelectorAll("script"))p.remove();const o=s.querySelector("body");if(o){const p=window.getComputedStyle(document.body);o.style.backgroundColor=p.backgroundColor,o.style.color=p.color,o.style.fontFamily=p.fontFamily,o.style.margin="0",o.style.overflow="hidden"}const l=new XMLSerializer().serializeToString(s),a=`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${t}">
4
4
  <foreignObject width="100%" height="100%">
5
5
  ${l}
6
6
  </foreignObject>
7
- </svg>`,h=new Blob([c],{type:"image/svg+xml;charset=utf-8"}),d=URL.createObjectURL(h),m=document.createElement("canvas");m.width=t*window.devicePixelRatio,m.height=e*window.devicePixelRatio;const f=m.getContext("2d");return f.scale(window.devicePixelRatio,window.devicePixelRatio),new Promise(p=>{const g=new Image;g.onload=()=>{f.drawImage(g,0,0,t,e),URL.revokeObjectURL(d),p(m.toDataURL("image/png"))},g.onerror=()=>{URL.revokeObjectURL(d),p(this.captureScreenshotFallback(t,e))},g.src=d})}catch{return this.captureScreenshotFallback(window.innerWidth,window.innerHeight)}}captureScreenshotFallback(t,e){const s=document.createElement("canvas");s.width=t,s.height=e;const i=s.getContext("2d"),n=window.getComputedStyle(document.body);return i.fillStyle=n.backgroundColor||"#ffffff",i.fillRect(0,0,t,e),i.fillStyle="#333",i.font="14px sans-serif",i.fillText(`URL: ${window.location.href}`,20,30),i.fillText(`Time: ${new Date().toISOString()}`,20,50),i.fillText("(SVG capture unavailable — metadata view)",20,70),s.toDataURL("image/png")}initCanvas(){if(!this.canvas)return;const t=new Image;t.onload=()=>{const e=window.innerWidth*.9,s=window.innerHeight*.7,i=Math.min(e/t.width,s/t.height,1);this.canvas.width=t.width*i,this.canvas.height=t.height*i,this.ctx=this.canvas.getContext("2d"),this.ctx.drawImage(t,0,0,this.canvas.width,this.canvas.height),this.canvas.addEventListener("pointerdown",this.onDrawStart.bind(this)),this.canvas.addEventListener("pointermove",this.onDrawMove.bind(this)),this.canvas.addEventListener("pointerup",this.onDrawEnd.bind(this)),this.canvas.addEventListener("pointerleave",this.onDrawEnd.bind(this))},t.src=this.screenshotDataUrl}onDrawStart(t){this.drawing=!0;const e=this.canvas.getBoundingClientRect();this.currentPath=[{x:t.clientX-e.left,y:t.clientY-e.top}]}onDrawMove(t){if(!this.drawing||!this.ctx)return;const e=this.canvas.getBoundingClientRect(),s={x:t.clientX-e.left,y:t.clientY-e.top};if(this.currentPath.push(s),this.ctx.strokeStyle=this.selectedColor,this.ctx.lineWidth=3,this.ctx.lineCap="round",this.ctx.lineJoin="round",this.currentPath.length>=2){const i=this.currentPath[this.currentPath.length-2];this.ctx.beginPath(),this.ctx.moveTo(i.x,i.y),this.ctx.lineTo(s.x,s.y),this.ctx.stroke()}}onDrawEnd(){this.drawing&&this.currentPath.length>0&&this.paths.push({points:[...this.currentPath],color:this.selectedColor,width:3}),this.drawing=!1,this.currentPath=[]}getAnnotatedImage(){var t;return((t=this.canvas)==null?void 0:t.toDataURL("image/png"))||this.screenshotDataUrl}}class K{constructor(t,e){this.shadowRoot=t,this.events=e,this.annotations=[],this.active=!1,this.onKeydown=s=>{s.ctrlKey&&s.shiftKey&&s.key==="R"&&(s.preventDefault(),this.trigger())},this.annotationCanvas=new U(t)}start(){document.addEventListener("keydown",this.onKeydown)}stop(){document.removeEventListener("keydown",this.onKeydown)}getAnnotations(){return[...this.annotations]}async trigger(){if(!this.active){this.active=!0;try{const t=await this.annotationCanvas.open();t&&(this.annotations.push(t),this.events.emit({type:"report:submitted"}))}finally{this.active=!1}}}}class H{constructor(t,e){this.requiredTaps=t,this.onActivate=e,this.tapCount=0,this.tapTimer=null,this.handler=null}start(){this.handler=this.onTap.bind(this),document.addEventListener("pointerdown",this.handler,{passive:!0})}stop(){this.handler&&(document.removeEventListener("pointerdown",this.handler),this.handler=null),this.reset()}onTap(t){this.tapCount++,this.tapTimer&&clearTimeout(this.tapTimer),this.tapCount>=this.requiredTaps?(this.reset(),this.onActivate()):this.tapTimer=setTimeout(()=>this.reset(),800)}reset(){this.tapCount=0,this.tapTimer&&(clearTimeout(this.tapTimer),this.tapTimer=null)}}const S="firstlook:uat-active";class q{constructor(t){this.onActivate=t,this.handlers=[]}start(){if(this.checkUrl()){this.persist(),this.onActivate();return}if(this.hasPersisted()){this.onActivate();return}const t=()=>{this.checkUrl()&&(this.persist(),this.onActivate())};window.addEventListener("hashchange",t),this.handlers.push(["hashchange",t]),window.addEventListener("popstate",t),this.handlers.push(["popstate",t])}stop(){for(const[t,e]of this.handlers)window.removeEventListener(t,e);this.handlers=[]}static clearPersisted(){try{sessionStorage.removeItem(S)}catch{}}checkUrl(){const t=new URLSearchParams(window.location.search);if(t.has("firstlook")||t.has("uat-mode")||t.get("uat")==="1")return!0;const e=window.location.hash,s=e.indexOf("?");if(s!==-1){const i=new URLSearchParams(e.substring(s));if(i.has("firstlook")||i.has("uat-mode")||i.get("uat")==="1")return!0}return!1}persist(){try{sessionStorage.setItem(S,"1")}catch{}}hasPersisted(){try{return sessionStorage.getItem(S)==="1"}catch{return!1}}}class _{constructor(t){this.onActivate=t,this.handler=null}start(){this.handler=t=>{t.code==="KeyU"&&t.shiftKey&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.onActivate())},window.addEventListener("keydown",this.handler)}stop(){this.handler&&(window.removeEventListener("keydown",this.handler),this.handler=null)}}class W{constructor(t,e){this.shadowRoot=t,this.callbacks=e,this.root=null,this.outsideClickHandler=null}show(){if(this.root)return;this.root=r("div",{className:"fl-debug-menu"}),this.root.style.pointerEvents="auto";for(const e of["click","mousedown","mouseup","pointerdown","pointerup","touchstart","touchend"])this.root.addEventListener(e,s=>s.stopPropagation());const t=[{icon:"▶",label:"Start Session",action:this.callbacks.onStartSession},{icon:"📸",label:"Report Issue",action:this.callbacks.onReportIssue},{icon:"✖",label:"Close SDK",action:this.callbacks.onClose}];for(const e of t){const s=r("button",{className:"fl-debug-menu-item"},[r("span",{className:"fl-debug-menu-item-icon"},[e.icon]),e.label]);s.onclick=i=>{i.stopPropagation(),this.hide(),e.action()},this.root.appendChild(s)}this.shadowRoot.appendChild(this.root),setTimeout(()=>{this.outsideClickHandler=e=>{this.root&&!this.root.contains(e.target)&&this.hide()},document.addEventListener("click",this.outsideClickHandler,{capture:!0})},100)}hide(){var t;(t=this.root)==null||t.remove(),this.root=null,this.outsideClickHandler&&(document.removeEventListener("click",this.outsideClickHandler,{capture:!0}),this.outsideClickHandler=null)}get visible(){return this.root!==null}}const V=`
7
+ </svg>`,h=new Blob([a],{type:"image/svg+xml;charset=utf-8"}),d=URL.createObjectURL(h),m=document.createElement("canvas");m.width=e*window.devicePixelRatio,m.height=t*window.devicePixelRatio;const f=m.getContext("2d");return f.scale(window.devicePixelRatio,window.devicePixelRatio),new Promise(p=>{const g=new Image;g.onload=()=>{f.drawImage(g,0,0,e,t),URL.revokeObjectURL(d),p(m.toDataURL("image/png"))},g.onerror=()=>{URL.revokeObjectURL(d),p(this.captureScreenshotFallback(e,t))},g.src=d})}catch{return this.captureScreenshotFallback(window.innerWidth,window.innerHeight)}}captureScreenshotFallback(e,t){const s=document.createElement("canvas");s.width=e,s.height=t;const i=s.getContext("2d"),n=window.getComputedStyle(document.body);return i.fillStyle=n.backgroundColor||"#ffffff",i.fillRect(0,0,e,t),i.fillStyle="#333",i.font="14px sans-serif",i.fillText(`URL: ${window.location.href}`,20,30),i.fillText(`Time: ${new Date().toISOString()}`,20,50),i.fillText("(SVG capture unavailable — metadata view)",20,70),s.toDataURL("image/png")}initCanvas(){if(!this.canvas)return;const e=new Image;e.onload=()=>{const t=window.innerWidth*.9,s=window.innerHeight*.7,i=Math.min(t/e.width,s/e.height,1);this.canvas.width=e.width*i,this.canvas.height=e.height*i,this.ctx=this.canvas.getContext("2d"),this.ctx.drawImage(e,0,0,this.canvas.width,this.canvas.height),this.canvas.addEventListener("pointerdown",this.onDrawStart.bind(this)),this.canvas.addEventListener("pointermove",this.onDrawMove.bind(this)),this.canvas.addEventListener("pointerup",this.onDrawEnd.bind(this)),this.canvas.addEventListener("pointerleave",this.onDrawEnd.bind(this))},e.src=this.screenshotDataUrl}onDrawStart(e){this.drawing=!0;const t=this.canvas.getBoundingClientRect();this.currentPath=[{x:e.clientX-t.left,y:e.clientY-t.top}]}onDrawMove(e){if(!this.drawing||!this.ctx)return;const t=this.canvas.getBoundingClientRect(),s={x:e.clientX-t.left,y:e.clientY-t.top};if(this.currentPath.push(s),this.ctx.strokeStyle=this.selectedColor,this.ctx.lineWidth=3,this.ctx.lineCap="round",this.ctx.lineJoin="round",this.currentPath.length>=2){const i=this.currentPath[this.currentPath.length-2];this.ctx.beginPath(),this.ctx.moveTo(i.x,i.y),this.ctx.lineTo(s.x,s.y),this.ctx.stroke()}}onDrawEnd(){this.drawing&&this.currentPath.length>0&&this.paths.push({points:[...this.currentPath],color:this.selectedColor,width:3}),this.drawing=!1,this.currentPath=[]}getAnnotatedImage(){var e;return((e=this.canvas)==null?void 0:e.toDataURL("image/png"))||this.screenshotDataUrl}}class _{constructor(e,t){this.shadowRoot=e,this.events=t,this.annotations=[],this.active=!1,this.onKeydown=s=>{s.ctrlKey&&s.shiftKey&&s.key==="R"&&(s.preventDefault(),this.trigger())},this.annotationCanvas=new H(e)}start(){document.addEventListener("keydown",this.onKeydown)}stop(){document.removeEventListener("keydown",this.onKeydown)}getAnnotations(){return[...this.annotations]}async trigger(){if(!this.active){this.active=!0;try{const e=await this.annotationCanvas.open();e&&(this.annotations.push(e),this.events.emit({type:"report:submitted"}))}finally{this.active=!1}}}}class W{constructor(e,t){this.requiredTaps=e,this.onActivate=t,this.tapCount=0,this.tapTimer=null,this.handler=null}start(){this.handler=this.onTap.bind(this),document.addEventListener("pointerdown",this.handler,{passive:!0})}stop(){this.handler&&(document.removeEventListener("pointerdown",this.handler),this.handler=null),this.reset()}onTap(e){this.tapCount++,this.tapTimer&&clearTimeout(this.tapTimer),this.tapCount>=this.requiredTaps?(this.reset(),this.onActivate()):this.tapTimer=setTimeout(()=>this.reset(),800)}reset(){this.tapCount=0,this.tapTimer&&(clearTimeout(this.tapTimer),this.tapTimer=null)}}const T="firstlook:uat-active";class M{constructor(e){this.onActivate=e,this.handlers=[]}start(){if(this.checkUrl()){this.persist(),this.onActivate();return}if(this.hasPersisted()){this.onActivate();return}const e=()=>{this.checkUrl()&&(this.persist(),this.onActivate())};window.addEventListener("hashchange",e),this.handlers.push(["hashchange",e]),window.addEventListener("popstate",e),this.handlers.push(["popstate",e])}stop(){for(const[e,t]of this.handlers)window.removeEventListener(e,t);this.handlers=[]}static clearPersisted(){try{sessionStorage.removeItem(T)}catch{}}checkUrl(){const e=new URLSearchParams(window.location.search);if(e.has("firstlook")||e.has("uat-mode")||e.get("uat")==="1")return!0;const t=window.location.hash,s=t.indexOf("?");if(s!==-1){const i=new URLSearchParams(t.substring(s));if(i.has("firstlook")||i.has("uat-mode")||i.get("uat")==="1")return!0}return!1}persist(){try{sessionStorage.setItem(T,"1")}catch{}}hasPersisted(){try{return sessionStorage.getItem(T)==="1"}catch{return!1}}}const y=class y{constructor(e){this.onActivate=e,this.handler=null,this.sequence=[],this.sequenceTimer=null}start(){this.handler=e=>{if((e.key==="u"||e.key==="U")&&e.shiftKey&&(e.ctrlKey||e.metaKey)){e.preventDefault(),this.onActivate();return}if(e.key.startsWith("Arrow")){this.sequence.push(e.key),this.sequenceTimer&&clearTimeout(this.sequenceTimer),this.sequenceTimer=setTimeout(()=>{this.sequence=[]},3e3);const t=y.KONAMI;this.sequence.length>t.length&&(this.sequence=this.sequence.slice(-t.length)),this.sequence.length===t.length&&this.sequence.every((s,i)=>s===t[i])&&(this.sequence=[],this.onActivate())}},window.addEventListener("keydown",this.handler)}stop(){this.handler&&(window.removeEventListener("keydown",this.handler),this.handler=null),this.sequenceTimer&&(clearTimeout(this.sequenceTimer),this.sequenceTimer=null)}};y.KONAMI=["ArrowUp","ArrowUp","ArrowDown","ArrowDown","ArrowLeft","ArrowRight","ArrowLeft","ArrowRight"];let q=y;class V{constructor(e,t){this.shadowRoot=e,this.callbacks=t,this.root=null,this.outsideClickHandler=null}show(){if(this.root)return;this.root=r("div",{className:"fl-debug-menu"}),this.root.style.pointerEvents="auto";for(const t of["click","mousedown","mouseup","pointerdown","pointerup","touchstart","touchend"])this.root.addEventListener(t,s=>s.stopPropagation());const e=[{icon:"▶",label:"Start Session",action:this.callbacks.onStartSession},{icon:"📸",label:"Report Issue",action:this.callbacks.onReportIssue},{icon:"✖",label:"Close SDK",action:this.callbacks.onClose}];for(const t of e){const s=r("button",{className:"fl-debug-menu-item"},[r("span",{className:"fl-debug-menu-item-icon"},[t.icon]),t.label]);s.onclick=i=>{i.stopPropagation(),this.hide(),t.action()},this.root.appendChild(s)}this.shadowRoot.appendChild(this.root),setTimeout(()=>{this.outsideClickHandler=t=>{this.root&&!this.root.contains(t.target)&&this.hide()},document.addEventListener("click",this.outsideClickHandler,{capture:!0})},100)}hide(){var e;(e=this.root)==null||e.remove(),this.root=null,this.outsideClickHandler&&(document.removeEventListener("click",this.outsideClickHandler,{capture:!0}),this.outsideClickHandler=null)}get visible(){return this.root!==null}}const X=`
8
8
  :host {
9
9
  all: initial;
10
10
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
@@ -502,5 +502,5 @@
502
502
  animation: fl-fade-in 0.15s ease-out;
503
503
  }
504
504
  .fl-feedback-modal-actions { padding: 0 16px 16px; }
505
- `,X=5;class Y{constructor(){this.config=null,this.events=new M,this.state="idle",this.hostElement=null,this.shadowRoot=null,this.questManager=null,this.questOverlay=null,this.sessionRecorder=null,this.voiceRecorder=null,this.watermark=null,this.fieldMasker=null,this.storage=null,this.issueReporter=null,this.debugMenu=null,this.tapTrigger=null,this.deepLinkTrigger=null,this.keyboardTrigger=null,this.sessionId=null,this.sessionStartTime=0,this.maxDurationTimer=null,this.backupTimer=null,this.isFlushing=!1}async init(t){if(this.state!=="idle"){console.warn("[FirstLook] SDK already initialized.");return}this.config=I(t),this.storage=new $,await this.storage.open(),this.createShadowHost(),this.setupTriggers(),this.fieldMasker=new Q(this.config.security.maskSelectors),this.state="initialized",this.events.emit({type:"sdk:initialized"}),this.flushUploadQueue(!0)}activate(){if(this.state!=="initialized"){console.warn("[FirstLook] Cannot activate: SDK not initialized or already active.");return}this.onActivate()}async startSession(t){var s;if(this.state!=="active")throw new Error("[FirstLook] Cannot start session: SDK not active. Call activate() first.");(s=this.debugMenu)==null||s.hide(),this.sessionId=E(),this.sessionStartTime=Date.now(),this.questManager=new L(this.events),this.questManager.loadQuests(t),this.sessionRecorder=new P(this.config,this.events,this.fieldMasker.getCombinedSelector()),this.voiceRecorder=new z(this.events),this.issueReporter=new K(this.shadowRoot,this.events),this.issueReporter.start(),this.sessionRecorder.start(),this.fieldMasker.start(),this.config.recording.voice&&await this.voiceRecorder.start(),this.config.security.watermark&&(this.watermark=new O(this.shadowRoot,this.config),this.watermark.show()),this.questOverlay=new D(this.shadowRoot,{onOkWithFeedback:i=>this.handleQuestOk(i),onConcern:i=>this.handleConcern(i),onNg:()=>this.handleQuestNg(),onNgWithFeedback:i=>this.handleNgFeedbackSubmit(i),onMemo:i=>this.handleMemo(i),onFinish:()=>this.endSession(),onMinimize:()=>{}}),this.questManager.startSession(this.sessionStartTime),this.renderCurrentQuest();const e=this.config.recording.maxDuration;return e>0&&(this.maxDurationTimer=setTimeout(()=>this.endSession(),e*1e3)),this.backupTimer=setInterval(()=>this.backupSession(),3e4),this.state="recording",this.events.emit({type:"session:started",sessionId:this.sessionId}),this.sessionId}async startSessionFromRemote(t){if(this.state!=="active")throw new Error("[FirstLook] Cannot start session: SDK not active. Call activate() first.");if(!this.config)throw new Error("[FirstLook] SDK not initialized.");const e=this.config.endpoint.replace(/\/ingest-session$/,""),s=await fetch(`${e}/export-quests?id=${encodeURIComponent(t)}`,{headers:{"X-API-Key":this.config.apiKey}});if(!s.ok)throw new Error(`[FirstLook] Failed to fetch quest set: ${s.status}`);const n=(await s.json()).quests;return this.startSession(n)}async endSession(){var i,n,o,l,c,h,d,m,f,p,g;if(this.state!=="recording"||!this.sessionId)return null;this.maxDurationTimer&&(clearTimeout(this.maxDurationTimer),this.maxDurationTimer=null),this.backupTimer&&(clearInterval(this.backupTimer),this.backupTimer=null),(i=this.sessionRecorder)==null||i.stop(),await((n=this.voiceRecorder)==null?void 0:n.stopAsync()),(o=this.issueReporter)==null||o.stop(),(l=this.fieldMasker)==null||l.stop(),(c=this.watermark)==null||c.hide();const t=this.questManager.getResults();await Promise.allSettled(t.map(async b=>{if(b.voiceMemoBlob){try{b.voiceMemoBase64=await this.blobToBase64(b.voiceMemoBlob)}catch{}delete b.voiceMemoBlob}await Promise.allSettled(b.feedbacks.map(async k=>{if(k.voiceMemoBlob){try{k.voiceMemoBase64=await this.blobToBase64(k.voiceMemoBlob)}catch{}delete k.voiceMemoBlob}}))}));const e={sessionId:this.sessionId,projectId:this.config.projectId,userId:this.config.userId,role:this.config.role,deviceInfo:T(),startedAt:new Date(this.sessionStartTime).toISOString(),endedAt:new Date().toISOString(),duration:Math.floor((Date.now()-this.sessionStartTime)/1e3),quests:t,recordings:((h=this.sessionRecorder)==null?void 0:h.getSnapshots())??[]};await((d=this.storage)==null?void 0:d.saveSession(e));const s=((m=this.issueReporter)==null?void 0:m.getAnnotations())??[];for(const b of s)await((f=this.storage)==null?void 0:f.saveAnnotation(this.sessionId,b));return await((p=this.storage)==null?void 0:p.enqueueUpload({session:e,annotations:s})),(g=this.questOverlay)==null||g.destroy(),this.questOverlay=null,this.state="finished",this.events.emit({type:"session:ended",sessionId:this.sessionId}),this.flushUploadQueue(!1),e}on(t,e){return this.events.on(t,e)}getState(){return this.state}destroy(){var t,e,s,i,n,o,l,c,h,d,m,f;this.maxDurationTimer&&(clearTimeout(this.maxDurationTimer),this.maxDurationTimer=null),this.backupTimer&&(clearInterval(this.backupTimer),this.backupTimer=null),(t=this.tapTrigger)==null||t.stop(),(e=this.deepLinkTrigger)==null||e.stop(),(s=this.keyboardTrigger)==null||s.stop(),q.clearPersisted(),(i=this.sessionRecorder)==null||i.stop(),(n=this.voiceRecorder)==null||n.stopAsync().catch(()=>{}),(o=this.issueReporter)==null||o.stop(),(l=this.fieldMasker)==null||l.stop(),(c=this.watermark)==null||c.hide(),(h=this.questOverlay)==null||h.destroy(),(d=this.debugMenu)==null||d.hide(),(m=this.hostElement)==null||m.remove(),(f=this.storage)==null||f.close(),this.events.removeAll(),this.config=null,this.questManager=null,this.questOverlay=null,this.sessionRecorder=null,this.voiceRecorder=null,this.issueReporter=null,this.fieldMasker=null,this.debugMenu=null,this.tapTrigger=null,this.deepLinkTrigger=null,this.keyboardTrigger=null,this.storage=null,this.hostElement=null,this.shadowRoot=null,this.sessionId=null,this.state="idle"}createShadowHost(){var e;(e=document.getElementById("firstlook-sdk-root"))==null||e.remove(),this.hostElement=document.createElement("div"),this.hostElement.id="firstlook-sdk-root",this.hostElement.style.cssText="position:fixed;inset:0;z-index:2147483647;pointer-events:none;",document.body.appendChild(this.hostElement),this.shadowRoot=this.hostElement.attachShadow({mode:"closed"});const t=document.createElement("style");t.textContent=V,this.shadowRoot.appendChild(t)}setupTriggers(){const t=this.config.triggers;this.tapTrigger=new H(t.tapCount,()=>this.onActivate()),this.tapTrigger.start(),t.deepLink&&(this.deepLinkTrigger=new q(()=>this.onActivate()),this.deepLinkTrigger.start()),t.keyboard&&(this.keyboardTrigger=new _(()=>this.onActivate()),this.keyboardTrigger.start()),t.customCheck&&t.customCheck()&&this.onActivate()}onActivate(){var t,e,s;this.state==="initialized"&&(this.state="active",(t=this.tapTrigger)==null||t.stop(),(e=this.deepLinkTrigger)==null||e.stop(),(s=this.keyboardTrigger)==null||s.stop(),this.events.emit({type:"sdk:activated"}),this.debugMenu=new W(this.shadowRoot,{onStartSession:()=>{this.events.emit({type:"sdk:activated"})},onReportIssue:()=>{var i;(i=this.issueReporter)==null||i.trigger()},onClose:()=>{this.destroy()}}),this.debugMenu.show())}handleQuestOk(t){var i;if(!this.questManager||!this.sessionRecorder)return;const e=this.sessionRecorder.getActionLogs(),s=(i=this.questOverlay)==null?void 0:i.getChecklist();this.questManager.completeCurrentQuest(e,{comment:t||void 0,checklist:s}),this.renderCurrentQuest()}handleConcern(t){var i;if(!this.questManager||!this.sessionRecorder)return;const e=this.sessionRecorder.getActionLogs(),s=(i=this.questOverlay)==null?void 0:i.getChecklist();this.questManager.completeCurrentQuest(e,{comment:t,concern:!0,checklist:s}),this.renderCurrentQuest()}handleQuestNg(){if(!this.questManager||!this.questOverlay)return;const t=this.questManager.getCurrentQuest();t&&this.questOverlay.renderFeedbackModal(t.title,"ng")}handleNgFeedbackSubmit(t){var i;if(!this.questManager||!this.sessionRecorder)return;const e=this.sessionRecorder.getActionLogs(),s=(i=this.questOverlay)==null?void 0:i.getChecklist();this.questManager.failCurrentQuest(e,t||void 0,void 0,s),this.renderCurrentQuest()}handleMemo(t){if(!this.questManager||!t)return;const e={comment:t,timestamp:Date.now(),relativeTime:Date.now()-this.sessionStartTime};this.questManager.addFeedback(e)}renderCurrentQuest(){var s;if(!this.questManager||!this.questOverlay)return;const t=this.questManager.getStatus();if(t.isBlocked){const n=this.questManager.getResults().find(l=>l.status==="FAILED"),o=n?n.questId:"Unknown";this.questOverlay.renderBlocked(o);return}if(t.isFinished){this.questOverlay.renderSummary(t.completed,t.failed,t.total);return}const e=this.questManager.getCurrentQuest();e&&this.questOverlay.renderQuest(e,this.questManager.getQuestStatuses(),((s=this.voiceRecorder)==null?void 0:s.recording)??!1)}async flushUploadQueue(t){if(!(!this.storage||!this.config||this.isFlushing)){this.isFlushing=!0;try{if(t){const s=navigator.connection;if(s&&s.type&&s.type!=="wifi"&&s.effectiveType!=="4g")return}const e=await this.storage.getPendingUploads();for(const s of e){if(s.attempts>=X){await this.storage.removeFromQueue(s.key);continue}if(s.attempts>0){const i=Math.pow(2,s.attempts)*1e3,n=s.lastAttemptAt??0;if(Date.now()-n<i)continue}try{(await fetch(this.config.endpoint,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(s.payload)})).ok?await this.storage.removeFromQueue(s.key):await this.storage.incrementAttempts(s.key)}catch{await this.storage.incrementAttempts(s.key)}}}finally{this.isFlushing=!1}}}async backupSession(){var t;if(!(!this.storage||!this.sessionId||!this.config||!this.sessionRecorder))try{const e={sessionId:this.sessionId,projectId:this.config.projectId,userId:this.config.userId,role:this.config.role,deviceInfo:T(),startedAt:new Date(this.sessionStartTime).toISOString(),duration:Math.floor((Date.now()-this.sessionStartTime)/1e3),quests:((t=this.questManager)==null?void 0:t.getResults())??[],recordings:this.sessionRecorder.getSnapshots()};await this.storage.saveSession(e)}catch{}}blobToBase64(t){return new Promise((e,s)=>{const i=new FileReader;i.onloadend=()=>e(i.result),i.onerror=()=>s(i.error),i.readAsDataURL(t)})}}x.FirstLookSDK=Y,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})});
505
+ `,Y=5;class G{constructor(){this.config=null,this.events=new A,this.state="idle",this.hostElement=null,this.shadowRoot=null,this.questManager=null,this.questOverlay=null,this.sessionRecorder=null,this.voiceRecorder=null,this.watermark=null,this.fieldMasker=null,this.storage=null,this.issueReporter=null,this.debugMenu=null,this.tapTrigger=null,this.deepLinkTrigger=null,this.keyboardTrigger=null,this.sessionId=null,this.sessionStartTime=0,this.maxDurationTimer=null,this.backupTimer=null,this.isFlushing=!1}async init(e){if(this.state!=="idle"){console.warn("[FirstLook] SDK already initialized.");return}this.config=L(e),this.storage=new K,await this.storage.open(),this.createShadowHost(),this.setupTriggers(),this.fieldMasker=new U(this.config.security.maskSelectors),this.state="initialized",this.events.emit({type:"sdk:initialized"}),this.flushUploadQueue(!0)}activate(){if(this.state!=="initialized"){console.warn("[FirstLook] Cannot activate: SDK not initialized or already active.");return}this.onActivate()}async startSession(e){var s;if(this.state!=="active")throw new Error("[FirstLook] Cannot start session: SDK not active. Call activate() first.");(s=this.debugMenu)==null||s.hide(),this.sessionId=E(),this.sessionStartTime=Date.now(),this.questManager=new R(this.events),this.questManager.loadQuests(e),this.sessionRecorder=new z(this.config,this.events,this.fieldMasker.getCombinedSelector()),this.voiceRecorder=new Q(this.events),this.issueReporter=new _(this.shadowRoot,this.events),this.issueReporter.start(),this.sessionRecorder.start(),this.fieldMasker.start(),this.config.recording.voice&&await this.voiceRecorder.start(),this.config.security.watermark&&(this.watermark=new B(this.shadowRoot,this.config),this.watermark.show()),this.questOverlay=new F(this.shadowRoot,{onOkWithFeedback:i=>this.handleQuestOk(i),onConcern:i=>this.handleConcern(i),onNg:()=>this.handleQuestNg(),onNgWithFeedback:i=>this.handleNgFeedbackSubmit(i),onMemo:i=>this.handleMemo(i),onFinish:()=>this.endSession(),onMinimize:()=>{}}),this.questManager.startSession(this.sessionStartTime),this.renderCurrentQuest();const t=this.config.recording.maxDuration;return t>0&&(this.maxDurationTimer=setTimeout(()=>this.endSession(),t*1e3)),this.backupTimer=setInterval(()=>this.backupSession(),3e4),this.state="recording",this.events.emit({type:"session:started",sessionId:this.sessionId}),this.sessionId}async startSessionFromRemote(e){if(this.state!=="active")throw new Error("[FirstLook] Cannot start session: SDK not active. Call activate() first.");if(!this.config)throw new Error("[FirstLook] SDK not initialized.");const t=this.config.endpoint.replace(/\/ingest-session$/,""),s=await fetch(`${t}/export-quests?id=${encodeURIComponent(e)}`,{headers:{"X-API-Key":this.config.apiKey}});if(!s.ok)throw new Error(`[FirstLook] Failed to fetch quest set: ${s.status}`);const n=(await s.json()).quests;return this.startSession(n)}async endSession(){var i,n,o,l,a,h,d,m,f,p,g;if(this.state!=="recording"||!this.sessionId)return null;this.maxDurationTimer&&(clearTimeout(this.maxDurationTimer),this.maxDurationTimer=null),this.backupTimer&&(clearInterval(this.backupTimer),this.backupTimer=null),(i=this.sessionRecorder)==null||i.stop(),await((n=this.voiceRecorder)==null?void 0:n.stopAsync()),(o=this.issueReporter)==null||o.stop(),(l=this.fieldMasker)==null||l.stop(),(a=this.watermark)==null||a.hide();const e=this.questManager.getResults();await Promise.allSettled(e.map(async b=>{if(b.voiceMemoBlob){try{b.voiceMemoBase64=await this.blobToBase64(b.voiceMemoBlob)}catch{}delete b.voiceMemoBlob}await Promise.allSettled(b.feedbacks.map(async k=>{if(k.voiceMemoBlob){try{k.voiceMemoBase64=await this.blobToBase64(k.voiceMemoBlob)}catch{}delete k.voiceMemoBlob}}))}));const t={sessionId:this.sessionId,projectId:this.config.projectId,userId:this.config.userId,role:this.config.role,deviceInfo:C(),startedAt:new Date(this.sessionStartTime).toISOString(),endedAt:new Date().toISOString(),duration:Math.floor((Date.now()-this.sessionStartTime)/1e3),quests:e,recordings:((h=this.sessionRecorder)==null?void 0:h.getSnapshots())??[]};await((d=this.storage)==null?void 0:d.saveSession(t));const s=((m=this.issueReporter)==null?void 0:m.getAnnotations())??[];for(const b of s)await((f=this.storage)==null?void 0:f.saveAnnotation(this.sessionId,b));return await((p=this.storage)==null?void 0:p.enqueueUpload({session:t,annotations:s})),(g=this.questOverlay)==null||g.destroy(),this.questOverlay=null,this.state="finished",this.events.emit({type:"session:ended",sessionId:this.sessionId}),this.flushUploadQueue(!1),t}on(e,t){return this.events.on(e,t)}getState(){return this.state}destroy(){var e,t,s,i,n,o,l,a,h,d,m,f;this.maxDurationTimer&&(clearTimeout(this.maxDurationTimer),this.maxDurationTimer=null),this.backupTimer&&(clearInterval(this.backupTimer),this.backupTimer=null),(e=this.tapTrigger)==null||e.stop(),(t=this.deepLinkTrigger)==null||t.stop(),(s=this.keyboardTrigger)==null||s.stop(),M.clearPersisted(),(i=this.sessionRecorder)==null||i.stop(),(n=this.voiceRecorder)==null||n.stopAsync().catch(()=>{}),(o=this.issueReporter)==null||o.stop(),(l=this.fieldMasker)==null||l.stop(),(a=this.watermark)==null||a.hide(),(h=this.questOverlay)==null||h.destroy(),(d=this.debugMenu)==null||d.hide(),(m=this.hostElement)==null||m.remove(),(f=this.storage)==null||f.close(),this.events.removeAll(),this.config=null,this.questManager=null,this.questOverlay=null,this.sessionRecorder=null,this.voiceRecorder=null,this.issueReporter=null,this.fieldMasker=null,this.debugMenu=null,this.tapTrigger=null,this.deepLinkTrigger=null,this.keyboardTrigger=null,this.storage=null,this.hostElement=null,this.shadowRoot=null,this.sessionId=null,this.state="idle"}createShadowHost(){var t;(t=document.getElementById("firstlook-sdk-root"))==null||t.remove(),this.hostElement=document.createElement("div"),this.hostElement.id="firstlook-sdk-root",this.hostElement.style.cssText="position:fixed;inset:0;z-index:2147483647;pointer-events:none;",document.body.appendChild(this.hostElement),this.shadowRoot=this.hostElement.attachShadow({mode:"closed"});const e=document.createElement("style");e.textContent=X,this.shadowRoot.appendChild(e)}setupTriggers(){const e=this.config.triggers;this.tapTrigger=new W(e.tapCount,()=>this.onActivate()),this.tapTrigger.start(),e.deepLink&&(this.deepLinkTrigger=new M(()=>this.onActivate()),this.deepLinkTrigger.start()),e.keyboard&&(this.keyboardTrigger=new q(()=>this.onActivate()),this.keyboardTrigger.start()),e.customCheck&&e.customCheck()&&this.onActivate()}onActivate(){var e,t,s;this.state==="initialized"&&(this.state="active",(e=this.tapTrigger)==null||e.stop(),(t=this.deepLinkTrigger)==null||t.stop(),(s=this.keyboardTrigger)==null||s.stop(),this.events.emit({type:"sdk:activated"}),this.debugMenu=new V(this.shadowRoot,{onStartSession:()=>{this.events.emit({type:"sdk:activated"})},onReportIssue:()=>{var i;(i=this.issueReporter)==null||i.trigger()},onClose:()=>{this.destroy()}}),this.debugMenu.show())}handleQuestOk(e){var i;if(!this.questManager||!this.sessionRecorder)return;const t=this.sessionRecorder.getActionLogs(),s=(i=this.questOverlay)==null?void 0:i.getChecklist();this.questManager.completeCurrentQuest(t,{comment:e||void 0,checklist:s}),this.renderCurrentQuest()}handleConcern(e){var i;if(!this.questManager||!this.sessionRecorder)return;const t=this.sessionRecorder.getActionLogs(),s=(i=this.questOverlay)==null?void 0:i.getChecklist();this.questManager.completeCurrentQuest(t,{comment:e,concern:!0,checklist:s}),this.renderCurrentQuest()}handleQuestNg(){if(!this.questManager||!this.questOverlay)return;const e=this.questManager.getCurrentQuest();e&&this.questOverlay.renderFeedbackModal(e.title,"ng")}handleNgFeedbackSubmit(e){var i;if(!this.questManager||!this.sessionRecorder)return;const t=this.sessionRecorder.getActionLogs(),s=(i=this.questOverlay)==null?void 0:i.getChecklist();this.questManager.failCurrentQuest(t,e||void 0,void 0,s),this.renderCurrentQuest()}handleMemo(e){if(!this.questManager||!e)return;const t={comment:e,timestamp:Date.now(),relativeTime:Date.now()-this.sessionStartTime};this.questManager.addFeedback(t)}renderCurrentQuest(){var s;if(!this.questManager||!this.questOverlay)return;const e=this.questManager.getStatus();if(e.isBlocked){const n=this.questManager.getResults().find(l=>l.status==="FAILED"),o=n?n.questId:"Unknown";this.questOverlay.renderBlocked(o);return}if(e.isFinished){this.questOverlay.renderSummary(e.completed,e.failed,e.total);return}const t=this.questManager.getCurrentQuest();t&&this.questOverlay.renderQuest(t,this.questManager.getQuestStatuses(),((s=this.voiceRecorder)==null?void 0:s.recording)??!1)}async flushUploadQueue(e){if(!(!this.storage||!this.config||this.isFlushing)){this.isFlushing=!0;try{if(e){const s=navigator.connection;if(s&&s.type&&s.type!=="wifi"&&s.effectiveType!=="4g")return}const t=await this.storage.getPendingUploads();for(const s of t){if(s.attempts>=Y){await this.storage.removeFromQueue(s.key);continue}if(s.attempts>0){const i=Math.pow(2,s.attempts)*1e3,n=s.lastAttemptAt??0;if(Date.now()-n<i)continue}try{(await fetch(this.config.endpoint,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(s.payload)})).ok?await this.storage.removeFromQueue(s.key):await this.storage.incrementAttempts(s.key)}catch{await this.storage.incrementAttempts(s.key)}}}finally{this.isFlushing=!1}}}async backupSession(){var e;if(!(!this.storage||!this.sessionId||!this.config||!this.sessionRecorder))try{const t={sessionId:this.sessionId,projectId:this.config.projectId,userId:this.config.userId,role:this.config.role,deviceInfo:C(),startedAt:new Date(this.sessionStartTime).toISOString(),duration:Math.floor((Date.now()-this.sessionStartTime)/1e3),quests:((e=this.questManager)==null?void 0:e.getResults())??[],recordings:this.sessionRecorder.getSnapshots()};await this.storage.saveSession(t)}catch{}}blobToBase64(e){return new Promise((t,s)=>{const i=new FileReader;i.onloadend=()=>t(i.result),i.onerror=()=>s(i.error),i.readAsDataURL(e)})}}x.FirstLookSDK=G,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})});
506
506
  //# sourceMappingURL=firstlook.umd.js.map