@firstlook-uat/sdk 0.2.0 → 0.3.1
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/firstlook.es.js +266 -157
- package/dist/firstlook.es.js.map +1 -1
- package/dist/firstlook.umd.js +27 -9
- package/dist/firstlook.umd.js.map +1 -1
- package/package.json +15 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firstlook.umd.js","sources":["../src/core/config.ts","../src/core/event-bus.ts","../src/quest/quest-manager.ts","../src/ui/dom-helpers.ts","../src/quest/quest-overlay.ts","../src/recording/session-recorder.ts","../src/recording/voice-recorder.ts","../src/security/watermark.ts","../src/security/masking.ts","../src/persistence/storage.ts","../src/reporting/annotation-canvas.ts","../src/triggers/shake-trigger.ts","../src/reporting/shake-reporter.ts","../src/triggers/tap-trigger.ts","../src/triggers/deep-link.ts","../src/core/debug-menu.ts","../src/ui/styles.ts","../src/core/sdk.ts"],"sourcesContent":["import type { FirstLookConfig, DeviceInfo } from \"@firstlook-uat/shared\";\n\n/** Resolved configuration with all defaults applied */\nexport interface ResolvedConfig {\n projectId: string;\n apiKey: string;\n userId: string;\n role?: string;\n context: Record<string, unknown>;\n endpoint: string;\n triggers: {\n tapCount: number;\n deepLink: boolean;\n shake: boolean;\n customCheck?: () => boolean;\n };\n security: {\n watermark: boolean;\n maskSelectors: string[];\n };\n recording: {\n domSnapshot: boolean;\n voice: boolean;\n maxDuration: number;\n snapshotInterval: number;\n };\n}\n\nconst DEFAULT_MASK_SELECTORS = [\n 'input[type=\"password\"]',\n \"[data-sensitive]\",\n \"[data-mask]\",\n \".uat-mask\",\n];\n\nexport function resolveConfig(raw: FirstLookConfig): ResolvedConfig {\n if (!raw.endpoint) {\n throw new Error(\"[FirstLook] 'endpoint' is required. Set your Supabase ingest-session URL.\");\n }\n\n return {\n projectId: raw.projectId,\n apiKey: raw.apiKey,\n userId: raw.userId,\n role: raw.role,\n context: raw.context ?? {},\n endpoint: raw.endpoint,\n triggers: {\n tapCount: raw.triggers?.tapCount ?? 5,\n deepLink: raw.triggers?.deepLink ?? true,\n shake: raw.triggers?.shake ?? true,\n customCheck: raw.triggers?.customCheck,\n },\n security: {\n watermark: raw.security?.watermark ?? true,\n maskSelectors: raw.security?.maskSelectors ?? DEFAULT_MASK_SELECTORS,\n },\n recording: {\n domSnapshot: raw.recording?.domSnapshot ?? true,\n voice: raw.recording?.voice ?? true,\n maxDuration: raw.recording?.maxDuration ?? 600,\n snapshotInterval: raw.recording?.snapshotInterval ?? 1000,\n },\n };\n}\n\nexport function collectDeviceInfo(): DeviceInfo {\n return {\n userAgent: navigator.userAgent,\n platform: navigator.platform,\n language: navigator.language,\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n pixelRatio: window.devicePixelRatio,\n touchSupport: \"ontouchstart\" in window || navigator.maxTouchPoints > 0,\n };\n}\n","import type { SDKEvent, SDKEventType, SDKEventListener } from \"@firstlook-uat/shared\";\n\n/**\n * Internal pub/sub event bus for SDK modules to communicate\n * without direct coupling.\n */\nexport class EventBus {\n private listeners = new Map<SDKEventType | \"*\", Set<SDKEventListener>>();\n\n on(type: SDKEventType | \"*\", listener: SDKEventListener): () => void {\n if (!this.listeners.has(type)) {\n this.listeners.set(type, new Set());\n }\n this.listeners.get(type)!.add(listener);\n return () => this.off(type, listener);\n }\n\n off(type: SDKEventType | \"*\", listener: SDKEventListener): void {\n this.listeners.get(type)?.delete(listener);\n }\n\n emit(event: SDKEvent): void {\n // Notify specific listeners\n this.listeners.get(event.type)?.forEach((fn) => {\n try {\n fn(event);\n } catch (e) {\n console.error(\"[FirstLook] Event listener error:\", e);\n }\n });\n // Notify wildcard listeners\n this.listeners.get(\"*\")?.forEach((fn) => {\n try {\n fn(event);\n } catch (e) {\n console.error(\"[FirstLook] Event listener error:\", e);\n }\n });\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n","import type { Quest, QuestResult, QuestStatus, ActionLog, Feedback } from \"@firstlook-uat/shared\";\nimport type { EventBus } from \"../core/event-bus.js\";\n\n/**\n * Manages the quest lifecycle: ordering, progression, status tracking.\n * Does not handle UI — delegates rendering to QuestOverlay.\n */\nexport class QuestManager {\n private quests: Quest[] = [];\n private results: QuestResult[] = [];\n private currentIndex = -1;\n private sessionStartTime = 0;\n private blocked = false;\n private pendingFeedbacks: Feedback[] = [];\n\n constructor(private events: EventBus) {}\n\n loadQuests(quests: Quest[]): void {\n this.quests = [...quests].sort((a, b) => a.order - b.order);\n this.results = [];\n this.currentIndex = -1;\n this.blocked = false;\n }\n\n startSession(startTime: number): void {\n this.sessionStartTime = startTime;\n this.advance();\n }\n\n getCurrentQuest(): Quest | null {\n if (this.blocked || this.currentIndex < 0 || this.currentIndex >= this.quests.length) {\n return null;\n }\n return this.quests[this.currentIndex];\n }\n\n getStatus(): {\n total: number;\n completed: number;\n failed: number;\n current: number;\n isBlocked: boolean;\n isFinished: boolean;\n } {\n return {\n total: this.quests.length,\n completed: this.results.filter((r) => r.status === \"COMPLETED\").length,\n failed: this.results.filter((r) => r.status === \"FAILED\").length,\n current: this.currentIndex,\n isBlocked: this.blocked,\n isFinished: this.currentIndex >= this.quests.length,\n };\n }\n\n getQuestStatuses(): QuestStatus[] {\n return this.quests.map((q, i) => {\n const result = this.results.find((r) => r.questId === q.id);\n if (result) return result.status;\n if (i === this.currentIndex && !this.blocked) return \"ACTIVE\";\n if (this.blocked && i >= this.currentIndex) return \"BLOCKED\";\n return \"PENDING\";\n });\n }\n\n completeCurrentQuest(logs: ActionLog[], voiceMemo?: Blob): QuestResult | null {\n const quest = this.getCurrentQuest();\n if (!quest) return null;\n\n const result: QuestResult = {\n questId: quest.id,\n status: \"COMPLETED\",\n timestamp: Date.now(),\n relativeTime: Date.now() - this.sessionStartTime,\n voiceMemoBlob: voiceMemo,\n logs,\n feedbacks: this.drainFeedbacks(),\n };\n\n this.results.push(result);\n this.events.emit({ type: \"quest:completed\", questId: quest.id });\n this.advance();\n return result;\n }\n\n failCurrentQuest(logs: ActionLog[], comment?: string, voiceMemo?: Blob): QuestResult | null {\n const quest = this.getCurrentQuest();\n if (!quest) return null;\n\n const result: QuestResult = {\n questId: quest.id,\n status: \"FAILED\",\n timestamp: Date.now(),\n relativeTime: Date.now() - this.sessionStartTime,\n voiceMemoBlob: voiceMemo,\n logs,\n comment,\n feedbacks: this.drainFeedbacks(),\n };\n\n this.results.push(result);\n this.events.emit({ type: \"quest:failed\", questId: quest.id });\n\n if (quest.blocking) {\n this.blocked = true;\n this.events.emit({ type: \"quest:blocked\", questId: quest.id });\n } else {\n this.advance();\n }\n\n return result;\n }\n\n addFeedback(feedback: Feedback): void {\n this.pendingFeedbacks.push(feedback);\n }\n\n getResults(): QuestResult[] {\n return [...this.results];\n }\n\n private drainFeedbacks(): Feedback[] {\n const fbs = this.pendingFeedbacks;\n this.pendingFeedbacks = [];\n return fbs;\n }\n\n private advance(): void {\n this.currentIndex++;\n if (this.currentIndex < this.quests.length) {\n this.events.emit({ type: \"quest:started\", questId: this.quests[this.currentIndex].id });\n }\n }\n}\n","/**\n * Minimal DOM helpers for building UI without a framework.\n */\n\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: Array<Node | string>,\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"className\") {\n node.className = value;\n } else {\n node.setAttribute(key, value);\n }\n }\n }\n if (children) {\n for (const child of children) {\n if (typeof child === \"string\") {\n node.appendChild(document.createTextNode(child));\n } else {\n node.appendChild(child);\n }\n }\n }\n return node;\n}\n\nexport function generateId(): string {\n return `fl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function formatTime(seconds: number): string {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${m.toString().padStart(2, \"0\")}:${s.toString().padStart(2, \"0\")}`;\n}\n","import type { Quest, QuestStatus } from \"@firstlook-uat/shared\";\nimport { el, formatTime } from \"../ui/dom-helpers.js\";\n\nexport interface QuestOverlayCallbacks {\n onOk: () => void;\n onNg: () => void;\n onNgWithFeedback: (comment: string) => void;\n onMemo: (comment: string) => void;\n onFinish: () => void;\n onMinimize: () => void;\n}\n\n/**\n * Quest overlay UI rendered inside Shadow DOM.\n * Displays the current quest card with OK/NG actions.\n */\nexport class QuestOverlay {\n private root: HTMLDivElement;\n private minimized = false;\n private timerInterval: ReturnType<typeof setInterval> | null = null;\n private startTime = 0;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private callbacks: QuestOverlayCallbacks,\n ) {\n this.root = document.createElement(\"div\");\n this.root.className = \"fl-quest-overlay\";\n this.shadowRoot.appendChild(this.root);\n }\n\n renderQuest(\n quest: Quest,\n statuses: QuestStatus[],\n recording: boolean,\n ): void {\n this.minimized = false;\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n\n // Header\n const currentIdx = statuses.indexOf(\"ACTIVE\");\n const header = el(\"div\", { className: \"fl-quest-header\" }, [\n el(\"div\", { className: \"fl-quest-header-left\" }, [\n el(\"span\", { className: \"fl-quest-badge\" }, [`Q${currentIdx + 1}/${statuses.length}`]),\n el(\"span\", { className: \"fl-quest-title\" }, [quest.title]),\n ]),\n this.createMinimizeBtn(),\n ]);\n this.root.appendChild(header);\n\n // Content wrapper\n const content = el(\"div\", { className: \"fl-quest-content\" });\n\n // Progress dots\n const progress = el(\"div\", { className: \"fl-quest-progress\" });\n for (const status of statuses) {\n const dotClass = status === \"COMPLETED\" ? \"fl-completed\"\n : status === \"FAILED\" ? \"fl-failed\"\n : status === \"ACTIVE\" ? \"fl-active\"\n : \"\";\n progress.appendChild(el(\"div\", { className: `fl-quest-progress-dot ${dotClass}` }));\n }\n\n // Body\n const body = el(\"div\", { className: \"fl-quest-body\" }, [\n progress,\n el(\"p\", { className: \"fl-quest-description\" }, [quest.description]),\n el(\"div\", { className: \"fl-quest-memo-row\" }, [\n this.createButton(\"fl-btn fl-btn-memo\", \"\\uD83D\\uDCDD \\u30E1\\u30E2\", () => this.renderFeedbackModal(quest.title)),\n ]),\n el(\"div\", { className: \"fl-quest-actions\" }, [\n this.createButton(\"fl-btn fl-btn-ok\", \"\\u2713 OK\", this.callbacks.onOk),\n this.createButton(\"fl-btn fl-btn-ng\", \"\\u2717 NG\", this.callbacks.onNg),\n ]),\n ]);\n content.appendChild(body);\n\n // Voice indicator\n if (recording) {\n content.appendChild(\n el(\"div\", { className: \"fl-voice-indicator\" }, [\n el(\"span\", { className: \"fl-voice-dot\" }),\n \"Recording...\",\n ]),\n );\n }\n\n // Status bar with timer\n const statusBar = el(\"div\", { className: \"fl-status-bar\" });\n if (recording) {\n statusBar.appendChild(\n el(\"span\", { className: \"fl-status-recording\" }, [\n el(\"span\", { className: \"fl-voice-dot\" }),\n \"REC\",\n ]),\n );\n }\n const timer = el(\"span\", { className: \"fl-status-timer\" }, [\"00:00\"]);\n statusBar.appendChild(timer);\n content.appendChild(statusBar);\n\n this.root.appendChild(content);\n\n // Start timer\n this.startTimer(timer);\n\n // Minimize handler on the overlay itself (when minimized)\n this.root.onclick = () => {\n if (this.minimized) {\n this.minimized = false;\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n this.root.appendChild(header);\n this.root.appendChild(content);\n this.startTimer(timer);\n }\n };\n }\n\n renderFeedbackModal(questTitle: string, variant: \"memo\" | \"ng\" = \"memo\"): void {\n const modal = el(\"div\", { className: \"fl-feedback-modal\" });\n\n const isNg = variant === \"ng\";\n const headerClass = isNg ? \"fl-quest-header fl-quest-header-ng\" : \"fl-quest-header\";\n const badgeText = isNg ? \"NG\" : \"\\uD83D\\uDCDD\";\n const badgeClass = isNg ? \"fl-quest-badge fl-badge-ng\" : \"fl-quest-badge\";\n\n const header = el(\"div\", { className: headerClass }, [\n el(\"div\", { className: \"fl-quest-header-left\" }, [\n el(\"span\", { className: badgeClass }, [badgeText]),\n el(\"span\", { className: \"fl-quest-title\" }, [questTitle]),\n ]),\n ]);\n modal.appendChild(header);\n\n const content = el(\"div\", { className: \"fl-quest-content\" });\n\n const textarea = document.createElement(\"textarea\");\n textarea.className = \"fl-feedback-textarea fl-feedback-textarea-modal\";\n textarea.placeholder = isNg\n ? \"\\u4F55\\u304C\\u3046\\u307E\\u304F\\u3044\\u304B\\u306A\\u304B\\u3063\\u305F\\u304B\\u6559\\u3048\\u3066\\u304F\\u3060\\u3055\\u3044...\"\n : \"\\u6C17\\u3065\\u3044\\u305F\\u3053\\u3068\\u3092\\u30E1\\u30E2...\\uD83D\\uDCDD\";\n textarea.rows = 3;\n content.appendChild(textarea);\n\n const actions = el(\"div\", { className: \"fl-quest-actions fl-feedback-modal-actions\" }, [\n this.createButton(\"fl-btn fl-btn-finish\", \"\\u9001\\u4FE1\", () => {\n const comment = textarea.value.trim();\n if (!comment && !isNg) {\n modal.remove();\n return;\n }\n if (isNg) {\n this.callbacks.onNgWithFeedback(comment);\n } else {\n this.callbacks.onMemo(comment);\n }\n modal.remove();\n }),\n this.createButton(\"fl-btn fl-btn-skip\", \"\\u30AD\\u30E3\\u30F3\\u30BB\\u30EB\", () => {\n if (isNg) {\n this.callbacks.onNgWithFeedback(\"\");\n }\n modal.remove();\n }),\n ]);\n content.appendChild(actions);\n\n modal.appendChild(content);\n this.root.appendChild(modal);\n\n textarea.focus();\n }\n\n renderSummary(\n completed: number,\n failed: number,\n total: number,\n ): void {\n this.stopTimer();\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n\n const summary = el(\"div\", { className: \"fl-summary\" }, [\n el(\"div\", { className: \"fl-summary-icon\" }, [completed === total ? \"\\uD83C\\uDF89\" : \"\\uD83D\\uDCCB\"]),\n el(\"div\", { className: \"fl-summary-title\" }, [\n completed === total ? \"All Quests Complete!\" : \"Session Complete\",\n ]),\n el(\"div\", { className: \"fl-summary-subtitle\" }, [\n `${completed + failed} of ${total} quests attempted`,\n ]),\n el(\"div\", { className: \"fl-summary-stats\" }, [\n this.createStat(completed.toString(), \"Passed\", \"fl-ok\"),\n this.createStat(failed.toString(), \"Failed\", \"fl-ng\"),\n ]),\n this.createButton(\"fl-btn fl-btn-finish\", \"Finish & Upload\", this.callbacks.onFinish),\n ]);\n\n this.root.appendChild(summary);\n }\n\n renderBlocked(questTitle: string): void {\n this.stopTimer();\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n\n const body = el(\"div\", { className: \"fl-summary\" }, [\n el(\"div\", { className: \"fl-summary-icon\" }, [\"\\uD83D\\uDED1\"]),\n el(\"div\", { className: \"fl-summary-title\" }, [\"Blocked\"]),\n el(\"div\", { className: \"fl-summary-subtitle\" }, [\n `Quest \"${questTitle}\" is blocking. Session halted.`,\n ]),\n this.createButton(\"fl-btn fl-btn-finish\", \"Finish & Upload\", this.callbacks.onFinish),\n ]);\n\n this.root.appendChild(body);\n }\n\n destroy(): void {\n this.stopTimer();\n this.root.remove();\n }\n\n private createMinimizeBtn(): HTMLButtonElement {\n const btn = el(\"button\", { className: \"fl-quest-minimize-btn\" }, [\"\\u2212\"]) as HTMLButtonElement;\n btn.onclick = (e) => {\n e.stopPropagation();\n this.minimized = true;\n this.root.className = \"fl-quest-overlay fl-minimized\";\n this.root.innerHTML = \"\";\n const icon = el(\"span\", { className: \"fl-minimize-icon\" }, [\"\\uD83D\\uDD0D\"]);\n this.root.appendChild(icon);\n this.callbacks.onMinimize();\n };\n return btn;\n }\n\n private createButton(className: string, text: string, onClick: () => void): HTMLButtonElement {\n const btn = el(\"button\", { className }) as HTMLButtonElement;\n btn.textContent = text;\n btn.onclick = (e) => {\n e.stopPropagation();\n onClick();\n };\n return btn;\n }\n\n private createStat(value: string, label: string, valueClass: string): HTMLDivElement {\n return el(\"div\", { className: \"fl-stat\" }, [\n el(\"div\", { className: `fl-stat-value ${valueClass}` }, [value]),\n el(\"div\", { className: \"fl-stat-label\" }, [label]),\n ]) as HTMLDivElement;\n }\n\n private startTimer(timerEl: HTMLElement): void {\n this.stopTimer();\n if (!this.startTime) this.startTime = Date.now();\n this.timerInterval = setInterval(() => {\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n timerEl.textContent = formatTime(elapsed);\n }, 1000);\n }\n\n private stopTimer(): void {\n if (this.timerInterval) {\n clearInterval(this.timerInterval);\n this.timerInterval = null;\n }\n }\n}\n","import type { ActionLog, RecordingChunk } from \"@firstlook-uat/shared\";\nimport type { ResolvedConfig } from \"../core/config.js\";\nimport type { EventBus } from \"../core/event-bus.js\";\n\n/**\n * Session recorder that captures:\n * 1. Periodic DOM snapshots (serialized HTML with masked fields)\n * 2. User action logs (clicks, scrolls, navigation, input)\n */\nconst MAX_SNAPSHOTS = 100;\nconst MAX_SNAPSHOT_SIZE = 2 * 1024 * 1024; // 2MB per snapshot\n\nexport class SessionRecorder {\n private actionLogs: ActionLog[] = [];\n private snapshots: RecordingChunk[] = [];\n private snapshotTimer: ReturnType<typeof setInterval> | null = null;\n private startTime = 0;\n private running = false;\n\n // Bound handlers for cleanup\n private handleClick: (e: MouseEvent) => void;\n private handleScroll: () => void;\n private handleInput: (e: Event) => void;\n\n constructor(\n private config: ResolvedConfig,\n private events: EventBus,\n private maskSelector: string,\n ) {\n this.handleClick = this.onClick.bind(this);\n this.handleScroll = this.throttle(this.onScroll.bind(this), 500);\n this.handleInput = this.onInput.bind(this);\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.startTime = Date.now();\n this.actionLogs = [];\n this.snapshots = [];\n\n document.addEventListener(\"click\", this.handleClick, { capture: true, passive: true });\n document.addEventListener(\"scroll\", this.handleScroll, { capture: true, passive: true });\n document.addEventListener(\"input\", this.handleInput, { capture: true, passive: true });\n\n if (this.config.recording.domSnapshot) {\n this.captureSnapshot();\n this.snapshotTimer = setInterval(\n () => this.captureSnapshot(),\n this.config.recording.snapshotInterval,\n );\n }\n\n this.events.emit({ type: \"recording:started\" });\n }\n\n stop(): void {\n if (!this.running) return;\n this.running = false;\n\n document.removeEventListener(\"click\", this.handleClick, { capture: true });\n document.removeEventListener(\"scroll\", this.handleScroll, { capture: true });\n document.removeEventListener(\"input\", this.handleInput, { capture: true });\n\n if (this.snapshotTimer) {\n clearInterval(this.snapshotTimer);\n this.snapshotTimer = null;\n }\n\n this.captureSnapshot();\n this.events.emit({ type: \"recording:stopped\" });\n }\n\n getActionLogs(): ActionLog[] {\n return [...this.actionLogs];\n }\n\n getSnapshots(): RecordingChunk[] {\n return [...this.snapshots];\n }\n\n getElapsedMs(): number {\n return this.running ? Date.now() - this.startTime : 0;\n }\n\n private addLog(log: Omit<ActionLog, \"timestamp\">): void {\n this.actionLogs.push({\n ...log,\n timestamp: Date.now() - this.startTime,\n });\n }\n\n private onClick(e: MouseEvent): void {\n const target = e.target as HTMLElement;\n this.addLog({\n type: \"click\",\n target: describeElement(target),\n });\n }\n\n private onScroll(): void {\n this.addLog({\n type: \"scroll\",\n value: `${window.scrollX},${window.scrollY}`,\n });\n }\n\n private onInput(e: Event): void {\n const target = e.target as HTMLElement;\n if (target.hasAttribute(\"data-fl-masked\")) {\n this.addLog({ type: \"input\", target: describeElement(target), value: \"[MASKED]\" });\n } else {\n const value = (target as HTMLInputElement).value;\n this.addLog({\n type: \"input\",\n target: describeElement(target),\n value: value?.slice(0, 100),\n });\n }\n }\n\n private captureSnapshot(): void {\n try {\n const clone = document.documentElement.cloneNode(true) as HTMLElement;\n if (this.maskSelector) {\n const masked = clone.querySelectorAll(this.maskSelector);\n for (const el of masked) {\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.value = \"***\";\n }\n el.textContent = \"***\";\n }\n }\n const scripts = clone.querySelectorAll(\"script\");\n for (const s of scripts) s.remove();\n\n const sdkHost = clone.querySelector(\"#firstlook-sdk-root\");\n sdkHost?.remove();\n\n const html = clone.outerHTML;\n if (html.length > MAX_SNAPSHOT_SIZE) return;\n if (this.snapshots.length >= MAX_SNAPSHOTS) {\n this.snapshots.shift();\n }\n this.snapshots.push({\n type: \"dom-snapshot\",\n timestamp: Date.now() - this.startTime,\n data: html,\n });\n } catch {\n // Snapshot failure is non-critical\n }\n }\n\n private throttle<T extends (...args: unknown[]) => void>(fn: T, ms: number): T {\n let last = 0;\n return ((...args: unknown[]) => {\n const now = Date.now();\n if (now - last >= ms) {\n last = now;\n fn(...args);\n }\n }) as T;\n }\n}\n\nfunction describeElement(el: HTMLElement): string {\n const tag = el.tagName.toLowerCase();\n const id = el.id ? `#${el.id}` : \"\";\n const classes = el.className && typeof el.className === \"string\"\n ? `.${el.className.trim().split(/\\s+/).slice(0, 2).join(\".\")}`\n : \"\";\n const text = el.textContent?.trim().slice(0, 30) || \"\";\n return `${tag}${id}${classes}${text ? ` \"${text}\"` : \"\"}`;\n}\n","import type { EventBus } from \"../core/event-bus.js\";\n\n/**\n * Voice annotation recorder using the MediaRecorder API.\n * Captures microphone input for \"thinking aloud\" protocol.\n */\nexport class VoiceRecorder {\n private mediaRecorder: MediaRecorder | null = null;\n private stream: MediaStream | null = null;\n private chunks: Blob[] = [];\n private _recording = false;\n\n constructor(private events: EventBus) {}\n\n get recording(): boolean {\n return this._recording;\n }\n\n async start(): Promise<void> {\n if (this._recording) return;\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const mimeType = this.getSupportedMimeType();\n const options: MediaRecorderOptions = mimeType ? { mimeType } : {};\n this.mediaRecorder = new MediaRecorder(this.stream, options);\n this.chunks = [];\n\n this.mediaRecorder.ondataavailable = (e) => {\n if (e.data.size > 0) {\n this.chunks.push(e.data);\n }\n };\n\n this.mediaRecorder.start(1000);\n this._recording = true;\n } catch (err) {\n console.warn(\"[FirstLook] Voice recording unavailable:\", err);\n this.events.emit({\n type: \"error\",\n error: new Error(`Voice recording failed: ${(err as Error).message}`),\n });\n }\n }\n\n async stopAsync(): Promise<Blob | null> {\n if (!this._recording || !this.mediaRecorder) return null;\n\n return new Promise<Blob | null>((resolve) => {\n this.mediaRecorder!.onstop = () => {\n const blob = this.chunks.length > 0\n ? new Blob(this.chunks, { type: this.mediaRecorder?.mimeType || \"audio/webm\" })\n : null;\n this.cleanup();\n resolve(blob);\n };\n this.mediaRecorder!.stop();\n });\n }\n\n async captureSnippet(durationMs: number = 10000): Promise<Blob | null> {\n await this.start();\n return new Promise((resolve) => {\n setTimeout(async () => {\n const blob = await this.stopAsync();\n resolve(blob);\n }, durationMs);\n });\n }\n\n private cleanup(): void {\n this._recording = false;\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n this.mediaRecorder = null;\n this.chunks = [];\n }\n\n private getSupportedMimeType(): string {\n const types = [\n \"audio/webm;codecs=opus\",\n \"audio/webm\",\n \"audio/ogg;codecs=opus\",\n \"audio/mp4\",\n ];\n for (const type of types) {\n if (typeof MediaRecorder !== \"undefined\" && MediaRecorder.isTypeSupported(type)) return type;\n }\n return \"\";\n }\n}\n","import type { ResolvedConfig } from \"../core/config.js\";\n\n/**\n * Dynamic watermark renderer.\n * Tiles UserID + Timestamp + IP across the viewport at near-invisible opacity.\n */\nexport class Watermark {\n private container: HTMLDivElement | null = null;\n private refreshInterval: ReturnType<typeof setInterval> | null = null;\n private cachedIp: string | null = null;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private config: ResolvedConfig,\n ) {}\n\n show(): void {\n if (this.container) return;\n this.container = document.createElement(\"div\");\n this.container.className = \"fl-watermark\";\n this.renderTiles();\n this.shadowRoot.appendChild(this.container);\n\n this.fetchIp().then(() => this.renderTiles());\n this.refreshInterval = setInterval(() => this.renderTiles(), 60_000);\n }\n\n hide(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n if (this.refreshInterval) {\n clearInterval(this.refreshInterval);\n this.refreshInterval = null;\n }\n }\n\n private async fetchIp(): Promise<void> {\n if (this.cachedIp) return;\n try {\n const response = await fetch(\"https://api.ipify.org?format=text\");\n if (response.ok) {\n this.cachedIp = await response.text();\n }\n } catch {\n this.cachedIp = \"N/A\";\n }\n }\n\n private renderTiles(): void {\n if (!this.container) return;\n this.container.innerHTML = \"\";\n\n const ip = this.cachedIp ?? \"\";\n const timestamp = new Date().toISOString().slice(0, 19);\n const text = ip\n ? `${this.config.userId} | ${timestamp} | ${ip}`\n : `${this.config.userId} | ${timestamp}`;\n\n const tileWidth = 320;\n const tileHeight = 80;\n const cols = Math.ceil(window.innerWidth / tileWidth) + 1;\n const rows = Math.ceil(window.innerHeight / tileHeight) + 1;\n\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const tile = document.createElement(\"span\");\n tile.className = \"fl-watermark-tile\";\n tile.textContent = text;\n tile.style.left = `${col * tileWidth}px`;\n tile.style.top = `${row * tileHeight}px`;\n this.container.appendChild(tile);\n }\n }\n }\n}\n","/**\n * Sensitive field masking.\n * Observes DOM for fields matching mask selectors and overlays them\n * in recording snapshots.\n */\nexport class FieldMasker {\n private observer: MutationObserver | null = null;\n private maskedElements = new Set<Element>();\n\n constructor(private selectors: string[]) {}\n\n start(): void {\n this.scanAndMark();\n this.observer = new MutationObserver(() => this.scanAndMark());\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"type\", \"class\", \"data-sensitive\", \"data-mask\"],\n });\n }\n\n stop(): void {\n this.observer?.disconnect();\n this.observer = null;\n for (const el of this.maskedElements) {\n el.removeAttribute(\"data-fl-masked\");\n }\n this.maskedElements.clear();\n }\n\n isMasked(element: Element): boolean {\n return element.hasAttribute(\"data-fl-masked\");\n }\n\n getCombinedSelector(): string {\n return this.selectors.join(\", \");\n }\n\n private scanAndMark(): void {\n const combinedSelector = this.getCombinedSelector();\n if (!combinedSelector) return;\n\n try {\n const elements = document.querySelectorAll(combinedSelector);\n for (const el of elements) {\n if (!this.maskedElements.has(el)) {\n el.setAttribute(\"data-fl-masked\", \"true\");\n this.maskedElements.add(el);\n }\n }\n for (const el of this.maskedElements) {\n if (!document.contains(el)) {\n this.maskedElements.delete(el);\n }\n }\n } catch {\n // Invalid selector — skip gracefully\n }\n }\n}\n","import type { SessionData, AnnotationData, RecordingChunk } from \"@firstlook-uat/shared\";\n\nconst DB_NAME = \"firstlook_uat\";\nconst DB_VERSION = 1;\n\nconst STORES = {\n sessions: \"sessions\",\n recordings: \"recordings\",\n annotations: \"annotations\",\n uploadQueue: \"upload_queue\",\n} as const;\n\n/**\n * IndexedDB-backed persistence layer.\n * Buffers session data locally and manages the upload queue.\n */\nexport class UATStorage {\n private db: IDBDatabase | null = null;\n\n async open(): Promise<void> {\n if (this.db) return;\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(STORES.sessions)) {\n db.createObjectStore(STORES.sessions, { keyPath: \"sessionId\" });\n }\n if (!db.objectStoreNames.contains(STORES.recordings)) {\n const store = db.createObjectStore(STORES.recordings, { autoIncrement: true });\n store.createIndex(\"sessionId\", \"sessionId\", { unique: false });\n }\n if (!db.objectStoreNames.contains(STORES.annotations)) {\n const store = db.createObjectStore(STORES.annotations, { autoIncrement: true });\n store.createIndex(\"sessionId\", \"sessionId\", { unique: false });\n }\n if (!db.objectStoreNames.contains(STORES.uploadQueue)) {\n db.createObjectStore(STORES.uploadQueue, { autoIncrement: true });\n }\n };\n request.onsuccess = () => {\n this.db = request.result;\n resolve();\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n async saveSession(session: SessionData): Promise<void> {\n await this.put(STORES.sessions, session);\n }\n\n async getSession(sessionId: string): Promise<SessionData | undefined> {\n return this.get(STORES.sessions, sessionId);\n }\n\n async saveRecording(sessionId: string, chunk: RecordingChunk): Promise<void> {\n await this.put(STORES.recordings, { sessionId, ...chunk });\n }\n\n async saveAnnotation(sessionId: string, annotation: AnnotationData): Promise<void> {\n await this.put(STORES.annotations, { sessionId, ...annotation });\n }\n\n async getAnnotations(sessionId: string): Promise<AnnotationData[]> {\n return this.getAllByIndex(STORES.annotations, \"sessionId\", sessionId);\n }\n\n async enqueueUpload(payload: unknown): Promise<void> {\n await this.put(STORES.uploadQueue, {\n payload,\n createdAt: Date.now(),\n attempts: 0,\n });\n }\n\n async getPendingUploads(): Promise<Array<{ key: IDBValidKey; payload: unknown; attempts: number; lastAttemptAt?: number }>> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORES.uploadQueue, \"readonly\");\n const store = tx.objectStore(STORES.uploadQueue);\n const request = store.openCursor();\n const results: Array<{ key: IDBValidKey; payload: unknown; attempts: number; lastAttemptAt?: number }> = [];\n request.onsuccess = () => {\n const cursor = request.result;\n if (cursor) {\n results.push({\n key: cursor.key,\n payload: cursor.value.payload,\n attempts: cursor.value.attempts,\n lastAttemptAt: cursor.value.lastAttemptAt,\n });\n cursor.continue();\n } else {\n resolve(results);\n }\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n async removeFromQueue(key: IDBValidKey): Promise<void> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORES.uploadQueue, \"readwrite\");\n const request = tx.objectStore(STORES.uploadQueue).delete(key);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n async incrementAttempts(key: IDBValidKey): Promise<void> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORES.uploadQueue, \"readwrite\");\n const store = tx.objectStore(STORES.uploadQueue);\n const getReq = store.get(key);\n getReq.onsuccess = () => {\n const record = getReq.result;\n if (record) {\n record.attempts = (record.attempts ?? 0) + 1;\n record.lastAttemptAt = Date.now();\n store.put(record, key);\n }\n };\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async clearSession(sessionId: string): Promise<void> {\n const db = this.ensureDb();\n const tx = db.transaction(\n [STORES.sessions, STORES.recordings, STORES.annotations],\n \"readwrite\",\n );\n tx.objectStore(STORES.sessions).delete(sessionId);\n for (const storeName of [STORES.recordings, STORES.annotations] as const) {\n const store = tx.objectStore(storeName);\n const index = store.index(\"sessionId\");\n const req = index.openCursor(IDBKeyRange.only(sessionId));\n req.onsuccess = () => {\n const cursor = req.result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n }\n };\n }\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n close(): void {\n this.db?.close();\n this.db = null;\n }\n\n private ensureDb(): IDBDatabase {\n if (!this.db) throw new Error(\"[FirstLook] Storage not opened. Call open() first.\");\n return this.db;\n }\n\n private async put(storeName: string, value: unknown): Promise<void> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(storeName, \"readwrite\");\n tx.objectStore(storeName).put(value);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n private async get<T>(storeName: string, key: IDBValidKey): Promise<T | undefined> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(storeName, \"readonly\");\n const request = tx.objectStore(storeName).get(key);\n request.onsuccess = () => resolve(request.result as T | undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n private async getAllByIndex<T>(storeName: string, indexName: string, key: IDBValidKey): Promise<T[]> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(storeName, \"readonly\");\n const index = tx.objectStore(storeName).index(indexName);\n const request = index.getAll(key);\n request.onsuccess = () => resolve(request.result as T[]);\n request.onerror = () => reject(request.error);\n });\n }\n}\n","import type { AnnotationData, DrawingPath } from \"@firstlook-uat/shared\";\nimport { el } from \"../ui/dom-helpers.js\";\n\nconst COLORS = [\"#e17055\", \"#6c5ce7\", \"#00b894\", \"#fdcb6e\", \"#ffffff\"];\n\n/**\n * Full-screen annotation overlay.\n * Captures a screenshot, allows drawing on it, and collects a text comment.\n */\nexport class AnnotationCanvas {\n private overlay: HTMLDivElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private ctx: CanvasRenderingContext2D | null = null;\n private drawing = false;\n private currentPath: Array<{ x: number; y: number }> = [];\n private paths: DrawingPath[] = [];\n private selectedColor = COLORS[0];\n private screenshotDataUrl = \"\";\n\n constructor(private shadowRoot: ShadowRoot) {}\n\n async open(): Promise<AnnotationData | null> {\n this.screenshotDataUrl = await this.captureScreenshot();\n\n return new Promise((resolve) => {\n this.overlay = el(\"div\", { className: \"fl-annotation-overlay\" }) as HTMLDivElement;\n\n const canvasWrap = el(\"div\", { className: \"fl-annotation-canvas-wrap\" });\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"fl-annotation-canvas\";\n canvasWrap.appendChild(this.canvas);\n this.overlay.appendChild(canvasWrap);\n\n const commentInput = el(\"input\", {\n className: \"fl-annotation-comment\",\n type: \"text\",\n placeholder: \"Add a comment...\",\n }) as HTMLInputElement;\n\n const colorPicker = el(\"div\", { className: \"fl-color-picker\" });\n for (const color of COLORS) {\n const swatch = el(\"div\", { className: `fl-color-swatch ${color === this.selectedColor ? \"fl-selected\" : \"\"}` });\n swatch.style.background = color;\n swatch.onclick = () => {\n this.selectedColor = color;\n colorPicker.querySelectorAll(\".fl-color-swatch\").forEach((s) => s.classList.remove(\"fl-selected\"));\n swatch.classList.add(\"fl-selected\");\n };\n colorPicker.appendChild(swatch);\n }\n\n const submitBtn = el(\"button\", { className: \"fl-btn fl-btn-ok\" }, [\"Submit\"]) as HTMLButtonElement;\n submitBtn.onclick = () => {\n const result: AnnotationData = {\n screenshotDataUrl: this.getAnnotatedImage(),\n drawings: [...this.paths],\n comment: commentInput.value,\n timestamp: Date.now(),\n };\n this.close();\n resolve(result);\n };\n\n const cancelBtn = el(\"button\", { className: \"fl-btn fl-btn-ng\" }, [\"Cancel\"]) as HTMLButtonElement;\n cancelBtn.onclick = () => {\n this.close();\n resolve(null);\n };\n\n const toolbar = el(\"div\", { className: \"fl-annotation-toolbar\" }, [\n colorPicker,\n commentInput,\n submitBtn,\n cancelBtn,\n ]);\n this.overlay.appendChild(toolbar);\n\n this.shadowRoot.appendChild(this.overlay);\n this.initCanvas();\n });\n }\n\n private close(): void {\n this.overlay?.remove();\n this.overlay = null;\n this.canvas = null;\n this.ctx = null;\n this.paths = [];\n this.currentPath = [];\n }\n\n private async captureScreenshot(): Promise<string> {\n try {\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n const clone = document.documentElement.cloneNode(true) as HTMLElement;\n const sdkRoot = clone.querySelector(\"#firstlook-sdk-root\");\n sdkRoot?.remove();\n const masked = clone.querySelectorAll('[data-fl-masked], input[type=\"password\"]');\n for (const el of masked) {\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.value = \"[MASKED]\";\n }\n el.textContent = \"[MASKED]\";\n }\n for (const s of clone.querySelectorAll(\"script\")) s.remove();\n\n const bodyClone = clone.querySelector(\"body\");\n if (bodyClone) {\n const bodyStyles = window.getComputedStyle(document.body);\n bodyClone.style.backgroundColor = bodyStyles.backgroundColor;\n bodyClone.style.color = bodyStyles.color;\n bodyClone.style.fontFamily = bodyStyles.fontFamily;\n bodyClone.style.margin = \"0\";\n bodyClone.style.overflow = \"hidden\";\n }\n\n const htmlString = new XMLSerializer().serializeToString(clone);\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\">\n <foreignObject width=\"100%\" height=\"100%\">\n ${htmlString}\n </foreignObject>\n </svg>`;\n\n const blob = new Blob([svg], { type: \"image/svg+xml;charset=utf-8\" });\n const url = URL.createObjectURL(blob);\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = width * window.devicePixelRatio;\n canvas.height = height * window.devicePixelRatio;\n const ctx = canvas.getContext(\"2d\")!;\n ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n\n return new Promise<string>((resolve) => {\n const img = new Image();\n img.onload = () => {\n ctx.drawImage(img, 0, 0, width, height);\n URL.revokeObjectURL(url);\n resolve(canvas.toDataURL(\"image/png\"));\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve(this.captureScreenshotFallback(width, height));\n };\n img.src = url;\n });\n } catch {\n return this.captureScreenshotFallback(window.innerWidth, window.innerHeight);\n }\n }\n\n private captureScreenshotFallback(width: number, height: number): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\")!;\n\n const bodyStyles = window.getComputedStyle(document.body);\n ctx.fillStyle = bodyStyles.backgroundColor || \"#ffffff\";\n ctx.fillRect(0, 0, width, height);\n\n ctx.fillStyle = \"#333\";\n ctx.font = \"14px sans-serif\";\n ctx.fillText(`URL: ${window.location.href}`, 20, 30);\n ctx.fillText(`Time: ${new Date().toISOString()}`, 20, 50);\n ctx.fillText(\"(SVG capture unavailable — metadata view)\", 20, 70);\n\n return canvas.toDataURL(\"image/png\");\n }\n\n private initCanvas(): void {\n if (!this.canvas) return;\n const img = new Image();\n img.onload = () => {\n const maxW = window.innerWidth * 0.9;\n const maxH = window.innerHeight * 0.7;\n const scale = Math.min(maxW / img.width, maxH / img.height, 1);\n this.canvas!.width = img.width * scale;\n this.canvas!.height = img.height * scale;\n\n this.ctx = this.canvas!.getContext(\"2d\")!;\n this.ctx.drawImage(img, 0, 0, this.canvas!.width, this.canvas!.height);\n\n this.canvas!.addEventListener(\"pointerdown\", this.onDrawStart.bind(this));\n this.canvas!.addEventListener(\"pointermove\", this.onDrawMove.bind(this));\n this.canvas!.addEventListener(\"pointerup\", this.onDrawEnd.bind(this));\n this.canvas!.addEventListener(\"pointerleave\", this.onDrawEnd.bind(this));\n };\n img.src = this.screenshotDataUrl;\n }\n\n private onDrawStart(e: PointerEvent): void {\n this.drawing = true;\n const rect = this.canvas!.getBoundingClientRect();\n this.currentPath = [{ x: e.clientX - rect.left, y: e.clientY - rect.top }];\n }\n\n private onDrawMove(e: PointerEvent): void {\n if (!this.drawing || !this.ctx) return;\n const rect = this.canvas!.getBoundingClientRect();\n const point = { x: e.clientX - rect.left, y: e.clientY - rect.top };\n this.currentPath.push(point);\n\n this.ctx.strokeStyle = this.selectedColor;\n this.ctx.lineWidth = 3;\n this.ctx.lineCap = \"round\";\n this.ctx.lineJoin = \"round\";\n\n if (this.currentPath.length >= 2) {\n const prev = this.currentPath[this.currentPath.length - 2];\n this.ctx.beginPath();\n this.ctx.moveTo(prev.x, prev.y);\n this.ctx.lineTo(point.x, point.y);\n this.ctx.stroke();\n }\n }\n\n private onDrawEnd(): void {\n if (this.drawing && this.currentPath.length > 0) {\n this.paths.push({\n points: [...this.currentPath],\n color: this.selectedColor,\n width: 3,\n });\n }\n this.drawing = false;\n this.currentPath = [];\n }\n\n private getAnnotatedImage(): string {\n return this.canvas?.toDataURL(\"image/png\") || this.screenshotDataUrl;\n }\n}\n","/**\n * Device shake detection trigger.\n * Uses the DeviceMotionEvent API to detect shake gestures.\n */\nexport class ShakeTrigger {\n private lastX = 0;\n private lastY = 0;\n private lastZ = 0;\n private shakeCount = 0;\n private lastShakeTime = 0;\n private handler: ((e: DeviceMotionEvent) => void) | null = null;\n private readonly THRESHOLD = 15;\n private readonly SHAKE_INTERVAL = 400;\n private readonly REQUIRED_SHAKES = 2;\n\n constructor(private onShake: () => void) {}\n\n async start(): Promise<void> {\n if (typeof (DeviceMotionEvent as any).requestPermission === \"function\") {\n try {\n const permission = await (DeviceMotionEvent as any).requestPermission();\n if (permission !== \"granted\") return;\n } catch {\n return;\n }\n }\n\n this.handler = this.onMotion.bind(this);\n window.addEventListener(\"devicemotion\", this.handler, { passive: true });\n }\n\n stop(): void {\n if (this.handler) {\n window.removeEventListener(\"devicemotion\", this.handler);\n this.handler = null;\n }\n }\n\n private onMotion(event: DeviceMotionEvent): void {\n const acc = event.accelerationIncludingGravity;\n if (!acc || acc.x == null || acc.y == null || acc.z == null) return;\n\n const deltaX = Math.abs(acc.x - this.lastX);\n const deltaY = Math.abs(acc.y - this.lastY);\n const deltaZ = Math.abs(acc.z - this.lastZ);\n\n if (deltaX + deltaY + deltaZ > this.THRESHOLD) {\n const now = Date.now();\n if (now - this.lastShakeTime < this.SHAKE_INTERVAL) {\n this.shakeCount++;\n if (this.shakeCount >= this.REQUIRED_SHAKES) {\n this.shakeCount = 0;\n this.onShake();\n }\n } else {\n this.shakeCount = 1;\n }\n this.lastShakeTime = now;\n }\n\n this.lastX = acc.x;\n this.lastY = acc.y;\n this.lastZ = acc.z;\n }\n}\n","import type { AnnotationData } from \"@firstlook-uat/shared\";\nimport type { EventBus } from \"../core/event-bus.js\";\nimport { AnnotationCanvas } from \"./annotation-canvas.js\";\nimport { ShakeTrigger } from \"../triggers/shake-trigger.js\";\n\n/**\n * Shake-to-report: when the user shakes the device (or triggers via keyboard shortcut),\n * the screen pauses and an annotation overlay appears for drawing + commenting.\n */\nexport class ShakeReporter {\n private shakeTrigger: ShakeTrigger;\n private annotationCanvas: AnnotationCanvas;\n private annotations: AnnotationData[] = [];\n private active = false;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private events: EventBus,\n ) {\n this.annotationCanvas = new AnnotationCanvas(shadowRoot);\n this.shakeTrigger = new ShakeTrigger(() => this.trigger());\n }\n\n async start(): Promise<void> {\n await this.shakeTrigger.start();\n document.addEventListener(\"keydown\", this.onKeydown);\n }\n\n stop(): void {\n this.shakeTrigger.stop();\n document.removeEventListener(\"keydown\", this.onKeydown);\n }\n\n getAnnotations(): AnnotationData[] {\n return [...this.annotations];\n }\n\n private onKeydown = (e: KeyboardEvent): void => {\n if (e.ctrlKey && e.shiftKey && e.key === \"R\") {\n e.preventDefault();\n this.trigger();\n }\n };\n\n private async trigger(): Promise<void> {\n if (this.active) return;\n this.active = true;\n\n try {\n const annotation = await this.annotationCanvas.open();\n if (annotation) {\n this.annotations.push(annotation);\n this.events.emit({ type: \"report:submitted\" });\n }\n } finally {\n this.active = false;\n }\n }\n}\n","/**\n * Multi-tap trigger for activating the debug menu.\n * Detects N rapid taps/clicks on the same screen region.\n */\nexport class TapTrigger {\n private tapCount = 0;\n private tapTimer: ReturnType<typeof setTimeout> | null = null;\n private handler: ((e: PointerEvent) => void) | null = null;\n\n constructor(\n private requiredTaps: number,\n private onActivate: () => void,\n ) {}\n\n start(): void {\n this.handler = this.onTap.bind(this);\n document.addEventListener(\"pointerdown\", this.handler, { passive: true });\n }\n\n stop(): void {\n if (this.handler) {\n document.removeEventListener(\"pointerdown\", this.handler);\n this.handler = null;\n }\n this.reset();\n }\n\n private onTap(_e: PointerEvent): void {\n this.tapCount++;\n if (this.tapTimer) clearTimeout(this.tapTimer);\n\n if (this.tapCount >= this.requiredTaps) {\n this.reset();\n this.onActivate();\n } else {\n this.tapTimer = setTimeout(() => this.reset(), 800);\n }\n }\n\n private reset(): void {\n this.tapCount = 0;\n if (this.tapTimer) {\n clearTimeout(this.tapTimer);\n this.tapTimer = null;\n }\n }\n}\n","/**\n * Deep link / URL-based activation trigger.\n * Checks for ?firstlook=1 or ?uat-mode=1 in the URL.\n */\nexport class DeepLinkTrigger {\n private hashHandler: (() => void) | null = null;\n\n constructor(private onActivate: () => void) {}\n\n start(): void {\n if (this.checkUrl()) {\n this.onActivate();\n }\n this.hashHandler = () => {\n if (this.checkUrl()) this.onActivate();\n };\n window.addEventListener(\"hashchange\", this.hashHandler);\n }\n\n stop(): void {\n if (this.hashHandler) {\n window.removeEventListener(\"hashchange\", this.hashHandler);\n this.hashHandler = null;\n }\n }\n\n private checkUrl(): boolean {\n const params = new URLSearchParams(window.location.search);\n return params.has(\"firstlook\") || params.has(\"uat-mode\");\n }\n}\n","import { el } from \"../ui/dom-helpers.js\";\n\nexport interface DebugMenuCallbacks {\n onStartSession: () => void;\n onReportIssue: () => void;\n onClose: () => void;\n}\n\n/**\n * Debug menu rendered inside Shadow DOM after SDK activation.\n * Provides manual control over SDK features.\n */\nexport class DebugMenu {\n private root: HTMLDivElement | null = null;\n private outsideClickHandler: ((e: MouseEvent) => void) | null = null;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private callbacks: DebugMenuCallbacks,\n ) {}\n\n show(): void {\n if (this.root) return;\n\n this.root = el(\"div\", { className: \"fl-debug-menu\" }) as HTMLDivElement;\n\n const items: Array<{ icon: string; label: string; action: () => void }> = [\n { icon: \"\\u25B6\", label: \"Start Session\", action: this.callbacks.onStartSession },\n { icon: \"\\uD83D\\uDCF8\", label: \"Report Issue\", action: this.callbacks.onReportIssue },\n { icon: \"\\u2716\", label: \"Close SDK\", action: this.callbacks.onClose },\n ];\n\n for (const item of items) {\n const btn = el(\"button\", { className: \"fl-debug-menu-item\" }, [\n el(\"span\", { className: \"fl-debug-menu-item-icon\" }, [item.icon]),\n item.label,\n ]) as HTMLButtonElement;\n btn.onclick = (e) => {\n e.stopPropagation();\n this.hide();\n item.action();\n };\n this.root.appendChild(btn);\n }\n\n this.shadowRoot.appendChild(this.root);\n\n // Close on outside click (delayed to avoid immediate close)\n setTimeout(() => {\n this.outsideClickHandler = (e: MouseEvent) => {\n if (this.root && !this.root.contains(e.target as Node)) {\n this.hide();\n }\n };\n document.addEventListener(\"click\", this.outsideClickHandler, { capture: true });\n }, 100);\n }\n\n hide(): void {\n this.root?.remove();\n this.root = null;\n if (this.outsideClickHandler) {\n document.removeEventListener(\"click\", this.outsideClickHandler, { capture: true });\n this.outsideClickHandler = null;\n }\n }\n\n get visible(): boolean {\n return this.root !== null;\n }\n}\n","/**\n * CSS styles injected into the Shadow DOM root.\n * Completely isolated from the host application.\n */\nexport const SDK_STYLES = /* css */ `\n :host {\n all: initial;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #1a1a2e;\n line-height: 1.5;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n /* === Quest Overlay === */\n .fl-quest-overlay {\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 2147483647;\n width: 360px;\n max-width: calc(100vw - 48px);\n background: #ffffff;\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16), 0 2px 8px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n animation: fl-slide-up 0.3s ease-out;\n border: 1px solid rgba(0, 0, 0, 0.06);\n }\n\n .fl-quest-overlay.fl-minimized {\n width: 56px;\n height: 56px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #6c5ce7;\n box-shadow: 0 4px 16px rgba(108, 92, 231, 0.4);\n }\n\n .fl-quest-overlay.fl-minimized .fl-quest-content {\n display: none;\n }\n\n .fl-quest-overlay.fl-minimized .fl-minimize-icon {\n display: block;\n color: #fff;\n font-size: 24px;\n }\n\n .fl-minimize-icon {\n display: none;\n }\n\n @keyframes fl-slide-up {\n from { transform: translateY(20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n\n .fl-quest-header {\n background: linear-gradient(135deg, #6c5ce7, #a29bfe);\n color: #fff;\n padding: 14px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n }\n\n .fl-quest-header-left {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .fl-quest-badge {\n background: rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n padding: 2px 10px;\n font-size: 11px;\n font-weight: 600;\n white-space: nowrap;\n letter-spacing: 0.5px;\n }\n\n .fl-quest-title {\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .fl-quest-minimize-btn {\n background: rgba(255, 255, 255, 0.2);\n border: none;\n color: #fff;\n width: 28px;\n height: 28px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n flex-shrink: 0;\n transition: background 0.15s;\n }\n\n .fl-quest-minimize-btn:hover {\n background: rgba(255, 255, 255, 0.3);\n }\n\n .fl-quest-body {\n padding: 16px;\n }\n\n .fl-quest-description {\n font-size: 14px;\n color: #4a4a5a;\n margin-bottom: 16px;\n line-height: 1.6;\n }\n\n .fl-quest-progress {\n display: flex;\n gap: 4px;\n margin-bottom: 16px;\n }\n\n .fl-quest-progress-dot {\n height: 4px;\n flex: 1;\n border-radius: 2px;\n background: #e0e0e0;\n transition: background 0.3s;\n }\n\n .fl-quest-progress-dot.fl-completed { background: #00b894; }\n .fl-quest-progress-dot.fl-failed { background: #e17055; }\n .fl-quest-progress-dot.fl-active { background: #6c5ce7; }\n\n .fl-quest-actions {\n display: flex;\n gap: 10px;\n }\n\n .fl-btn {\n flex: 1;\n padding: 10px 16px;\n border-radius: 10px;\n border: none;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: transform 0.1s, box-shadow 0.15s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n }\n\n .fl-btn:active { transform: scale(0.97); }\n\n .fl-btn-ok {\n background: #00b894;\n color: #fff;\n box-shadow: 0 2px 8px rgba(0, 184, 148, 0.3);\n }\n .fl-btn-ok:hover { box-shadow: 0 4px 12px rgba(0, 184, 148, 0.4); }\n\n .fl-btn-ng {\n background: #e17055;\n color: #fff;\n box-shadow: 0 2px 8px rgba(225, 112, 85, 0.3);\n }\n .fl-btn-ng:hover { box-shadow: 0 4px 12px rgba(225, 112, 85, 0.4); }\n\n /* === Voice recording indicator === */\n .fl-voice-indicator {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n background: #fff3f0;\n border-top: 1px solid rgba(225, 112, 85, 0.1);\n font-size: 12px;\n color: #e17055;\n }\n\n .fl-voice-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #e17055;\n animation: fl-pulse 1.2s ease-in-out infinite;\n }\n\n @keyframes fl-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n }\n\n /* === Annotation overlay === */\n .fl-annotation-overlay {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n background: rgba(0, 0, 0, 0.7);\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n animation: fl-fade-in 0.2s ease-out;\n }\n\n @keyframes fl-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .fl-annotation-canvas-wrap {\n position: relative;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n }\n\n .fl-annotation-canvas {\n display: block;\n cursor: crosshair;\n max-width: 90vw;\n max-height: 70vh;\n }\n\n .fl-annotation-toolbar {\n display: flex;\n gap: 8px;\n margin-top: 16px;\n align-items: center;\n }\n\n .fl-annotation-toolbar .fl-btn {\n flex: none;\n padding: 10px 20px;\n }\n\n .fl-annotation-comment {\n width: 300px;\n padding: 10px 14px;\n border-radius: 10px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n font-size: 14px;\n outline: none;\n backdrop-filter: blur(4px);\n transition: border-color 0.15s;\n }\n\n .fl-annotation-comment::placeholder { color: rgba(255, 255, 255, 0.5); }\n .fl-annotation-comment:focus { border-color: #a29bfe; }\n\n .fl-color-picker { display: flex; gap: 4px; }\n\n .fl-color-swatch {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n border: 2px solid transparent;\n cursor: pointer;\n transition: transform 0.1s;\n }\n .fl-color-swatch:hover { transform: scale(1.15); }\n .fl-color-swatch.fl-selected {\n border-color: #fff;\n box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.6);\n }\n\n /* === Watermark === */\n .fl-watermark {\n position: fixed;\n inset: 0;\n z-index: 2147483640;\n pointer-events: none;\n overflow: hidden;\n opacity: 0.03;\n }\n\n .fl-watermark-tile {\n position: absolute;\n font-size: 12px;\n font-family: monospace;\n color: #000;\n white-space: nowrap;\n transform: rotate(-30deg);\n user-select: none;\n }\n\n /* === Debug Menu === */\n .fl-debug-menu {\n position: fixed;\n bottom: 100px;\n right: 24px;\n z-index: 2147483646;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n padding: 8px;\n min-width: 200px;\n animation: fl-slide-up 0.2s ease-out;\n border: 1px solid rgba(0, 0, 0, 0.06);\n }\n\n .fl-debug-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border: none;\n background: none;\n width: 100%;\n text-align: left;\n border-radius: 8px;\n cursor: pointer;\n font-size: 14px;\n color: #1a1a2e;\n transition: background 0.15s;\n }\n .fl-debug-menu-item:hover { background: #f0f0f8; }\n\n .fl-debug-menu-item-icon {\n font-size: 18px;\n width: 24px;\n text-align: center;\n }\n\n /* === Session status bar === */\n .fl-status-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 16px;\n background: #f8f8fc;\n border-top: 1px solid rgba(0, 0, 0, 0.04);\n font-size: 11px;\n color: #888;\n }\n\n .fl-status-recording {\n display: flex;\n align-items: center;\n gap: 4px;\n color: #e17055;\n }\n\n .fl-status-timer {\n margin-left: auto;\n font-variant-numeric: tabular-nums;\n }\n\n /* === Completion summary === */\n .fl-summary { padding: 20px 16px; text-align: center; }\n .fl-summary-icon { font-size: 48px; margin-bottom: 12px; }\n .fl-summary-title { font-size: 18px; font-weight: 700; color: #1a1a2e; margin-bottom: 4px; }\n .fl-summary-subtitle { font-size: 13px; color: #888; margin-bottom: 16px; }\n .fl-summary-stats { display: flex; justify-content: center; gap: 24px; margin-bottom: 16px; }\n .fl-stat { text-align: center; }\n .fl-stat-value { font-size: 24px; font-weight: 700; }\n .fl-stat-value.fl-ok { color: #00b894; }\n .fl-stat-value.fl-ng { color: #e17055; }\n .fl-stat-label { font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 0.5px; }\n\n .fl-btn-finish {\n background: #6c5ce7;\n color: #fff;\n width: 100%;\n box-shadow: 0 2px 8px rgba(108, 92, 231, 0.3);\n }\n\n .fl-btn-skip {\n background: #636e72;\n color: #fff;\n box-shadow: 0 2px 8px rgba(99, 110, 114, 0.3);\n }\n\n /* === NG Voice Feedback Mode === */\n .fl-quest-header-ng { background: linear-gradient(135deg, #e17055, #d63031); }\n .fl-badge-ng { background: rgba(255, 255, 255, 0.25); }\n\n .fl-feedback-textarea {\n width: calc(100% - 32px);\n margin: 8px 16px 16px;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid #ddd;\n font-size: 13px;\n font-family: inherit;\n resize: vertical;\n outline: none;\n transition: border-color 0.15s;\n }\n .fl-feedback-textarea:focus { border-color: #6c5ce7; }\n .fl-feedback-textarea-modal { width: calc(100% - 32px); margin: 16px 16px 12px; }\n\n /* === Memo button === */\n .fl-quest-memo-row { display: flex; margin-bottom: 10px; }\n .fl-btn-memo {\n flex: none;\n background: #f0f0f8;\n color: #4a4a5a;\n font-size: 12px;\n padding: 6px 12px;\n box-shadow: none;\n border-radius: 8px;\n }\n .fl-btn-memo:hover { background: #e4e4f0; }\n\n /* === Feedback modal === */\n .fl-feedback-modal {\n position: absolute;\n inset: 0;\n background: #fff;\n border-radius: 16px;\n z-index: 10;\n display: flex;\n flex-direction: column;\n animation: fl-fade-in 0.15s ease-out;\n }\n .fl-feedback-modal-actions { padding: 0 16px 16px; }\n`;\n","import type {\n FirstLookConfig,\n Feedback,\n Quest,\n SessionData,\n SDKEventType,\n SDKEventListener,\n} from \"@firstlook-uat/shared\";\nimport { resolveConfig, collectDeviceInfo, type ResolvedConfig } from \"./config.js\";\nimport { EventBus } from \"./event-bus.js\";\nimport { QuestManager } from \"../quest/quest-manager.js\";\nimport { QuestOverlay } from \"../quest/quest-overlay.js\";\nimport { SessionRecorder } from \"../recording/session-recorder.js\";\nimport { VoiceRecorder } from \"../recording/voice-recorder.js\";\nimport { Watermark } from \"../security/watermark.js\";\nimport { FieldMasker } from \"../security/masking.js\";\nimport { UATStorage } from \"../persistence/storage.js\";\nimport { ShakeReporter } from \"../reporting/shake-reporter.js\";\nimport { TapTrigger } from \"../triggers/tap-trigger.js\";\nimport { DeepLinkTrigger } from \"../triggers/deep-link.js\";\nimport { DebugMenu } from \"./debug-menu.js\";\nimport { SDK_STYLES } from \"../ui/styles.js\";\nimport { generateId } from \"../ui/dom-helpers.js\";\n\nexport type SDKState = \"idle\" | \"initialized\" | \"active\" | \"recording\" | \"finished\";\n\nconst MAX_UPLOAD_RETRIES = 5;\n\n/**\n * FirstLookSDK — Main entry point for the UAT SDK.\n *\n * Lifecycle:\n * init(config) -> [trigger activation] -> startSession(quests) -> [quest flow] -> endSession()\n *\n * All UI is rendered inside a Shadow DOM to avoid style conflicts with the host app.\n */\nexport class FirstLookSDK {\n private config: ResolvedConfig | null = null;\n private events = new EventBus();\n private state: SDKState = \"idle\";\n\n // Shadow DOM root\n private hostElement: HTMLDivElement | null = null;\n private shadowRoot: ShadowRoot | null = null;\n\n // Modules (initialized lazily after init())\n private questManager: QuestManager | null = null;\n private questOverlay: QuestOverlay | null = null;\n private sessionRecorder: SessionRecorder | null = null;\n private voiceRecorder: VoiceRecorder | null = null;\n private watermark: Watermark | null = null;\n private fieldMasker: FieldMasker | null = null;\n private storage: UATStorage | null = null;\n private shakeReporter: ShakeReporter | null = null;\n private debugMenu: DebugMenu | null = null;\n\n // Triggers\n private tapTrigger: TapTrigger | null = null;\n private deepLinkTrigger: DeepLinkTrigger | null = null;\n\n // Session\n private sessionId: string | null = null;\n private sessionStartTime = 0;\n private maxDurationTimer: ReturnType<typeof setTimeout> | null = null;\n private backupTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n\n // ----------------------------------------------------------------\n // Public API\n // ----------------------------------------------------------------\n\n /**\n * Initialize the SDK with configuration.\n * This does NOT activate the UI — it only sets up triggers.\n */\n async init(rawConfig: FirstLookConfig): Promise<void> {\n if (this.state !== \"idle\") {\n console.warn(\"[FirstLook] SDK already initialized.\");\n return;\n }\n\n this.config = resolveConfig(rawConfig);\n\n // Open IndexedDB\n this.storage = new UATStorage();\n await this.storage.open();\n\n // Create Shadow DOM host\n this.createShadowHost();\n\n // Set up activation triggers\n this.setupTriggers();\n\n // Set up field masker\n this.fieldMasker = new FieldMasker(this.config.security.maskSelectors);\n\n this.state = \"initialized\";\n this.events.emit({ type: \"sdk:initialized\" });\n\n // Process any pending uploads from previous sessions (Wi-Fi check)\n this.flushUploadQueue(true);\n }\n\n /**\n * Manually activate the SDK UI (bypassing triggers).\n */\n activate(): void {\n if (this.state !== \"initialized\") {\n console.warn(\"[FirstLook] Cannot activate: SDK not initialized or already active.\");\n return;\n }\n this.onActivate();\n }\n\n /**\n * Start a UAT session with a list of quests.\n */\n async startSession(quests: Quest[]): Promise<string> {\n if (this.state !== \"active\") {\n throw new Error(\"[FirstLook] Cannot start session: SDK not active. Call activate() first.\");\n }\n\n // Hide debug menu if visible\n this.debugMenu?.hide();\n\n this.sessionId = generateId();\n this.sessionStartTime = Date.now();\n\n // Initialize modules\n this.questManager = new QuestManager(this.events);\n this.questManager.loadQuests(quests);\n\n this.sessionRecorder = new SessionRecorder(\n this.config!,\n this.events,\n this.fieldMasker!.getCombinedSelector(),\n );\n\n this.voiceRecorder = new VoiceRecorder(this.events);\n\n this.shakeReporter = new ShakeReporter(this.shadowRoot!, this.events);\n await this.shakeReporter.start();\n\n // Start recording\n this.sessionRecorder.start();\n this.fieldMasker!.start();\n\n // Start voice recording if enabled\n if (this.config!.recording.voice) {\n await this.voiceRecorder.start();\n }\n\n // Show watermark\n if (this.config!.security.watermark) {\n this.watermark = new Watermark(this.shadowRoot!, this.config!);\n this.watermark.show();\n }\n\n // Create quest overlay and start quest flow\n this.questOverlay = new QuestOverlay(this.shadowRoot!, {\n onOk: () => this.handleQuestOk(),\n onNg: () => this.handleQuestNg(),\n onNgWithFeedback: (comment: string) => this.handleNgFeedbackSubmit(comment),\n onMemo: (comment: string) => this.handleMemo(comment),\n onFinish: () => this.endSession(),\n onMinimize: () => {},\n });\n\n this.questManager.startSession(this.sessionStartTime);\n this.renderCurrentQuest();\n\n // Max duration timer\n const maxDuration = this.config!.recording.maxDuration;\n if (maxDuration > 0) {\n this.maxDurationTimer = setTimeout(() => this.endSession(), maxDuration * 1000);\n }\n\n // Periodic backup to IndexedDB every 30 seconds\n this.backupTimer = setInterval(() => this.backupSession(), 30_000);\n\n this.state = \"recording\";\n this.events.emit({ type: \"session:started\", sessionId: this.sessionId });\n\n return this.sessionId;\n }\n\n /**\n * Fetch quest definitions from the API and start a session.\n */\n async startSessionFromRemote(questSetId: string): Promise<string> {\n if (this.state !== \"active\") {\n throw new Error(\"[FirstLook] Cannot start session: SDK not active. Call activate() first.\");\n }\n if (!this.config) {\n throw new Error(\"[FirstLook] SDK not initialized.\");\n }\n\n const baseUrl = this.config.endpoint.replace(/\\/ingest-session$/, \"\");\n const response = await fetch(`${baseUrl}/export-quests?id=${encodeURIComponent(questSetId)}`, {\n headers: { \"X-API-Key\": this.config.apiKey },\n });\n\n if (!response.ok) {\n throw new Error(`[FirstLook] Failed to fetch quest set: ${response.status}`);\n }\n\n const data = await response.json();\n const quests: Quest[] = data.quests;\n return this.startSession(quests);\n }\n\n /**\n * End the current session, persist data, and trigger upload.\n */\n async endSession(): Promise<SessionData | null> {\n if (this.state !== \"recording\" || !this.sessionId) return null;\n\n // Clear timers\n if (this.maxDurationTimer) {\n clearTimeout(this.maxDurationTimer);\n this.maxDurationTimer = null;\n }\n if (this.backupTimer) {\n clearInterval(this.backupTimer);\n this.backupTimer = null;\n }\n\n // Stop recording\n this.sessionRecorder?.stop();\n await this.voiceRecorder?.stopAsync();\n this.shakeReporter?.stop();\n this.fieldMasker?.stop();\n this.watermark?.hide();\n\n // Convert voiceMemoBlob -> base64 for all quest results and feedbacks before upload\n const results = this.questManager!.getResults();\n await Promise.allSettled(\n results.map(async (r) => {\n if (r.voiceMemoBlob) {\n try {\n r.voiceMemoBase64 = await this.blobToBase64(r.voiceMemoBlob);\n } catch { /* skip failed conversion */ }\n delete (r as unknown as Record<string, unknown>).voiceMemoBlob;\n }\n await Promise.allSettled(\n r.feedbacks.map(async (f) => {\n if (f.voiceMemoBlob) {\n try {\n f.voiceMemoBase64 = await this.blobToBase64(f.voiceMemoBlob);\n } catch { /* skip failed conversion */ }\n delete (f as unknown as Record<string, unknown>).voiceMemoBlob;\n }\n }),\n );\n }),\n );\n\n // Build session data\n const session: SessionData = {\n sessionId: this.sessionId,\n projectId: this.config!.projectId,\n userId: this.config!.userId,\n role: this.config!.role,\n deviceInfo: collectDeviceInfo(),\n startedAt: new Date(this.sessionStartTime).toISOString(),\n endedAt: new Date().toISOString(),\n duration: Math.floor((Date.now() - this.sessionStartTime) / 1000),\n quests: results,\n recordings: this.sessionRecorder?.getSnapshots() ?? [],\n };\n\n // Persist to IndexedDB\n await this.storage?.saveSession(session);\n\n // Save annotations\n const annotations = this.shakeReporter?.getAnnotations() ?? [];\n for (const annotation of annotations) {\n await this.storage?.saveAnnotation(this.sessionId, annotation);\n }\n\n // Enqueue for upload\n await this.storage?.enqueueUpload({ session, annotations });\n\n // Clean up overlay\n this.questOverlay?.destroy();\n this.questOverlay = null;\n\n this.state = \"finished\";\n this.events.emit({ type: \"session:ended\", sessionId: this.sessionId });\n\n // Upload immediately on session end (no Wi-Fi check — explicit user action)\n this.flushUploadQueue(false);\n\n return session;\n }\n\n /**\n * Subscribe to SDK events.\n */\n on(type: SDKEventType | \"*\", listener: SDKEventListener): () => void {\n return this.events.on(type, listener);\n }\n\n /**\n * Get current SDK state.\n */\n getState(): SDKState {\n return this.state;\n }\n\n /**\n * Completely destroy the SDK instance and clean up all resources.\n */\n destroy(): void {\n if (this.maxDurationTimer) {\n clearTimeout(this.maxDurationTimer);\n this.maxDurationTimer = null;\n }\n if (this.backupTimer) {\n clearInterval(this.backupTimer);\n this.backupTimer = null;\n }\n this.tapTrigger?.stop();\n this.deepLinkTrigger?.stop();\n this.sessionRecorder?.stop();\n this.shakeReporter?.stop();\n this.fieldMasker?.stop();\n this.watermark?.hide();\n this.questOverlay?.destroy();\n this.debugMenu?.hide();\n this.hostElement?.remove();\n this.storage?.close();\n this.events.removeAll();\n this.state = \"idle\";\n }\n\n // ----------------------------------------------------------------\n // Private methods\n // ----------------------------------------------------------------\n\n private createShadowHost(): void {\n this.hostElement = document.createElement(\"div\");\n this.hostElement.id = \"firstlook-sdk-root\";\n this.hostElement.style.cssText = \"position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;\";\n document.body.appendChild(this.hostElement);\n\n this.shadowRoot = this.hostElement.attachShadow({ mode: \"closed\" });\n\n // Inject styles\n const style = document.createElement(\"style\");\n style.textContent = SDK_STYLES;\n this.shadowRoot.appendChild(style);\n\n // Make child elements interactive\n const container = document.createElement(\"div\");\n container.style.cssText = \"pointer-events:auto;\";\n this.shadowRoot.appendChild(container);\n }\n\n private setupTriggers(): void {\n const cfg = this.config!.triggers;\n\n // Multi-tap trigger\n this.tapTrigger = new TapTrigger(cfg.tapCount, () => this.onActivate());\n this.tapTrigger.start();\n\n // Deep link trigger\n if (cfg.deepLink) {\n this.deepLinkTrigger = new DeepLinkTrigger(() => this.onActivate());\n this.deepLinkTrigger.start();\n }\n\n // Custom check\n if (cfg.customCheck && cfg.customCheck()) {\n this.onActivate();\n }\n }\n\n private onActivate(): void {\n if (this.state !== \"initialized\") return;\n this.state = \"active\";\n\n // Stop triggers once activated\n this.tapTrigger?.stop();\n this.deepLinkTrigger?.stop();\n\n this.events.emit({ type: \"sdk:activated\" });\n\n // Show debug menu\n this.debugMenu = new DebugMenu(this.shadowRoot!, {\n onStartSession: () => {\n this.events.emit({ type: \"sdk:activated\" });\n },\n onReportIssue: () => {\n this.shakeReporter?.[\"trigger\"]?.();\n },\n onClose: () => {\n this.destroy();\n },\n });\n this.debugMenu.show();\n }\n\n private handleQuestOk(): void {\n if (!this.questManager || !this.sessionRecorder) return;\n\n const logs = this.sessionRecorder.getActionLogs();\n this.questManager.completeCurrentQuest(logs);\n this.renderCurrentQuest();\n }\n\n private handleQuestNg(): void {\n if (!this.questManager || !this.questOverlay) return;\n\n const quest = this.questManager.getCurrentQuest();\n if (!quest) return;\n\n this.questOverlay.renderFeedbackModal(quest.title, \"ng\");\n }\n\n private handleNgFeedbackSubmit(comment: string): void {\n if (!this.questManager || !this.sessionRecorder) return;\n\n const logs = this.sessionRecorder.getActionLogs();\n this.questManager.failCurrentQuest(logs, comment || undefined);\n this.renderCurrentQuest();\n }\n\n private handleMemo(comment: string): void {\n if (!this.questManager || !comment) return;\n\n const feedback: Feedback = {\n comment,\n timestamp: Date.now(),\n relativeTime: Date.now() - this.sessionStartTime,\n };\n this.questManager.addFeedback(feedback);\n }\n\n private renderCurrentQuest(): void {\n if (!this.questManager || !this.questOverlay) return;\n\n const status = this.questManager.getStatus();\n\n if (status.isBlocked) {\n const quest = this.questManager.getCurrentQuest();\n this.questOverlay.renderBlocked(quest?.title ?? \"Unknown\");\n return;\n }\n\n if (status.isFinished) {\n this.questOverlay.renderSummary(status.completed, status.failed, status.total);\n return;\n }\n\n const quest = this.questManager.getCurrentQuest();\n if (quest) {\n this.questOverlay.renderQuest(\n quest,\n this.questManager.getQuestStatuses(),\n this.voiceRecorder?.recording ?? false,\n );\n }\n }\n\n private async flushUploadQueue(checkConnection: boolean): Promise<void> {\n if (!this.storage || !this.config || this.isFlushing) return;\n this.isFlushing = true;\n\n try {\n if (checkConnection) {\n const conn = (navigator as any).connection;\n if (conn && conn.type && conn.type !== \"wifi\" && conn.effectiveType !== \"4g\") {\n return;\n }\n }\n\n const pending = await this.storage.getPendingUploads();\n for (const item of pending) {\n if (item.attempts >= MAX_UPLOAD_RETRIES) {\n await this.storage.removeFromQueue(item.key);\n continue;\n }\n\n if (item.attempts > 0) {\n const backoffMs = Math.pow(2, item.attempts) * 1000;\n const lastAttemptTime = item.lastAttemptAt ?? 0;\n if (Date.now() - lastAttemptTime < backoffMs) {\n continue;\n }\n }\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n },\n body: JSON.stringify(item.payload),\n });\n if (response.ok) {\n await this.storage.removeFromQueue(item.key);\n } else {\n await this.storage.incrementAttempts(item.key);\n }\n } catch {\n await this.storage.incrementAttempts(item.key);\n }\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n private async backupSession(): Promise<void> {\n if (!this.storage || !this.sessionId || !this.config || !this.sessionRecorder) return;\n try {\n const session: SessionData = {\n sessionId: this.sessionId,\n projectId: this.config.projectId,\n userId: this.config.userId,\n role: this.config.role,\n deviceInfo: collectDeviceInfo(),\n startedAt: new Date(this.sessionStartTime).toISOString(),\n duration: Math.floor((Date.now() - this.sessionStartTime) / 1000),\n quests: this.questManager?.getResults() ?? [],\n recordings: this.sessionRecorder.getSnapshots(),\n };\n await this.storage.saveSession(session);\n } catch {\n // Backup failure is non-critical\n }\n }\n\n private blobToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolve(reader.result as string);\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(blob);\n });\n }\n}\n"],"names":["DEFAULT_MASK_SELECTORS","resolveConfig","raw","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","collectDeviceInfo","EventBus","type","listener","event","fn","e","QuestManager","events","quests","a","b","startTime","r","q","i","result","logs","voiceMemo","quest","comment","feedback","fbs","el","tag","attrs","children","node","key","value","child","generateId","formatTime","seconds","m","s","QuestOverlay","shadowRoot","callbacks","statuses","recording","currentIdx","header","content","progress","status","dotClass","body","statusBar","timer","questTitle","variant","modal","isNg","textarea","actions","completed","failed","total","summary","btn","icon","className","text","onClick","label","valueClass","timerEl","elapsed","MAX_SNAPSHOTS","MAX_SNAPSHOT_SIZE","SessionRecorder","config","maskSelector","log","target","describeElement","clone","masked","scripts","sdkHost","html","ms","last","args","now","id","classes","VoiceRecorder","mimeType","options","err","resolve","blob","durationMs","track","types","Watermark","response","ip","timestamp","tileWidth","tileHeight","cols","rows","row","col","tile","FieldMasker","selectors","element","combinedSelector","elements","DB_NAME","DB_VERSION","STORES","UATStorage","reject","request","db","session","sessionId","chunk","annotation","payload","results","cursor","tx","store","getReq","record","storeName","req","indexName","COLORS","AnnotationCanvas","canvasWrap","commentInput","colorPicker","color","swatch","submitBtn","cancelBtn","toolbar","width","height","sdkRoot","bodyClone","bodyStyles","htmlString","svg","url","canvas","ctx","img","maxW","maxH","scale","rect","point","prev","ShakeTrigger","onShake","acc","deltaX","deltaY","deltaZ","ShakeReporter","TapTrigger","requiredTaps","onActivate","DeepLinkTrigger","params","DebugMenu","items","item","SDK_STYLES","MAX_UPLOAD_RETRIES","FirstLookSDK","rawConfig","maxDuration","questSetId","baseUrl","f","annotations","_k","style","container","cfg","checkConnection","conn","pending","backoffMs","lastAttemptTime","reader"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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"}
|
|
1
|
+
{"version":3,"file":"firstlook.umd.js","sources":["../src/core/config.ts","../src/core/event-bus.ts","../src/quest/quest-manager.ts","../src/ui/dom-helpers.ts","../src/quest/quest-overlay.ts","../src/recording/session-recorder.ts","../src/recording/voice-recorder.ts","../src/security/watermark.ts","../src/security/masking.ts","../src/persistence/storage.ts","../src/reporting/annotation-canvas.ts","../src/triggers/shake-trigger.ts","../src/reporting/shake-reporter.ts","../src/triggers/tap-trigger.ts","../src/triggers/deep-link.ts","../src/triggers/keyboard-trigger.ts","../src/core/debug-menu.ts","../src/ui/styles.ts","../src/core/sdk.ts"],"sourcesContent":["import type { FirstLookConfig, DeviceInfo } from \"@firstlook-uat/shared\";\n\n/** Resolved configuration with all defaults applied */\nexport interface ResolvedConfig {\n projectId: string;\n apiKey: string;\n userId: string;\n role?: string;\n context: Record<string, unknown>;\n endpoint: string;\n triggers: {\n tapCount: number;\n deepLink: boolean;\n shake: boolean;\n keyboard: boolean;\n customCheck?: () => boolean;\n };\n security: {\n watermark: boolean;\n maskSelectors: string[];\n };\n recording: {\n domSnapshot: boolean;\n voice: boolean;\n maxDuration: number;\n snapshotInterval: number;\n };\n}\n\nconst DEFAULT_MASK_SELECTORS = [\n 'input[type=\"password\"]',\n \"[data-sensitive]\",\n \"[data-mask]\",\n \".uat-mask\",\n];\n\nexport function resolveConfig(raw: FirstLookConfig): ResolvedConfig {\n if (!raw.endpoint) {\n throw new Error(\"[FirstLook] 'endpoint' is required. Set your Supabase ingest-session URL.\");\n }\n\n return {\n projectId: raw.projectId,\n apiKey: raw.apiKey,\n userId: raw.userId,\n role: raw.role,\n context: raw.context ?? {},\n endpoint: raw.endpoint,\n triggers: {\n tapCount: raw.triggers?.tapCount ?? 5,\n deepLink: raw.triggers?.deepLink ?? true,\n shake: raw.triggers?.shake ?? true,\n keyboard: raw.triggers?.keyboard ?? true,\n customCheck: raw.triggers?.customCheck,\n },\n security: {\n watermark: raw.security?.watermark ?? true,\n maskSelectors: raw.security?.maskSelectors ?? DEFAULT_MASK_SELECTORS,\n },\n recording: {\n domSnapshot: raw.recording?.domSnapshot ?? true,\n voice: raw.recording?.voice ?? true,\n maxDuration: raw.recording?.maxDuration ?? 600,\n snapshotInterval: raw.recording?.snapshotInterval ?? 1000,\n },\n };\n}\n\nexport function collectDeviceInfo(): DeviceInfo {\n return {\n userAgent: navigator.userAgent,\n platform: navigator.platform,\n language: navigator.language,\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n pixelRatio: window.devicePixelRatio,\n touchSupport: \"ontouchstart\" in window || navigator.maxTouchPoints > 0,\n };\n}\n","import type { SDKEvent, SDKEventType, SDKEventListener } from \"@firstlook-uat/shared\";\n\n/**\n * Internal pub/sub event bus for SDK modules to communicate\n * without direct coupling.\n */\nexport class EventBus {\n private listeners = new Map<SDKEventType | \"*\", Set<SDKEventListener>>();\n\n on(type: SDKEventType | \"*\", listener: SDKEventListener): () => void {\n if (!this.listeners.has(type)) {\n this.listeners.set(type, new Set());\n }\n this.listeners.get(type)!.add(listener);\n return () => this.off(type, listener);\n }\n\n off(type: SDKEventType | \"*\", listener: SDKEventListener): void {\n this.listeners.get(type)?.delete(listener);\n }\n\n emit(event: SDKEvent): void {\n // Notify specific listeners\n this.listeners.get(event.type)?.forEach((fn) => {\n try {\n fn(event);\n } catch (e) {\n console.error(\"[FirstLook] Event listener error:\", e);\n }\n });\n // Notify wildcard listeners\n this.listeners.get(\"*\")?.forEach((fn) => {\n try {\n fn(event);\n } catch (e) {\n console.error(\"[FirstLook] Event listener error:\", e);\n }\n });\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n","import type { Quest, QuestResult, QuestStatus, ActionLog, Feedback } from \"@firstlook-uat/shared\";\nimport type { EventBus } from \"../core/event-bus.js\";\n\n/**\n * Manages the quest lifecycle: ordering, progression, status tracking.\n * Does not handle UI — delegates rendering to QuestOverlay.\n */\nexport class QuestManager {\n private quests: Quest[] = [];\n private results: QuestResult[] = [];\n private currentIndex = -1;\n private sessionStartTime = 0;\n private blocked = false;\n private pendingFeedbacks: Feedback[] = [];\n\n constructor(private events: EventBus) {}\n\n loadQuests(quests: Quest[]): void {\n this.quests = [...quests].sort((a, b) => a.order - b.order);\n this.results = [];\n this.currentIndex = -1;\n this.blocked = false;\n }\n\n startSession(startTime: number): void {\n this.sessionStartTime = startTime;\n this.advance();\n }\n\n getCurrentQuest(): Quest | null {\n if (this.blocked || this.currentIndex < 0 || this.currentIndex >= this.quests.length) {\n return null;\n }\n return this.quests[this.currentIndex];\n }\n\n getStatus(): {\n total: number;\n completed: number;\n failed: number;\n current: number;\n isBlocked: boolean;\n isFinished: boolean;\n } {\n return {\n total: this.quests.length,\n completed: this.results.filter((r) => r.status === \"COMPLETED\").length,\n failed: this.results.filter((r) => r.status === \"FAILED\").length,\n current: this.currentIndex,\n isBlocked: this.blocked,\n isFinished: this.currentIndex >= this.quests.length,\n };\n }\n\n getQuestStatuses(): QuestStatus[] {\n return this.quests.map((q, i) => {\n const result = this.results.find((r) => r.questId === q.id);\n if (result) return result.status;\n if (i === this.currentIndex && !this.blocked) return \"ACTIVE\";\n if (this.blocked && i >= this.currentIndex) return \"BLOCKED\";\n return \"PENDING\";\n });\n }\n\n completeCurrentQuest(\n logs: ActionLog[],\n options?: { comment?: string; concern?: boolean; voiceMemo?: Blob },\n ): QuestResult | null {\n const quest = this.getCurrentQuest();\n if (!quest) return null;\n\n const result: QuestResult = {\n questId: quest.id,\n status: \"COMPLETED\",\n timestamp: Date.now(),\n relativeTime: Date.now() - this.sessionStartTime,\n voiceMemoBlob: options?.voiceMemo,\n logs,\n comment: options?.comment,\n concern: options?.concern ?? false,\n feedbacks: this.drainFeedbacks(),\n };\n\n this.results.push(result);\n this.events.emit({ type: \"quest:completed\", questId: quest.id });\n this.advance();\n return result;\n }\n\n failCurrentQuest(logs: ActionLog[], comment?: string, voiceMemo?: Blob): QuestResult | null {\n const quest = this.getCurrentQuest();\n if (!quest) return null;\n\n const result: QuestResult = {\n questId: quest.id,\n status: \"FAILED\",\n timestamp: Date.now(),\n relativeTime: Date.now() - this.sessionStartTime,\n voiceMemoBlob: voiceMemo,\n logs,\n comment,\n concern: false,\n feedbacks: this.drainFeedbacks(),\n };\n\n this.results.push(result);\n this.events.emit({ type: \"quest:failed\", questId: quest.id });\n\n if (quest.blocking) {\n this.blocked = true;\n this.events.emit({ type: \"quest:blocked\", questId: quest.id });\n } else {\n this.advance();\n }\n\n return result;\n }\n\n addFeedback(feedback: Feedback): void {\n this.pendingFeedbacks.push(feedback);\n }\n\n getResults(): QuestResult[] {\n return [...this.results];\n }\n\n private drainFeedbacks(): Feedback[] {\n const fbs = this.pendingFeedbacks;\n this.pendingFeedbacks = [];\n return fbs;\n }\n\n private advance(): void {\n this.currentIndex++;\n if (this.currentIndex < this.quests.length) {\n this.events.emit({ type: \"quest:started\", questId: this.quests[this.currentIndex].id });\n }\n }\n}\n","/**\n * Minimal DOM helpers for building UI without a framework.\n */\n\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: Array<Node | string>,\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"className\") {\n node.className = value;\n } else {\n node.setAttribute(key, value);\n }\n }\n }\n if (children) {\n for (const child of children) {\n if (typeof child === \"string\") {\n node.appendChild(document.createTextNode(child));\n } else {\n node.appendChild(child);\n }\n }\n }\n return node;\n}\n\nexport function generateId(): string {\n return `fl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function formatTime(seconds: number): string {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${m.toString().padStart(2, \"0\")}:${s.toString().padStart(2, \"0\")}`;\n}\n","import type { Quest, QuestStatus } from \"@firstlook-uat/shared\";\nimport { el, formatTime } from \"../ui/dom-helpers.js\";\n\nexport interface QuestOverlayCallbacks {\n onOkWithFeedback: (comment: string) => void;\n onConcern: (comment: string) => void;\n onNg: () => void;\n onNgWithFeedback: (comment: string) => void;\n onMemo: (comment: string) => void;\n onFinish: () => void;\n onMinimize: () => void;\n}\n\n/**\n * Quest overlay UI rendered inside Shadow DOM.\n * Displays the current quest card with OK/NG actions.\n */\nexport class QuestOverlay {\n private root: HTMLDivElement;\n private minimized = false;\n private timerInterval: ReturnType<typeof setInterval> | null = null;\n private startTime = 0;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private callbacks: QuestOverlayCallbacks,\n ) {\n this.root = document.createElement(\"div\");\n this.root.className = \"fl-quest-overlay\";\n // Prevent clicks on the overlay from reaching the host app\n for (const evt of [\"click\", \"mousedown\", \"mouseup\", \"pointerdown\", \"pointerup\", \"touchstart\", \"touchend\"] as const) {\n this.root.addEventListener(evt, (e) => e.stopPropagation());\n }\n this.shadowRoot.appendChild(this.root);\n }\n\n renderQuest(\n quest: Quest,\n statuses: QuestStatus[],\n recording: boolean,\n ): void {\n this.minimized = false;\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n\n // Header\n const currentIdx = statuses.indexOf(\"ACTIVE\");\n const header = el(\"div\", { className: \"fl-quest-header\" }, [\n el(\"div\", { className: \"fl-quest-header-left\" }, [\n el(\"span\", { className: \"fl-quest-badge\" }, [`Q${currentIdx + 1}/${statuses.length}`]),\n el(\"span\", { className: \"fl-quest-title\" }, [quest.title]),\n ]),\n this.createMinimizeBtn(),\n ]);\n this.root.appendChild(header);\n\n // Content wrapper\n const content = el(\"div\", { className: \"fl-quest-content\" });\n\n // Progress dots\n const progress = el(\"div\", { className: \"fl-quest-progress\" });\n for (const status of statuses) {\n const dotClass = status === \"COMPLETED\" ? \"fl-completed\"\n : status === \"FAILED\" ? \"fl-failed\"\n : status === \"ACTIVE\" ? \"fl-active\"\n : \"\";\n progress.appendChild(el(\"div\", { className: `fl-quest-progress-dot ${dotClass}` }));\n }\n\n // Body\n const body = el(\"div\", { className: \"fl-quest-body\" }, [\n progress,\n el(\"p\", { className: \"fl-quest-description\" }, [quest.description]),\n el(\"div\", { className: \"fl-quest-memo-row\" }, [\n this.createButton(\"fl-btn fl-btn-memo\", \"\\uD83D\\uDCDD \\u30E1\\u30E2\\u3092\\u6B8B\\u3059\", () => this.renderFeedbackModal(quest.title)),\n ]),\n el(\"div\", { className: \"fl-quest-actions\" }, [\n this.createButton(\"fl-btn fl-btn-ok\", \"\\u2713 OK\", () => this.renderFeedbackModal(quest.title, \"ok\")),\n this.createButton(\"fl-btn fl-btn-concern\", \"\\u26A0 \\u6C17\\u306B\\u306A\\u308B\", () => this.renderFeedbackModal(quest.title, \"concern\")),\n this.createButton(\"fl-btn fl-btn-ng\", \"\\u2717 NG\", this.callbacks.onNg),\n ]),\n ]);\n content.appendChild(body);\n\n // Voice indicator\n if (recording) {\n content.appendChild(\n el(\"div\", { className: \"fl-voice-indicator\" }, [\n el(\"span\", { className: \"fl-voice-dot\" }),\n \"Recording...\",\n ]),\n );\n }\n\n // Status bar with timer\n const statusBar = el(\"div\", { className: \"fl-status-bar\" });\n if (recording) {\n statusBar.appendChild(\n el(\"span\", { className: \"fl-status-recording\" }, [\n el(\"span\", { className: \"fl-voice-dot\" }),\n \"REC\",\n ]),\n );\n }\n const timer = el(\"span\", { className: \"fl-status-timer\" }, [\"00:00\"]);\n statusBar.appendChild(timer);\n content.appendChild(statusBar);\n\n this.root.appendChild(content);\n\n // Start timer\n this.startTimer(timer);\n\n // Minimize handler on the overlay itself (when minimized)\n this.root.onclick = () => {\n if (this.minimized) {\n this.minimized = false;\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n this.root.appendChild(header);\n this.root.appendChild(content);\n this.startTimer(timer);\n }\n };\n }\n\n renderFeedbackModal(questTitle: string, variant: \"memo\" | \"ng\" | \"ok\" | \"concern\" = \"memo\"): void {\n const modal = el(\"div\", { className: \"fl-feedback-modal\" });\n\n const headerClassMap: Record<string, string> = {\n ng: \"fl-quest-header fl-quest-header-ng\",\n ok: \"fl-quest-header fl-quest-header-ok\",\n concern: \"fl-quest-header fl-quest-header-concern\",\n memo: \"fl-quest-header\",\n };\n const badgeTextMap: Record<string, string> = {\n ng: \"NG\",\n ok: \"\\u2713 OK\",\n concern: \"\\u26A0 \\u6C17\\u306B\\u306A\\u308B\",\n memo: \"\\uD83D\\uDCDD\",\n };\n const badgeClassMap: Record<string, string> = {\n ng: \"fl-quest-badge fl-badge-ng\",\n ok: \"fl-quest-badge fl-badge-ok\",\n concern: \"fl-quest-badge fl-badge-concern\",\n memo: \"fl-quest-badge\",\n };\n\n const header = el(\"div\", { className: headerClassMap[variant] }, [\n el(\"div\", { className: \"fl-quest-header-left\" }, [\n el(\"span\", { className: badgeClassMap[variant] }, [badgeTextMap[variant]]),\n el(\"span\", { className: \"fl-quest-title\" }, [questTitle]),\n ]),\n ]);\n modal.appendChild(header);\n\n const content = el(\"div\", { className: \"fl-quest-content\" });\n\n const placeholderMap: Record<string, string> = {\n ng: \"\\u4F55\\u304C\\u3046\\u307E\\u304F\\u3044\\u304B\\u306A\\u304B\\u3063\\u305F\\u304B\\u6559\\u3048\\u3066\\u304F\\u3060\\u3055\\u3044...\",\n ok: \"\\u30B3\\u30E1\\u30F3\\u30C8\\uFF08\\u4EFB\\u610F\\uFF09\",\n concern: \"\\u6C17\\u306B\\u306A\\u3063\\u305F\\u70B9\\u3092\\u6559\\u3048\\u3066\\u304F\\u3060\\u3055\\u3044...\",\n memo: \"\\u6C17\\u3065\\u3044\\u305F\\u3053\\u3068\\u3092\\u30E1\\u30E2...\\uD83D\\uDCDD\",\n };\n\n const textarea = document.createElement(\"textarea\");\n textarea.className = \"fl-feedback-textarea fl-feedback-textarea-modal\";\n textarea.placeholder = placeholderMap[variant];\n textarea.rows = 3;\n content.appendChild(textarea);\n\n const submitLabel = variant === \"ok\" || variant === \"concern\" ? \"\\u9001\\u4FE1\" : \"\\u9001\\u4FE1\";\n const skipLabel = variant === \"ok\" ? \"\\u30B9\\u30AD\\u30C3\\u30D7\" : \"\\u30AD\\u30E3\\u30F3\\u30BB\\u30EB\";\n\n const actions = el(\"div\", { className: \"fl-quest-actions fl-feedback-modal-actions\" }, [\n this.createButton(\"fl-btn fl-btn-skip\", skipLabel, () => {\n if (variant === \"ng\") {\n this.callbacks.onNgWithFeedback(\"\");\n } else if (variant === \"ok\") {\n this.callbacks.onOkWithFeedback(\"\");\n }\n modal.remove();\n }),\n this.createButton(\"fl-btn fl-btn-finish\", submitLabel, () => {\n const comment = textarea.value.trim();\n if (variant === \"ng\") {\n this.callbacks.onNgWithFeedback(comment);\n } else if (variant === \"ok\") {\n this.callbacks.onOkWithFeedback(comment);\n } else if (variant === \"concern\") {\n if (!comment) return;\n this.callbacks.onConcern(comment);\n } else {\n if (!comment) {\n modal.remove();\n return;\n }\n this.callbacks.onMemo(comment);\n }\n modal.remove();\n }),\n ]);\n content.appendChild(actions);\n\n modal.appendChild(content);\n this.root.appendChild(modal);\n\n textarea.focus();\n }\n\n renderSummary(\n completed: number,\n failed: number,\n total: number,\n ): void {\n this.stopTimer();\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n\n const summary = el(\"div\", { className: \"fl-summary\" }, [\n el(\"div\", { className: \"fl-summary-icon\" }, [completed === total ? \"\\uD83C\\uDF89\" : \"\\uD83D\\uDCCB\"]),\n el(\"div\", { className: \"fl-summary-title\" }, [\n completed === total ? \"All Quests Complete!\" : \"Session Complete\",\n ]),\n el(\"div\", { className: \"fl-summary-subtitle\" }, [\n `${completed + failed} of ${total} quests attempted`,\n ]),\n el(\"div\", { className: \"fl-summary-stats\" }, [\n this.createStat(completed.toString(), \"Passed\", \"fl-ok\"),\n this.createStat(failed.toString(), \"Failed\", \"fl-ng\"),\n ]),\n this.createButton(\"fl-btn fl-btn-finish\", \"Finish & Upload\", this.callbacks.onFinish),\n ]);\n\n this.root.appendChild(summary);\n }\n\n renderBlocked(questTitle: string): void {\n this.stopTimer();\n this.root.className = \"fl-quest-overlay\";\n this.root.innerHTML = \"\";\n\n const body = el(\"div\", { className: \"fl-summary\" }, [\n el(\"div\", { className: \"fl-summary-icon\" }, [\"\\uD83D\\uDED1\"]),\n el(\"div\", { className: \"fl-summary-title\" }, [\"Blocked\"]),\n el(\"div\", { className: \"fl-summary-subtitle\" }, [\n `Quest \"${questTitle}\" is blocking. Session halted.`,\n ]),\n this.createButton(\"fl-btn fl-btn-finish\", \"Finish & Upload\", this.callbacks.onFinish),\n ]);\n\n this.root.appendChild(body);\n }\n\n destroy(): void {\n this.stopTimer();\n this.root.remove();\n }\n\n private createMinimizeBtn(): HTMLButtonElement {\n const btn = el(\"button\", { className: \"fl-quest-minimize-btn\" }, [\"\\u2212\"]) as HTMLButtonElement;\n btn.onclick = (e) => {\n e.stopPropagation();\n this.minimized = true;\n this.root.className = \"fl-quest-overlay fl-minimized\";\n this.root.innerHTML = \"\";\n const icon = el(\"span\", { className: \"fl-minimize-icon\" }, [\"\\uD83D\\uDD0D\"]);\n this.root.appendChild(icon);\n this.callbacks.onMinimize();\n };\n return btn;\n }\n\n private createButton(className: string, text: string, onClick: () => void): HTMLButtonElement {\n const btn = el(\"button\", { className }) as HTMLButtonElement;\n btn.textContent = text;\n btn.onclick = (e) => {\n e.stopPropagation();\n onClick();\n };\n return btn;\n }\n\n private createStat(value: string, label: string, valueClass: string): HTMLDivElement {\n return el(\"div\", { className: \"fl-stat\" }, [\n el(\"div\", { className: `fl-stat-value ${valueClass}` }, [value]),\n el(\"div\", { className: \"fl-stat-label\" }, [label]),\n ]) as HTMLDivElement;\n }\n\n private startTimer(timerEl: HTMLElement): void {\n this.stopTimer();\n if (!this.startTime) this.startTime = Date.now();\n this.timerInterval = setInterval(() => {\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n timerEl.textContent = formatTime(elapsed);\n }, 1000);\n }\n\n private stopTimer(): void {\n if (this.timerInterval) {\n clearInterval(this.timerInterval);\n this.timerInterval = null;\n }\n }\n}\n","import type { ActionLog, RecordingChunk } from \"@firstlook-uat/shared\";\nimport type { ResolvedConfig } from \"../core/config.js\";\nimport type { EventBus } from \"../core/event-bus.js\";\n\n/**\n * Session recorder that captures:\n * 1. Periodic DOM snapshots (serialized HTML with masked fields)\n * 2. User action logs (clicks, scrolls, navigation, input)\n */\nconst MAX_SNAPSHOTS = 100;\nconst MAX_SNAPSHOT_SIZE = 2 * 1024 * 1024; // 2MB per snapshot\n\nexport class SessionRecorder {\n private actionLogs: ActionLog[] = [];\n private snapshots: RecordingChunk[] = [];\n private snapshotTimer: ReturnType<typeof setInterval> | null = null;\n private startTime = 0;\n private running = false;\n\n // Bound handlers for cleanup\n private handleClick: (e: MouseEvent) => void;\n private handleScroll: () => void;\n private handleInput: (e: Event) => void;\n\n constructor(\n private config: ResolvedConfig,\n private events: EventBus,\n private maskSelector: string,\n ) {\n this.handleClick = this.onClick.bind(this);\n this.handleScroll = this.throttle(this.onScroll.bind(this), 500);\n this.handleInput = this.onInput.bind(this);\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.startTime = Date.now();\n this.actionLogs = [];\n this.snapshots = [];\n\n document.addEventListener(\"click\", this.handleClick, { capture: true, passive: true });\n document.addEventListener(\"scroll\", this.handleScroll, { capture: true, passive: true });\n document.addEventListener(\"input\", this.handleInput, { capture: true, passive: true });\n\n if (this.config.recording.domSnapshot) {\n this.captureSnapshot();\n this.snapshotTimer = setInterval(\n () => this.captureSnapshot(),\n this.config.recording.snapshotInterval,\n );\n }\n\n this.events.emit({ type: \"recording:started\" });\n }\n\n stop(): void {\n if (!this.running) return;\n this.running = false;\n\n document.removeEventListener(\"click\", this.handleClick, { capture: true });\n document.removeEventListener(\"scroll\", this.handleScroll, { capture: true });\n document.removeEventListener(\"input\", this.handleInput, { capture: true });\n\n if (this.snapshotTimer) {\n clearInterval(this.snapshotTimer);\n this.snapshotTimer = null;\n }\n\n this.captureSnapshot();\n this.events.emit({ type: \"recording:stopped\" });\n }\n\n getActionLogs(): ActionLog[] {\n return [...this.actionLogs];\n }\n\n getSnapshots(): RecordingChunk[] {\n return [...this.snapshots];\n }\n\n getElapsedMs(): number {\n return this.running ? Date.now() - this.startTime : 0;\n }\n\n private addLog(log: Omit<ActionLog, \"timestamp\">): void {\n this.actionLogs.push({\n ...log,\n timestamp: Date.now() - this.startTime,\n });\n }\n\n private onClick(e: MouseEvent): void {\n const target = e.target as HTMLElement;\n this.addLog({\n type: \"click\",\n target: describeElement(target),\n });\n }\n\n private onScroll(): void {\n this.addLog({\n type: \"scroll\",\n value: `${window.scrollX},${window.scrollY}`,\n });\n }\n\n private onInput(e: Event): void {\n const target = e.target as HTMLElement;\n if (target.hasAttribute(\"data-fl-masked\")) {\n this.addLog({ type: \"input\", target: describeElement(target), value: \"[MASKED]\" });\n } else {\n const value = (target as HTMLInputElement).value;\n this.addLog({\n type: \"input\",\n target: describeElement(target),\n value: value?.slice(0, 100),\n });\n }\n }\n\n private captureSnapshot(): void {\n try {\n const clone = document.documentElement.cloneNode(true) as HTMLElement;\n if (this.maskSelector) {\n const masked = clone.querySelectorAll(this.maskSelector);\n for (const el of masked) {\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.value = \"***\";\n }\n el.textContent = \"***\";\n }\n }\n const scripts = clone.querySelectorAll(\"script\");\n for (const s of scripts) s.remove();\n\n const sdkHost = clone.querySelector(\"#firstlook-sdk-root\");\n sdkHost?.remove();\n\n const html = clone.outerHTML;\n if (html.length > MAX_SNAPSHOT_SIZE) return;\n if (this.snapshots.length >= MAX_SNAPSHOTS) {\n this.snapshots.shift();\n }\n this.snapshots.push({\n type: \"dom-snapshot\",\n timestamp: Date.now() - this.startTime,\n data: html,\n });\n } catch {\n // Snapshot failure is non-critical\n }\n }\n\n private throttle<T extends (...args: unknown[]) => void>(fn: T, ms: number): T {\n let last = 0;\n return ((...args: unknown[]) => {\n const now = Date.now();\n if (now - last >= ms) {\n last = now;\n fn(...args);\n }\n }) as T;\n }\n}\n\nfunction describeElement(el: HTMLElement): string {\n const tag = el.tagName.toLowerCase();\n const id = el.id ? `#${el.id}` : \"\";\n const classes = el.className && typeof el.className === \"string\"\n ? `.${el.className.trim().split(/\\s+/).slice(0, 2).join(\".\")}`\n : \"\";\n const text = el.textContent?.trim().slice(0, 30) || \"\";\n return `${tag}${id}${classes}${text ? ` \"${text}\"` : \"\"}`;\n}\n","import type { EventBus } from \"../core/event-bus.js\";\n\n/**\n * Voice annotation recorder using the MediaRecorder API.\n * Captures microphone input for \"thinking aloud\" protocol.\n */\nexport class VoiceRecorder {\n private mediaRecorder: MediaRecorder | null = null;\n private stream: MediaStream | null = null;\n private chunks: Blob[] = [];\n private _recording = false;\n\n constructor(private events: EventBus) {}\n\n get recording(): boolean {\n return this._recording;\n }\n\n async start(): Promise<void> {\n if (this._recording) return;\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const mimeType = this.getSupportedMimeType();\n const options: MediaRecorderOptions = mimeType ? { mimeType } : {};\n this.mediaRecorder = new MediaRecorder(this.stream, options);\n this.chunks = [];\n\n this.mediaRecorder.ondataavailable = (e) => {\n if (e.data.size > 0) {\n this.chunks.push(e.data);\n }\n };\n\n this.mediaRecorder.start(1000);\n this._recording = true;\n } catch (err) {\n console.warn(\"[FirstLook] Voice recording unavailable:\", err);\n this.events.emit({\n type: \"error\",\n error: new Error(`Voice recording failed: ${(err as Error).message}`),\n });\n }\n }\n\n async stopAsync(): Promise<Blob | null> {\n if (!this._recording || !this.mediaRecorder) return null;\n\n return new Promise<Blob | null>((resolve) => {\n this.mediaRecorder!.onstop = () => {\n const blob = this.chunks.length > 0\n ? new Blob(this.chunks, { type: this.mediaRecorder?.mimeType || \"audio/webm\" })\n : null;\n this.cleanup();\n resolve(blob);\n };\n this.mediaRecorder!.stop();\n });\n }\n\n async captureSnippet(durationMs: number = 10000): Promise<Blob | null> {\n await this.start();\n return new Promise((resolve) => {\n setTimeout(async () => {\n const blob = await this.stopAsync();\n resolve(blob);\n }, durationMs);\n });\n }\n\n private cleanup(): void {\n this._recording = false;\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n this.mediaRecorder = null;\n this.chunks = [];\n }\n\n private getSupportedMimeType(): string {\n const types = [\n \"audio/webm;codecs=opus\",\n \"audio/webm\",\n \"audio/ogg;codecs=opus\",\n \"audio/mp4\",\n ];\n for (const type of types) {\n if (typeof MediaRecorder !== \"undefined\" && MediaRecorder.isTypeSupported(type)) return type;\n }\n return \"\";\n }\n}\n","import type { ResolvedConfig } from \"../core/config.js\";\n\n/**\n * Dynamic watermark renderer.\n * Tiles UserID + Timestamp + IP across the viewport at near-invisible opacity.\n */\nexport class Watermark {\n private container: HTMLDivElement | null = null;\n private refreshInterval: ReturnType<typeof setInterval> | null = null;\n private cachedIp: string | null = null;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private config: ResolvedConfig,\n ) {}\n\n show(): void {\n if (this.container) return;\n this.container = document.createElement(\"div\");\n this.container.className = \"fl-watermark\";\n this.renderTiles();\n this.shadowRoot.appendChild(this.container);\n\n this.fetchIp().then(() => this.renderTiles());\n this.refreshInterval = setInterval(() => this.renderTiles(), 60_000);\n }\n\n hide(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n if (this.refreshInterval) {\n clearInterval(this.refreshInterval);\n this.refreshInterval = null;\n }\n }\n\n private async fetchIp(): Promise<void> {\n if (this.cachedIp) return;\n try {\n const response = await fetch(\"https://api.ipify.org?format=text\");\n if (response.ok) {\n this.cachedIp = await response.text();\n }\n } catch {\n this.cachedIp = \"N/A\";\n }\n }\n\n private renderTiles(): void {\n if (!this.container) return;\n this.container.innerHTML = \"\";\n\n const ip = this.cachedIp ?? \"\";\n const timestamp = new Date().toISOString().slice(0, 19);\n const text = ip\n ? `${this.config.userId} | ${timestamp} | ${ip}`\n : `${this.config.userId} | ${timestamp}`;\n\n const tileWidth = 320;\n const tileHeight = 80;\n const cols = Math.ceil(window.innerWidth / tileWidth) + 1;\n const rows = Math.ceil(window.innerHeight / tileHeight) + 1;\n\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const tile = document.createElement(\"span\");\n tile.className = \"fl-watermark-tile\";\n tile.textContent = text;\n tile.style.left = `${col * tileWidth}px`;\n tile.style.top = `${row * tileHeight}px`;\n this.container.appendChild(tile);\n }\n }\n }\n}\n","/**\n * Sensitive field masking.\n * Observes DOM for fields matching mask selectors and overlays them\n * in recording snapshots.\n */\nexport class FieldMasker {\n private observer: MutationObserver | null = null;\n private maskedElements = new Set<Element>();\n\n constructor(private selectors: string[]) {}\n\n start(): void {\n this.scanAndMark();\n this.observer = new MutationObserver(() => this.scanAndMark());\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"type\", \"class\", \"data-sensitive\", \"data-mask\"],\n });\n }\n\n stop(): void {\n this.observer?.disconnect();\n this.observer = null;\n for (const el of this.maskedElements) {\n el.removeAttribute(\"data-fl-masked\");\n }\n this.maskedElements.clear();\n }\n\n isMasked(element: Element): boolean {\n return element.hasAttribute(\"data-fl-masked\");\n }\n\n getCombinedSelector(): string {\n return this.selectors.join(\", \");\n }\n\n private scanAndMark(): void {\n const combinedSelector = this.getCombinedSelector();\n if (!combinedSelector) return;\n\n try {\n const elements = document.querySelectorAll(combinedSelector);\n for (const el of elements) {\n if (!this.maskedElements.has(el)) {\n el.setAttribute(\"data-fl-masked\", \"true\");\n this.maskedElements.add(el);\n }\n }\n for (const el of this.maskedElements) {\n if (!document.contains(el)) {\n this.maskedElements.delete(el);\n }\n }\n } catch {\n // Invalid selector — skip gracefully\n }\n }\n}\n","import type { SessionData, AnnotationData, RecordingChunk } from \"@firstlook-uat/shared\";\n\nconst DB_NAME = \"firstlook_uat\";\nconst DB_VERSION = 1;\n\nconst STORES = {\n sessions: \"sessions\",\n recordings: \"recordings\",\n annotations: \"annotations\",\n uploadQueue: \"upload_queue\",\n} as const;\n\n/**\n * IndexedDB-backed persistence layer.\n * Buffers session data locally and manages the upload queue.\n */\nexport class UATStorage {\n private db: IDBDatabase | null = null;\n\n async open(): Promise<void> {\n if (this.db) return;\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(STORES.sessions)) {\n db.createObjectStore(STORES.sessions, { keyPath: \"sessionId\" });\n }\n if (!db.objectStoreNames.contains(STORES.recordings)) {\n const store = db.createObjectStore(STORES.recordings, { autoIncrement: true });\n store.createIndex(\"sessionId\", \"sessionId\", { unique: false });\n }\n if (!db.objectStoreNames.contains(STORES.annotations)) {\n const store = db.createObjectStore(STORES.annotations, { autoIncrement: true });\n store.createIndex(\"sessionId\", \"sessionId\", { unique: false });\n }\n if (!db.objectStoreNames.contains(STORES.uploadQueue)) {\n db.createObjectStore(STORES.uploadQueue, { autoIncrement: true });\n }\n };\n request.onsuccess = () => {\n this.db = request.result;\n resolve();\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n async saveSession(session: SessionData): Promise<void> {\n await this.put(STORES.sessions, session);\n }\n\n async getSession(sessionId: string): Promise<SessionData | undefined> {\n return this.get(STORES.sessions, sessionId);\n }\n\n async saveRecording(sessionId: string, chunk: RecordingChunk): Promise<void> {\n await this.put(STORES.recordings, { sessionId, ...chunk });\n }\n\n async saveAnnotation(sessionId: string, annotation: AnnotationData): Promise<void> {\n await this.put(STORES.annotations, { sessionId, ...annotation });\n }\n\n async getAnnotations(sessionId: string): Promise<AnnotationData[]> {\n return this.getAllByIndex(STORES.annotations, \"sessionId\", sessionId);\n }\n\n async enqueueUpload(payload: unknown): Promise<void> {\n await this.put(STORES.uploadQueue, {\n payload,\n createdAt: Date.now(),\n attempts: 0,\n });\n }\n\n async getPendingUploads(): Promise<Array<{ key: IDBValidKey; payload: unknown; attempts: number; lastAttemptAt?: number }>> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORES.uploadQueue, \"readonly\");\n const store = tx.objectStore(STORES.uploadQueue);\n const request = store.openCursor();\n const results: Array<{ key: IDBValidKey; payload: unknown; attempts: number; lastAttemptAt?: number }> = [];\n request.onsuccess = () => {\n const cursor = request.result;\n if (cursor) {\n results.push({\n key: cursor.key,\n payload: cursor.value.payload,\n attempts: cursor.value.attempts,\n lastAttemptAt: cursor.value.lastAttemptAt,\n });\n cursor.continue();\n } else {\n resolve(results);\n }\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n async removeFromQueue(key: IDBValidKey): Promise<void> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORES.uploadQueue, \"readwrite\");\n const request = tx.objectStore(STORES.uploadQueue).delete(key);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n async incrementAttempts(key: IDBValidKey): Promise<void> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORES.uploadQueue, \"readwrite\");\n const store = tx.objectStore(STORES.uploadQueue);\n const getReq = store.get(key);\n getReq.onsuccess = () => {\n const record = getReq.result;\n if (record) {\n record.attempts = (record.attempts ?? 0) + 1;\n record.lastAttemptAt = Date.now();\n store.put(record, key);\n }\n };\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async clearSession(sessionId: string): Promise<void> {\n const db = this.ensureDb();\n const tx = db.transaction(\n [STORES.sessions, STORES.recordings, STORES.annotations],\n \"readwrite\",\n );\n tx.objectStore(STORES.sessions).delete(sessionId);\n for (const storeName of [STORES.recordings, STORES.annotations] as const) {\n const store = tx.objectStore(storeName);\n const index = store.index(\"sessionId\");\n const req = index.openCursor(IDBKeyRange.only(sessionId));\n req.onsuccess = () => {\n const cursor = req.result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n }\n };\n }\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n close(): void {\n this.db?.close();\n this.db = null;\n }\n\n private ensureDb(): IDBDatabase {\n if (!this.db) throw new Error(\"[FirstLook] Storage not opened. Call open() first.\");\n return this.db;\n }\n\n private async put(storeName: string, value: unknown): Promise<void> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(storeName, \"readwrite\");\n tx.objectStore(storeName).put(value);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n private async get<T>(storeName: string, key: IDBValidKey): Promise<T | undefined> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(storeName, \"readonly\");\n const request = tx.objectStore(storeName).get(key);\n request.onsuccess = () => resolve(request.result as T | undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n private async getAllByIndex<T>(storeName: string, indexName: string, key: IDBValidKey): Promise<T[]> {\n const db = this.ensureDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(storeName, \"readonly\");\n const index = tx.objectStore(storeName).index(indexName);\n const request = index.getAll(key);\n request.onsuccess = () => resolve(request.result as T[]);\n request.onerror = () => reject(request.error);\n });\n }\n}\n","import type { AnnotationData, DrawingPath } from \"@firstlook-uat/shared\";\nimport { el } from \"../ui/dom-helpers.js\";\n\nconst COLORS = [\"#e17055\", \"#6c5ce7\", \"#00b894\", \"#fdcb6e\", \"#ffffff\"];\n\n/**\n * Full-screen annotation overlay.\n * Captures a screenshot, allows drawing on it, and collects a text comment.\n */\nexport class AnnotationCanvas {\n private overlay: HTMLDivElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private ctx: CanvasRenderingContext2D | null = null;\n private drawing = false;\n private currentPath: Array<{ x: number; y: number }> = [];\n private paths: DrawingPath[] = [];\n private selectedColor = COLORS[0];\n private screenshotDataUrl = \"\";\n\n constructor(private shadowRoot: ShadowRoot) {}\n\n async open(): Promise<AnnotationData | null> {\n this.screenshotDataUrl = await this.captureScreenshot();\n\n return new Promise((resolve) => {\n this.overlay = el(\"div\", { className: \"fl-annotation-overlay\" }) as HTMLDivElement;\n\n const canvasWrap = el(\"div\", { className: \"fl-annotation-canvas-wrap\" });\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"fl-annotation-canvas\";\n canvasWrap.appendChild(this.canvas);\n this.overlay.appendChild(canvasWrap);\n\n const commentInput = el(\"input\", {\n className: \"fl-annotation-comment\",\n type: \"text\",\n placeholder: \"Add a comment...\",\n }) as HTMLInputElement;\n\n const colorPicker = el(\"div\", { className: \"fl-color-picker\" });\n for (const color of COLORS) {\n const swatch = el(\"div\", { className: `fl-color-swatch ${color === this.selectedColor ? \"fl-selected\" : \"\"}` });\n swatch.style.background = color;\n swatch.onclick = () => {\n this.selectedColor = color;\n colorPicker.querySelectorAll(\".fl-color-swatch\").forEach((s) => s.classList.remove(\"fl-selected\"));\n swatch.classList.add(\"fl-selected\");\n };\n colorPicker.appendChild(swatch);\n }\n\n const submitBtn = el(\"button\", { className: \"fl-btn fl-btn-ok\" }, [\"Submit\"]) as HTMLButtonElement;\n submitBtn.onclick = () => {\n const result: AnnotationData = {\n screenshotDataUrl: this.getAnnotatedImage(),\n drawings: [...this.paths],\n comment: commentInput.value,\n timestamp: Date.now(),\n };\n this.close();\n resolve(result);\n };\n\n const cancelBtn = el(\"button\", { className: \"fl-btn fl-btn-ng\" }, [\"Cancel\"]) as HTMLButtonElement;\n cancelBtn.onclick = () => {\n this.close();\n resolve(null);\n };\n\n const toolbar = el(\"div\", { className: \"fl-annotation-toolbar\" }, [\n colorPicker,\n commentInput,\n submitBtn,\n cancelBtn,\n ]);\n this.overlay.appendChild(toolbar);\n\n this.shadowRoot.appendChild(this.overlay);\n this.initCanvas();\n });\n }\n\n private close(): void {\n this.overlay?.remove();\n this.overlay = null;\n this.canvas = null;\n this.ctx = null;\n this.paths = [];\n this.currentPath = [];\n }\n\n private async captureScreenshot(): Promise<string> {\n try {\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n const clone = document.documentElement.cloneNode(true) as HTMLElement;\n const sdkRoot = clone.querySelector(\"#firstlook-sdk-root\");\n sdkRoot?.remove();\n const masked = clone.querySelectorAll('[data-fl-masked], input[type=\"password\"]');\n for (const el of masked) {\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.value = \"[MASKED]\";\n }\n el.textContent = \"[MASKED]\";\n }\n for (const s of clone.querySelectorAll(\"script\")) s.remove();\n\n const bodyClone = clone.querySelector(\"body\");\n if (bodyClone) {\n const bodyStyles = window.getComputedStyle(document.body);\n bodyClone.style.backgroundColor = bodyStyles.backgroundColor;\n bodyClone.style.color = bodyStyles.color;\n bodyClone.style.fontFamily = bodyStyles.fontFamily;\n bodyClone.style.margin = \"0\";\n bodyClone.style.overflow = \"hidden\";\n }\n\n const htmlString = new XMLSerializer().serializeToString(clone);\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\">\n <foreignObject width=\"100%\" height=\"100%\">\n ${htmlString}\n </foreignObject>\n </svg>`;\n\n const blob = new Blob([svg], { type: \"image/svg+xml;charset=utf-8\" });\n const url = URL.createObjectURL(blob);\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = width * window.devicePixelRatio;\n canvas.height = height * window.devicePixelRatio;\n const ctx = canvas.getContext(\"2d\")!;\n ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n\n return new Promise<string>((resolve) => {\n const img = new Image();\n img.onload = () => {\n ctx.drawImage(img, 0, 0, width, height);\n URL.revokeObjectURL(url);\n resolve(canvas.toDataURL(\"image/png\"));\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve(this.captureScreenshotFallback(width, height));\n };\n img.src = url;\n });\n } catch {\n return this.captureScreenshotFallback(window.innerWidth, window.innerHeight);\n }\n }\n\n private captureScreenshotFallback(width: number, height: number): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\")!;\n\n const bodyStyles = window.getComputedStyle(document.body);\n ctx.fillStyle = bodyStyles.backgroundColor || \"#ffffff\";\n ctx.fillRect(0, 0, width, height);\n\n ctx.fillStyle = \"#333\";\n ctx.font = \"14px sans-serif\";\n ctx.fillText(`URL: ${window.location.href}`, 20, 30);\n ctx.fillText(`Time: ${new Date().toISOString()}`, 20, 50);\n ctx.fillText(\"(SVG capture unavailable — metadata view)\", 20, 70);\n\n return canvas.toDataURL(\"image/png\");\n }\n\n private initCanvas(): void {\n if (!this.canvas) return;\n const img = new Image();\n img.onload = () => {\n const maxW = window.innerWidth * 0.9;\n const maxH = window.innerHeight * 0.7;\n const scale = Math.min(maxW / img.width, maxH / img.height, 1);\n this.canvas!.width = img.width * scale;\n this.canvas!.height = img.height * scale;\n\n this.ctx = this.canvas!.getContext(\"2d\")!;\n this.ctx.drawImage(img, 0, 0, this.canvas!.width, this.canvas!.height);\n\n this.canvas!.addEventListener(\"pointerdown\", this.onDrawStart.bind(this));\n this.canvas!.addEventListener(\"pointermove\", this.onDrawMove.bind(this));\n this.canvas!.addEventListener(\"pointerup\", this.onDrawEnd.bind(this));\n this.canvas!.addEventListener(\"pointerleave\", this.onDrawEnd.bind(this));\n };\n img.src = this.screenshotDataUrl;\n }\n\n private onDrawStart(e: PointerEvent): void {\n this.drawing = true;\n const rect = this.canvas!.getBoundingClientRect();\n this.currentPath = [{ x: e.clientX - rect.left, y: e.clientY - rect.top }];\n }\n\n private onDrawMove(e: PointerEvent): void {\n if (!this.drawing || !this.ctx) return;\n const rect = this.canvas!.getBoundingClientRect();\n const point = { x: e.clientX - rect.left, y: e.clientY - rect.top };\n this.currentPath.push(point);\n\n this.ctx.strokeStyle = this.selectedColor;\n this.ctx.lineWidth = 3;\n this.ctx.lineCap = \"round\";\n this.ctx.lineJoin = \"round\";\n\n if (this.currentPath.length >= 2) {\n const prev = this.currentPath[this.currentPath.length - 2];\n this.ctx.beginPath();\n this.ctx.moveTo(prev.x, prev.y);\n this.ctx.lineTo(point.x, point.y);\n this.ctx.stroke();\n }\n }\n\n private onDrawEnd(): void {\n if (this.drawing && this.currentPath.length > 0) {\n this.paths.push({\n points: [...this.currentPath],\n color: this.selectedColor,\n width: 3,\n });\n }\n this.drawing = false;\n this.currentPath = [];\n }\n\n private getAnnotatedImage(): string {\n return this.canvas?.toDataURL(\"image/png\") || this.screenshotDataUrl;\n }\n}\n","/**\n * Device shake detection trigger.\n * Uses the DeviceMotionEvent API to detect shake gestures.\n */\nexport class ShakeTrigger {\n private lastX = 0;\n private lastY = 0;\n private lastZ = 0;\n private shakeCount = 0;\n private lastShakeTime = 0;\n private handler: ((e: DeviceMotionEvent) => void) | null = null;\n private readonly THRESHOLD = 15;\n private readonly SHAKE_INTERVAL = 400;\n private readonly REQUIRED_SHAKES = 2;\n\n constructor(private onShake: () => void) {}\n\n async start(): Promise<void> {\n if (typeof (DeviceMotionEvent as any).requestPermission === \"function\") {\n try {\n const permission = await (DeviceMotionEvent as any).requestPermission();\n if (permission !== \"granted\") return;\n } catch {\n return;\n }\n }\n\n this.handler = this.onMotion.bind(this);\n window.addEventListener(\"devicemotion\", this.handler, { passive: true });\n }\n\n stop(): void {\n if (this.handler) {\n window.removeEventListener(\"devicemotion\", this.handler);\n this.handler = null;\n }\n }\n\n private onMotion(event: DeviceMotionEvent): void {\n const acc = event.accelerationIncludingGravity;\n if (!acc || acc.x == null || acc.y == null || acc.z == null) return;\n\n const deltaX = Math.abs(acc.x - this.lastX);\n const deltaY = Math.abs(acc.y - this.lastY);\n const deltaZ = Math.abs(acc.z - this.lastZ);\n\n if (deltaX + deltaY + deltaZ > this.THRESHOLD) {\n const now = Date.now();\n if (now - this.lastShakeTime < this.SHAKE_INTERVAL) {\n this.shakeCount++;\n if (this.shakeCount >= this.REQUIRED_SHAKES) {\n this.shakeCount = 0;\n this.onShake();\n }\n } else {\n this.shakeCount = 1;\n }\n this.lastShakeTime = now;\n }\n\n this.lastX = acc.x;\n this.lastY = acc.y;\n this.lastZ = acc.z;\n }\n}\n","import type { AnnotationData } from \"@firstlook-uat/shared\";\nimport type { EventBus } from \"../core/event-bus.js\";\nimport { AnnotationCanvas } from \"./annotation-canvas.js\";\nimport { ShakeTrigger } from \"../triggers/shake-trigger.js\";\n\n/**\n * Shake-to-report: when the user shakes the device (or triggers via keyboard shortcut),\n * the screen pauses and an annotation overlay appears for drawing + commenting.\n */\nexport class ShakeReporter {\n private shakeTrigger: ShakeTrigger;\n private annotationCanvas: AnnotationCanvas;\n private annotations: AnnotationData[] = [];\n private active = false;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private events: EventBus,\n ) {\n this.annotationCanvas = new AnnotationCanvas(shadowRoot);\n this.shakeTrigger = new ShakeTrigger(() => this.trigger());\n }\n\n async start(): Promise<void> {\n await this.shakeTrigger.start();\n document.addEventListener(\"keydown\", this.onKeydown);\n }\n\n stop(): void {\n this.shakeTrigger.stop();\n document.removeEventListener(\"keydown\", this.onKeydown);\n }\n\n getAnnotations(): AnnotationData[] {\n return [...this.annotations];\n }\n\n private onKeydown = (e: KeyboardEvent): void => {\n if (e.ctrlKey && e.shiftKey && e.key === \"R\") {\n e.preventDefault();\n this.trigger();\n }\n };\n\n private async trigger(): Promise<void> {\n if (this.active) return;\n this.active = true;\n\n try {\n const annotation = await this.annotationCanvas.open();\n if (annotation) {\n this.annotations.push(annotation);\n this.events.emit({ type: \"report:submitted\" });\n }\n } finally {\n this.active = false;\n }\n }\n}\n","/**\n * Multi-tap trigger for activating the debug menu.\n * Detects N rapid taps/clicks on the same screen region.\n */\nexport class TapTrigger {\n private tapCount = 0;\n private tapTimer: ReturnType<typeof setTimeout> | null = null;\n private handler: ((e: PointerEvent) => void) | null = null;\n\n constructor(\n private requiredTaps: number,\n private onActivate: () => void,\n ) {}\n\n start(): void {\n this.handler = this.onTap.bind(this);\n document.addEventListener(\"pointerdown\", this.handler, { passive: true });\n }\n\n stop(): void {\n if (this.handler) {\n document.removeEventListener(\"pointerdown\", this.handler);\n this.handler = null;\n }\n this.reset();\n }\n\n private onTap(_e: PointerEvent): void {\n this.tapCount++;\n if (this.tapTimer) clearTimeout(this.tapTimer);\n\n if (this.tapCount >= this.requiredTaps) {\n this.reset();\n this.onActivate();\n } else {\n this.tapTimer = setTimeout(() => this.reset(), 800);\n }\n }\n\n private reset(): void {\n this.tapCount = 0;\n if (this.tapTimer) {\n clearTimeout(this.tapTimer);\n this.tapTimer = null;\n }\n }\n}\n","const STORAGE_KEY = \"firstlook:uat-active\";\n\n/**\n * Deep link / URL-based activation trigger.\n * Checks for ?uat=1, ?firstlook=1, or ?uat-mode=1 in the URL.\n * Persists the flag to sessionStorage so it survives page navigations.\n */\nexport class DeepLinkTrigger {\n private hashHandler: (() => void) | null = null;\n\n constructor(private onActivate: () => void) {}\n\n start(): void {\n if (this.checkUrl()) {\n this.persist();\n this.onActivate();\n return;\n }\n // Check sessionStorage for persisted flag (survives page navigations)\n if (this.hasPersisted()) {\n this.onActivate();\n return;\n }\n this.hashHandler = () => {\n if (this.checkUrl()) {\n this.persist();\n this.onActivate();\n }\n };\n window.addEventListener(\"hashchange\", this.hashHandler);\n }\n\n stop(): void {\n if (this.hashHandler) {\n window.removeEventListener(\"hashchange\", this.hashHandler);\n this.hashHandler = null;\n }\n }\n\n /** Clear the persisted flag (call on session end / destroy). */\n static clearPersisted(): void {\n try {\n sessionStorage.removeItem(STORAGE_KEY);\n } catch { /* storage unavailable */ }\n }\n\n private checkUrl(): boolean {\n const params = new URLSearchParams(window.location.search);\n return params.has(\"firstlook\") || params.has(\"uat-mode\") || params.get(\"uat\") === \"1\";\n }\n\n private persist(): void {\n try {\n sessionStorage.setItem(STORAGE_KEY, \"1\");\n } catch { /* storage unavailable */ }\n }\n\n private hasPersisted(): boolean {\n try {\n return sessionStorage.getItem(STORAGE_KEY) === \"1\";\n } catch {\n return false;\n }\n }\n}\n","/**\n * Keyboard shortcut activation trigger.\n * Activates on Ctrl+Shift+U (or Cmd+Shift+U on Mac).\n */\nexport class KeyboardTrigger {\n private handler: ((e: KeyboardEvent) => void) | null = null;\n\n constructor(private onActivate: () => void) {}\n\n start(): void {\n this.handler = (e: KeyboardEvent) => {\n if (e.key === \"U\" && e.shiftKey && (e.ctrlKey || e.metaKey)) {\n e.preventDefault();\n this.onActivate();\n }\n };\n window.addEventListener(\"keydown\", this.handler);\n }\n\n stop(): void {\n if (this.handler) {\n window.removeEventListener(\"keydown\", this.handler);\n this.handler = null;\n }\n }\n}\n","import { el } from \"../ui/dom-helpers.js\";\n\nexport interface DebugMenuCallbacks {\n onStartSession: () => void;\n onReportIssue: () => void;\n onClose: () => void;\n}\n\n/**\n * Debug menu rendered inside Shadow DOM after SDK activation.\n * Provides manual control over SDK features.\n */\nexport class DebugMenu {\n private root: HTMLDivElement | null = null;\n private outsideClickHandler: ((e: MouseEvent) => void) | null = null;\n\n constructor(\n private shadowRoot: ShadowRoot,\n private callbacks: DebugMenuCallbacks,\n ) {}\n\n show(): void {\n if (this.root) return;\n\n this.root = el(\"div\", { className: \"fl-debug-menu\" }) as HTMLDivElement;\n // Prevent clicks on the debug menu from reaching the host app\n for (const evt of [\"click\", \"mousedown\", \"mouseup\", \"pointerdown\", \"pointerup\", \"touchstart\", \"touchend\"] as const) {\n this.root.addEventListener(evt, (e) => e.stopPropagation());\n }\n\n const items: Array<{ icon: string; label: string; action: () => void }> = [\n { icon: \"\\u25B6\", label: \"Start Session\", action: this.callbacks.onStartSession },\n { icon: \"\\uD83D\\uDCF8\", label: \"Report Issue\", action: this.callbacks.onReportIssue },\n { icon: \"\\u2716\", label: \"Close SDK\", action: this.callbacks.onClose },\n ];\n\n for (const item of items) {\n const btn = el(\"button\", { className: \"fl-debug-menu-item\" }, [\n el(\"span\", { className: \"fl-debug-menu-item-icon\" }, [item.icon]),\n item.label,\n ]) as HTMLButtonElement;\n btn.onclick = (e) => {\n e.stopPropagation();\n this.hide();\n item.action();\n };\n this.root.appendChild(btn);\n }\n\n this.shadowRoot.appendChild(this.root);\n\n // Close on outside click (delayed to avoid immediate close)\n setTimeout(() => {\n this.outsideClickHandler = (e: MouseEvent) => {\n if (this.root && !this.root.contains(e.target as Node)) {\n this.hide();\n }\n };\n document.addEventListener(\"click\", this.outsideClickHandler, { capture: true });\n }, 100);\n }\n\n hide(): void {\n this.root?.remove();\n this.root = null;\n if (this.outsideClickHandler) {\n document.removeEventListener(\"click\", this.outsideClickHandler, { capture: true });\n this.outsideClickHandler = null;\n }\n }\n\n get visible(): boolean {\n return this.root !== null;\n }\n}\n","/**\n * CSS styles injected into the Shadow DOM root.\n * Completely isolated from the host application.\n */\nexport const SDK_STYLES = /* css */ `\n :host {\n all: initial;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #1a1a2e;\n line-height: 1.5;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n /* === Quest Overlay === */\n .fl-quest-overlay {\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 2147483647;\n pointer-events: auto;\n width: 360px;\n max-width: calc(100vw - 48px);\n background: #ffffff;\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16), 0 2px 8px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n animation: fl-slide-up 0.3s ease-out;\n border: 1px solid rgba(0, 0, 0, 0.06);\n }\n\n .fl-quest-overlay.fl-minimized {\n width: 56px;\n height: 56px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #6c5ce7;\n box-shadow: 0 4px 16px rgba(108, 92, 231, 0.4);\n }\n\n .fl-quest-overlay.fl-minimized .fl-quest-content {\n display: none;\n }\n\n .fl-quest-overlay.fl-minimized .fl-minimize-icon {\n display: block;\n color: #fff;\n font-size: 24px;\n }\n\n .fl-minimize-icon {\n display: none;\n }\n\n @keyframes fl-slide-up {\n from { transform: translateY(20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n\n .fl-quest-header {\n background: linear-gradient(135deg, #6c5ce7, #a29bfe);\n color: #fff;\n padding: 14px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n }\n\n .fl-quest-header-left {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .fl-quest-badge {\n background: rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n padding: 2px 10px;\n font-size: 11px;\n font-weight: 600;\n white-space: nowrap;\n letter-spacing: 0.5px;\n }\n\n .fl-quest-title {\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .fl-quest-minimize-btn {\n background: rgba(255, 255, 255, 0.2);\n border: none;\n color: #fff;\n width: 28px;\n height: 28px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n flex-shrink: 0;\n transition: background 0.15s;\n }\n\n .fl-quest-minimize-btn:hover {\n background: rgba(255, 255, 255, 0.3);\n }\n\n .fl-quest-body {\n padding: 16px;\n }\n\n .fl-quest-description {\n font-size: 14px;\n color: #4a4a5a;\n margin-bottom: 16px;\n line-height: 1.6;\n }\n\n .fl-quest-progress {\n display: flex;\n gap: 4px;\n margin-bottom: 16px;\n }\n\n .fl-quest-progress-dot {\n height: 4px;\n flex: 1;\n border-radius: 2px;\n background: #e0e0e0;\n transition: background 0.3s;\n }\n\n .fl-quest-progress-dot.fl-completed { background: #00b894; }\n .fl-quest-progress-dot.fl-failed { background: #e17055; }\n .fl-quest-progress-dot.fl-active { background: #6c5ce7; }\n\n .fl-quest-actions {\n display: flex;\n gap: 10px;\n }\n\n .fl-btn {\n flex: 1;\n padding: 10px 16px;\n border-radius: 10px;\n border: none;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: transform 0.1s, box-shadow 0.15s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n }\n\n .fl-btn:active { transform: scale(0.97); }\n\n .fl-btn-ok {\n background: #00b894;\n color: #fff;\n box-shadow: 0 2px 8px rgba(0, 184, 148, 0.3);\n }\n .fl-btn-ok:hover { box-shadow: 0 4px 12px rgba(0, 184, 148, 0.4); }\n\n .fl-btn-ng {\n background: #e17055;\n color: #fff;\n box-shadow: 0 2px 8px rgba(225, 112, 85, 0.3);\n }\n .fl-btn-ng:hover { box-shadow: 0 4px 12px rgba(225, 112, 85, 0.4); }\n\n /* === Voice recording indicator === */\n .fl-voice-indicator {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n background: #fff3f0;\n border-top: 1px solid rgba(225, 112, 85, 0.1);\n font-size: 12px;\n color: #e17055;\n }\n\n .fl-voice-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #e17055;\n animation: fl-pulse 1.2s ease-in-out infinite;\n }\n\n @keyframes fl-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n }\n\n /* === Annotation overlay === */\n .fl-annotation-overlay {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n pointer-events: auto;\n background: rgba(0, 0, 0, 0.7);\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n animation: fl-fade-in 0.2s ease-out;\n }\n\n @keyframes fl-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .fl-annotation-canvas-wrap {\n position: relative;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n }\n\n .fl-annotation-canvas {\n display: block;\n cursor: crosshair;\n max-width: 90vw;\n max-height: 70vh;\n }\n\n .fl-annotation-toolbar {\n display: flex;\n gap: 8px;\n margin-top: 16px;\n align-items: center;\n }\n\n .fl-annotation-toolbar .fl-btn {\n flex: none;\n padding: 10px 20px;\n }\n\n .fl-annotation-comment {\n width: 300px;\n padding: 10px 14px;\n border-radius: 10px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n font-size: 14px;\n outline: none;\n backdrop-filter: blur(4px);\n transition: border-color 0.15s;\n }\n\n .fl-annotation-comment::placeholder { color: rgba(255, 255, 255, 0.5); }\n .fl-annotation-comment:focus { border-color: #a29bfe; }\n\n .fl-color-picker { display: flex; gap: 4px; }\n\n .fl-color-swatch {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n border: 2px solid transparent;\n cursor: pointer;\n transition: transform 0.1s;\n }\n .fl-color-swatch:hover { transform: scale(1.15); }\n .fl-color-swatch.fl-selected {\n border-color: #fff;\n box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.6);\n }\n\n /* === Watermark === */\n .fl-watermark {\n position: fixed;\n inset: 0;\n z-index: 2147483640;\n pointer-events: none;\n overflow: hidden;\n opacity: 0.03;\n }\n\n .fl-watermark-tile {\n position: absolute;\n font-size: 12px;\n font-family: monospace;\n color: #000;\n white-space: nowrap;\n transform: rotate(-30deg);\n user-select: none;\n }\n\n /* === Debug Menu === */\n .fl-debug-menu {\n position: fixed;\n bottom: 100px;\n right: 24px;\n z-index: 2147483646;\n pointer-events: auto;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n padding: 8px;\n min-width: 200px;\n animation: fl-slide-up 0.2s ease-out;\n border: 1px solid rgba(0, 0, 0, 0.06);\n }\n\n .fl-debug-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border: none;\n background: none;\n width: 100%;\n text-align: left;\n border-radius: 8px;\n cursor: pointer;\n font-size: 14px;\n color: #1a1a2e;\n transition: background 0.15s;\n }\n .fl-debug-menu-item:hover { background: #f0f0f8; }\n\n .fl-debug-menu-item-icon {\n font-size: 18px;\n width: 24px;\n text-align: center;\n }\n\n /* === Session status bar === */\n .fl-status-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 16px;\n background: #f8f8fc;\n border-top: 1px solid rgba(0, 0, 0, 0.04);\n font-size: 11px;\n color: #888;\n }\n\n .fl-status-recording {\n display: flex;\n align-items: center;\n gap: 4px;\n color: #e17055;\n }\n\n .fl-status-timer {\n margin-left: auto;\n font-variant-numeric: tabular-nums;\n }\n\n /* === Completion summary === */\n .fl-summary { padding: 20px 16px; text-align: center; }\n .fl-summary-icon { font-size: 48px; margin-bottom: 12px; }\n .fl-summary-title { font-size: 18px; font-weight: 700; color: #1a1a2e; margin-bottom: 4px; }\n .fl-summary-subtitle { font-size: 13px; color: #888; margin-bottom: 16px; }\n .fl-summary-stats { display: flex; justify-content: center; gap: 24px; margin-bottom: 16px; }\n .fl-stat { text-align: center; }\n .fl-stat-value { font-size: 24px; font-weight: 700; }\n .fl-stat-value.fl-ok { color: #00b894; }\n .fl-stat-value.fl-ng { color: #e17055; }\n .fl-stat-label { font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 0.5px; }\n\n .fl-btn-finish {\n background: #6c5ce7;\n color: #fff;\n width: 100%;\n box-shadow: 0 2px 8px rgba(108, 92, 231, 0.3);\n }\n\n .fl-btn-skip {\n background: #636e72;\n color: #fff;\n box-shadow: 0 2px 8px rgba(99, 110, 114, 0.3);\n }\n\n /* === Feedback modal header variants === */\n .fl-quest-header-ng { background: linear-gradient(135deg, #e17055, #d63031); }\n .fl-badge-ng { background: rgba(255, 255, 255, 0.25); }\n\n .fl-quest-header-ok { background: linear-gradient(135deg, #00b894, #00a381); }\n .fl-badge-ok { background: rgba(255, 255, 255, 0.25); }\n\n .fl-quest-header-concern { background: linear-gradient(135deg, #fdcb6e, #e17055); }\n .fl-badge-concern { background: rgba(255, 255, 255, 0.25); }\n\n .fl-feedback-textarea {\n width: calc(100% - 32px);\n margin: 8px 16px 16px;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid #ddd;\n font-size: 13px;\n font-family: inherit;\n resize: vertical;\n outline: none;\n transition: border-color 0.15s;\n }\n .fl-feedback-textarea:focus { border-color: #6c5ce7; }\n .fl-feedback-textarea-modal { width: calc(100% - 32px); margin: 16px 16px 12px; }\n\n /* === Concern button === */\n .fl-btn-concern {\n background: #fdcb6e;\n color: #2d3436;\n box-shadow: 0 2px 8px rgba(253, 203, 110, 0.3);\n }\n .fl-btn-concern:hover { box-shadow: 0 4px 12px rgba(253, 203, 110, 0.4); }\n\n /* === Memo button === */\n .fl-quest-memo-row { display: flex; margin-bottom: 10px; }\n .fl-btn-memo {\n flex: 1;\n background: #f0f0f8;\n color: #4a4a5a;\n font-size: 14px;\n padding: 10px 16px;\n box-shadow: none;\n border-radius: 10px;\n border: 1px dashed #c8c8d8;\n }\n .fl-btn-memo:hover { background: #e4e4f0; }\n\n /* === Feedback modal === */\n .fl-feedback-modal {\n position: absolute;\n inset: 0;\n background: #fff;\n border-radius: 16px;\n z-index: 10;\n display: flex;\n flex-direction: column;\n animation: fl-fade-in 0.15s ease-out;\n }\n .fl-feedback-modal-actions { padding: 0 16px 16px; }\n`;\n","import type {\n FirstLookConfig,\n Feedback,\n Quest,\n SessionData,\n SDKEventType,\n SDKEventListener,\n} from \"@firstlook-uat/shared\";\nimport { resolveConfig, collectDeviceInfo, type ResolvedConfig } from \"./config.js\";\nimport { EventBus } from \"./event-bus.js\";\nimport { QuestManager } from \"../quest/quest-manager.js\";\nimport { QuestOverlay } from \"../quest/quest-overlay.js\";\nimport { SessionRecorder } from \"../recording/session-recorder.js\";\nimport { VoiceRecorder } from \"../recording/voice-recorder.js\";\nimport { Watermark } from \"../security/watermark.js\";\nimport { FieldMasker } from \"../security/masking.js\";\nimport { UATStorage } from \"../persistence/storage.js\";\nimport { ShakeReporter } from \"../reporting/shake-reporter.js\";\nimport { TapTrigger } from \"../triggers/tap-trigger.js\";\nimport { DeepLinkTrigger } from \"../triggers/deep-link.js\";\nimport { KeyboardTrigger } from \"../triggers/keyboard-trigger.js\";\nimport { DebugMenu } from \"./debug-menu.js\";\nimport { SDK_STYLES } from \"../ui/styles.js\";\nimport { generateId } from \"../ui/dom-helpers.js\";\n\nexport type SDKState = \"idle\" | \"initialized\" | \"active\" | \"recording\" | \"finished\";\n\nconst MAX_UPLOAD_RETRIES = 5;\n\n/**\n * FirstLookSDK — Main entry point for the UAT SDK.\n *\n * Lifecycle:\n * init(config) -> [trigger activation] -> startSession(quests) -> [quest flow] -> endSession()\n *\n * All UI is rendered inside a Shadow DOM to avoid style conflicts with the host app.\n */\nexport class FirstLookSDK {\n private config: ResolvedConfig | null = null;\n private events = new EventBus();\n private state: SDKState = \"idle\";\n\n // Shadow DOM root\n private hostElement: HTMLDivElement | null = null;\n private shadowRoot: ShadowRoot | null = null;\n\n // Modules (initialized lazily after init())\n private questManager: QuestManager | null = null;\n private questOverlay: QuestOverlay | null = null;\n private sessionRecorder: SessionRecorder | null = null;\n private voiceRecorder: VoiceRecorder | null = null;\n private watermark: Watermark | null = null;\n private fieldMasker: FieldMasker | null = null;\n private storage: UATStorage | null = null;\n private shakeReporter: ShakeReporter | null = null;\n private debugMenu: DebugMenu | null = null;\n\n // Triggers\n private tapTrigger: TapTrigger | null = null;\n private deepLinkTrigger: DeepLinkTrigger | null = null;\n private keyboardTrigger: KeyboardTrigger | null = null;\n\n // Session\n private sessionId: string | null = null;\n private sessionStartTime = 0;\n private maxDurationTimer: ReturnType<typeof setTimeout> | null = null;\n private backupTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n\n // ----------------------------------------------------------------\n // Public API\n // ----------------------------------------------------------------\n\n /**\n * Initialize the SDK with configuration.\n * This does NOT activate the UI — it only sets up triggers.\n */\n async init(rawConfig: FirstLookConfig): Promise<void> {\n if (this.state !== \"idle\") {\n console.warn(\"[FirstLook] SDK already initialized.\");\n return;\n }\n\n this.config = resolveConfig(rawConfig);\n\n // Open IndexedDB\n this.storage = new UATStorage();\n await this.storage.open();\n\n // Create Shadow DOM host\n this.createShadowHost();\n\n // Set up activation triggers\n this.setupTriggers();\n\n // Set up field masker\n this.fieldMasker = new FieldMasker(this.config.security.maskSelectors);\n\n this.state = \"initialized\";\n this.events.emit({ type: \"sdk:initialized\" });\n\n // Process any pending uploads from previous sessions (Wi-Fi check)\n this.flushUploadQueue(true);\n }\n\n /**\n * Manually activate the SDK UI (bypassing triggers).\n */\n activate(): void {\n if (this.state !== \"initialized\") {\n console.warn(\"[FirstLook] Cannot activate: SDK not initialized or already active.\");\n return;\n }\n this.onActivate();\n }\n\n /**\n * Start a UAT session with a list of quests.\n */\n async startSession(quests: Quest[]): Promise<string> {\n if (this.state !== \"active\") {\n throw new Error(\"[FirstLook] Cannot start session: SDK not active. Call activate() first.\");\n }\n\n // Hide debug menu if visible\n this.debugMenu?.hide();\n\n this.sessionId = generateId();\n this.sessionStartTime = Date.now();\n\n // Initialize modules\n this.questManager = new QuestManager(this.events);\n this.questManager.loadQuests(quests);\n\n this.sessionRecorder = new SessionRecorder(\n this.config!,\n this.events,\n this.fieldMasker!.getCombinedSelector(),\n );\n\n this.voiceRecorder = new VoiceRecorder(this.events);\n\n this.shakeReporter = new ShakeReporter(this.shadowRoot!, this.events);\n await this.shakeReporter.start();\n\n // Start recording\n this.sessionRecorder.start();\n this.fieldMasker!.start();\n\n // Start voice recording if enabled\n if (this.config!.recording.voice) {\n await this.voiceRecorder.start();\n }\n\n // Show watermark\n if (this.config!.security.watermark) {\n this.watermark = new Watermark(this.shadowRoot!, this.config!);\n this.watermark.show();\n }\n\n // Create quest overlay and start quest flow\n this.questOverlay = new QuestOverlay(this.shadowRoot!, {\n onOkWithFeedback: (comment: string) => this.handleQuestOk(comment),\n onConcern: (comment: string) => this.handleConcern(comment),\n onNg: () => this.handleQuestNg(),\n onNgWithFeedback: (comment: string) => this.handleNgFeedbackSubmit(comment),\n onMemo: (comment: string) => this.handleMemo(comment),\n onFinish: () => this.endSession(),\n onMinimize: () => {},\n });\n\n this.questManager.startSession(this.sessionStartTime);\n this.renderCurrentQuest();\n\n // Max duration timer\n const maxDuration = this.config!.recording.maxDuration;\n if (maxDuration > 0) {\n this.maxDurationTimer = setTimeout(() => this.endSession(), maxDuration * 1000);\n }\n\n // Periodic backup to IndexedDB every 30 seconds\n this.backupTimer = setInterval(() => this.backupSession(), 30_000);\n\n this.state = \"recording\";\n this.events.emit({ type: \"session:started\", sessionId: this.sessionId });\n\n return this.sessionId;\n }\n\n /**\n * Fetch quest definitions from the API and start a session.\n */\n async startSessionFromRemote(questSetId: string): Promise<string> {\n if (this.state !== \"active\") {\n throw new Error(\"[FirstLook] Cannot start session: SDK not active. Call activate() first.\");\n }\n if (!this.config) {\n throw new Error(\"[FirstLook] SDK not initialized.\");\n }\n\n const baseUrl = this.config.endpoint.replace(/\\/ingest-session$/, \"\");\n const response = await fetch(`${baseUrl}/export-quests?id=${encodeURIComponent(questSetId)}`, {\n headers: { \"X-API-Key\": this.config.apiKey },\n });\n\n if (!response.ok) {\n throw new Error(`[FirstLook] Failed to fetch quest set: ${response.status}`);\n }\n\n const data = await response.json();\n const quests: Quest[] = data.quests;\n return this.startSession(quests);\n }\n\n /**\n * End the current session, persist data, and trigger upload.\n */\n async endSession(): Promise<SessionData | null> {\n if (this.state !== \"recording\" || !this.sessionId) return null;\n\n // Clear timers\n if (this.maxDurationTimer) {\n clearTimeout(this.maxDurationTimer);\n this.maxDurationTimer = null;\n }\n if (this.backupTimer) {\n clearInterval(this.backupTimer);\n this.backupTimer = null;\n }\n\n // Stop recording\n this.sessionRecorder?.stop();\n await this.voiceRecorder?.stopAsync();\n this.shakeReporter?.stop();\n this.fieldMasker?.stop();\n this.watermark?.hide();\n\n // Convert voiceMemoBlob -> base64 for all quest results and feedbacks before upload\n const results = this.questManager!.getResults();\n await Promise.allSettled(\n results.map(async (r) => {\n if (r.voiceMemoBlob) {\n try {\n r.voiceMemoBase64 = await this.blobToBase64(r.voiceMemoBlob);\n } catch { /* skip failed conversion */ }\n delete (r as unknown as Record<string, unknown>).voiceMemoBlob;\n }\n await Promise.allSettled(\n r.feedbacks.map(async (f) => {\n if (f.voiceMemoBlob) {\n try {\n f.voiceMemoBase64 = await this.blobToBase64(f.voiceMemoBlob);\n } catch { /* skip failed conversion */ }\n delete (f as unknown as Record<string, unknown>).voiceMemoBlob;\n }\n }),\n );\n }),\n );\n\n // Build session data\n const session: SessionData = {\n sessionId: this.sessionId,\n projectId: this.config!.projectId,\n userId: this.config!.userId,\n role: this.config!.role,\n deviceInfo: collectDeviceInfo(),\n startedAt: new Date(this.sessionStartTime).toISOString(),\n endedAt: new Date().toISOString(),\n duration: Math.floor((Date.now() - this.sessionStartTime) / 1000),\n quests: results,\n recordings: this.sessionRecorder?.getSnapshots() ?? [],\n };\n\n // Persist to IndexedDB\n await this.storage?.saveSession(session);\n\n // Save annotations\n const annotations = this.shakeReporter?.getAnnotations() ?? [];\n for (const annotation of annotations) {\n await this.storage?.saveAnnotation(this.sessionId, annotation);\n }\n\n // Enqueue for upload\n await this.storage?.enqueueUpload({ session, annotations });\n\n // Clean up overlay\n this.questOverlay?.destroy();\n this.questOverlay = null;\n\n this.state = \"finished\";\n this.events.emit({ type: \"session:ended\", sessionId: this.sessionId });\n\n // Upload immediately on session end (no Wi-Fi check — explicit user action)\n this.flushUploadQueue(false);\n\n return session;\n }\n\n /**\n * Subscribe to SDK events.\n */\n on(type: SDKEventType | \"*\", listener: SDKEventListener): () => void {\n return this.events.on(type, listener);\n }\n\n /**\n * Get current SDK state.\n */\n getState(): SDKState {\n return this.state;\n }\n\n /**\n * Completely destroy the SDK instance and clean up all resources.\n */\n destroy(): void {\n if (this.maxDurationTimer) {\n clearTimeout(this.maxDurationTimer);\n this.maxDurationTimer = null;\n }\n if (this.backupTimer) {\n clearInterval(this.backupTimer);\n this.backupTimer = null;\n }\n this.tapTrigger?.stop();\n this.deepLinkTrigger?.stop();\n this.keyboardTrigger?.stop();\n DeepLinkTrigger.clearPersisted();\n this.sessionRecorder?.stop();\n this.shakeReporter?.stop();\n this.fieldMasker?.stop();\n this.watermark?.hide();\n this.questOverlay?.destroy();\n this.debugMenu?.hide();\n this.hostElement?.remove();\n this.storage?.close();\n this.events.removeAll();\n this.state = \"idle\";\n }\n\n // ----------------------------------------------------------------\n // Private methods\n // ----------------------------------------------------------------\n\n private createShadowHost(): void {\n this.hostElement = document.createElement(\"div\");\n this.hostElement.id = \"firstlook-sdk-root\";\n this.hostElement.style.cssText = \"position:fixed;inset:0;z-index:2147483647;pointer-events:none;\";\n document.body.appendChild(this.hostElement);\n\n this.shadowRoot = this.hostElement.attachShadow({ mode: \"closed\" });\n\n // Inject styles\n const style = document.createElement(\"style\");\n style.textContent = SDK_STYLES;\n this.shadowRoot.appendChild(style);\n }\n\n private setupTriggers(): void {\n const cfg = this.config!.triggers;\n\n // Multi-tap trigger\n this.tapTrigger = new TapTrigger(cfg.tapCount, () => this.onActivate());\n this.tapTrigger.start();\n\n // Deep link trigger\n if (cfg.deepLink) {\n this.deepLinkTrigger = new DeepLinkTrigger(() => this.onActivate());\n this.deepLinkTrigger.start();\n }\n\n // Keyboard shortcut trigger (Ctrl+Shift+U / Cmd+Shift+U)\n if (cfg.keyboard) {\n this.keyboardTrigger = new KeyboardTrigger(() => this.onActivate());\n this.keyboardTrigger.start();\n }\n\n // Custom check\n if (cfg.customCheck && cfg.customCheck()) {\n this.onActivate();\n }\n }\n\n private onActivate(): void {\n if (this.state !== \"initialized\") return;\n this.state = \"active\";\n\n // Stop triggers once activated\n this.tapTrigger?.stop();\n this.deepLinkTrigger?.stop();\n this.keyboardTrigger?.stop();\n\n this.events.emit({ type: \"sdk:activated\" });\n\n // Show debug menu\n this.debugMenu = new DebugMenu(this.shadowRoot!, {\n onStartSession: () => {\n this.events.emit({ type: \"sdk:activated\" });\n },\n onReportIssue: () => {\n this.shakeReporter?.[\"trigger\"]?.();\n },\n onClose: () => {\n this.destroy();\n },\n });\n this.debugMenu.show();\n }\n\n private handleQuestOk(comment: string): void {\n if (!this.questManager || !this.sessionRecorder) return;\n\n const logs = this.sessionRecorder.getActionLogs();\n this.questManager.completeCurrentQuest(logs, {\n comment: comment || undefined,\n });\n this.renderCurrentQuest();\n }\n\n private handleConcern(comment: string): void {\n if (!this.questManager || !this.sessionRecorder) return;\n\n const logs = this.sessionRecorder.getActionLogs();\n this.questManager.completeCurrentQuest(logs, {\n comment,\n concern: true,\n });\n this.renderCurrentQuest();\n }\n\n private handleQuestNg(): void {\n if (!this.questManager || !this.questOverlay) return;\n\n const quest = this.questManager.getCurrentQuest();\n if (!quest) return;\n\n this.questOverlay.renderFeedbackModal(quest.title, \"ng\");\n }\n\n private handleNgFeedbackSubmit(comment: string): void {\n if (!this.questManager || !this.sessionRecorder) return;\n\n const logs = this.sessionRecorder.getActionLogs();\n this.questManager.failCurrentQuest(logs, comment || undefined);\n this.renderCurrentQuest();\n }\n\n private handleMemo(comment: string): void {\n if (!this.questManager || !comment) return;\n\n const feedback: Feedback = {\n comment,\n timestamp: Date.now(),\n relativeTime: Date.now() - this.sessionStartTime,\n };\n this.questManager.addFeedback(feedback);\n }\n\n private renderCurrentQuest(): void {\n if (!this.questManager || !this.questOverlay) return;\n\n const status = this.questManager.getStatus();\n\n if (status.isBlocked) {\n const quest = this.questManager.getCurrentQuest();\n this.questOverlay.renderBlocked(quest?.title ?? \"Unknown\");\n return;\n }\n\n if (status.isFinished) {\n this.questOverlay.renderSummary(status.completed, status.failed, status.total);\n return;\n }\n\n const quest = this.questManager.getCurrentQuest();\n if (quest) {\n this.questOverlay.renderQuest(\n quest,\n this.questManager.getQuestStatuses(),\n this.voiceRecorder?.recording ?? false,\n );\n }\n }\n\n private async flushUploadQueue(checkConnection: boolean): Promise<void> {\n if (!this.storage || !this.config || this.isFlushing) return;\n this.isFlushing = true;\n\n try {\n if (checkConnection) {\n const conn = (navigator as any).connection;\n if (conn && conn.type && conn.type !== \"wifi\" && conn.effectiveType !== \"4g\") {\n return;\n }\n }\n\n const pending = await this.storage.getPendingUploads();\n for (const item of pending) {\n if (item.attempts >= MAX_UPLOAD_RETRIES) {\n await this.storage.removeFromQueue(item.key);\n continue;\n }\n\n if (item.attempts > 0) {\n const backoffMs = Math.pow(2, item.attempts) * 1000;\n const lastAttemptTime = item.lastAttemptAt ?? 0;\n if (Date.now() - lastAttemptTime < backoffMs) {\n continue;\n }\n }\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n },\n body: JSON.stringify(item.payload),\n });\n if (response.ok) {\n await this.storage.removeFromQueue(item.key);\n } else {\n await this.storage.incrementAttempts(item.key);\n }\n } catch {\n await this.storage.incrementAttempts(item.key);\n }\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n private async backupSession(): Promise<void> {\n if (!this.storage || !this.sessionId || !this.config || !this.sessionRecorder) return;\n try {\n const session: SessionData = {\n sessionId: this.sessionId,\n projectId: this.config.projectId,\n userId: this.config.userId,\n role: this.config.role,\n deviceInfo: collectDeviceInfo(),\n startedAt: new Date(this.sessionStartTime).toISOString(),\n duration: Math.floor((Date.now() - this.sessionStartTime) / 1000),\n quests: this.questManager?.getResults() ?? [],\n recordings: this.sessionRecorder.getSnapshots(),\n };\n await this.storage.saveSession(session);\n } catch {\n // Backup failure is non-critical\n }\n }\n\n private blobToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolve(reader.result as string);\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(blob);\n });\n }\n}\n"],"names":["DEFAULT_MASK_SELECTORS","resolveConfig","raw","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","collectDeviceInfo","EventBus","type","listener","event","fn","e","QuestManager","events","quests","a","b","startTime","r","q","i","result","logs","options","quest","comment","voiceMemo","feedback","fbs","el","tag","attrs","children","node","key","value","child","generateId","formatTime","seconds","m","s","QuestOverlay","shadowRoot","callbacks","evt","statuses","recording","currentIdx","header","content","progress","status","dotClass","body","statusBar","timer","questTitle","variant","modal","headerClassMap","badgeTextMap","badgeClassMap","placeholderMap","textarea","submitLabel","skipLabel","actions","completed","failed","total","summary","btn","icon","className","text","onClick","label","valueClass","timerEl","elapsed","MAX_SNAPSHOTS","MAX_SNAPSHOT_SIZE","SessionRecorder","config","maskSelector","log","target","describeElement","clone","masked","scripts","sdkHost","html","ms","last","args","now","id","classes","VoiceRecorder","mimeType","err","resolve","blob","durationMs","track","types","Watermark","response","ip","timestamp","tileWidth","tileHeight","cols","rows","row","col","tile","FieldMasker","selectors","element","combinedSelector","elements","DB_NAME","DB_VERSION","STORES","UATStorage","reject","request","db","session","sessionId","chunk","annotation","payload","results","cursor","tx","store","getReq","record","storeName","req","indexName","COLORS","AnnotationCanvas","canvasWrap","commentInput","colorPicker","color","swatch","submitBtn","cancelBtn","toolbar","width","height","sdkRoot","bodyClone","bodyStyles","htmlString","svg","url","canvas","ctx","img","maxW","maxH","scale","rect","point","prev","ShakeTrigger","onShake","acc","deltaX","deltaY","deltaZ","ShakeReporter","TapTrigger","requiredTaps","onActivate","STORAGE_KEY","DeepLinkTrigger","params","KeyboardTrigger","DebugMenu","items","item","SDK_STYLES","MAX_UPLOAD_RETRIES","FirstLookSDK","rawConfig","maxDuration","questSetId","baseUrl","f","annotations","style","cfg","checkConnection","conn","pending","backoffMs","lastAttemptTime","reader"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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"}
|