@qaiddev/thumbs-embed 1.9.0 → 1.9.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/chunks/{loader-XbJ2b8KE.js → loader-DepVS4i5.js} +6 -6
- package/dist/chunks/{loader-XbJ2b8KE.js.map → loader-DepVS4i5.js.map} +1 -1
- package/dist/chunks/{modal-XxYobTKE.js → modal-CEfcaIQR.js} +2 -2
- package/dist/chunks/{modal-XxYobTKE.js.map → modal-CEfcaIQR.js.map} +1 -1
- package/dist/chunks/{recording-Dx8AX-BC.js → recording-CcfOfIC7.js} +6 -6
- package/dist/chunks/recording-CcfOfIC7.js.map +1 -0
- package/dist/index.js +28 -28
- package/dist/index.js.map +1 -1
- package/dist/loader.js +1 -1
- package/dist/qaid.umd.cjs +1 -1
- package/dist/qaid.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/recording-Dx8AX-BC.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as u } from "./loader-
|
|
1
|
+
import { c as u } from "./loader-DepVS4i5.js";
|
|
2
2
|
import { h as i } from "./dom-DvdtcUS8.js";
|
|
3
3
|
import { T as r, a as h, F as b, D as f, E as g } from "./annotate-D-o8xCV9.js";
|
|
4
4
|
class v {
|
|
@@ -210,4 +210,4 @@ class v {
|
|
|
210
210
|
export {
|
|
211
211
|
v as ModalController
|
|
212
212
|
};
|
|
213
|
-
//# sourceMappingURL=modal-
|
|
213
|
+
//# sourceMappingURL=modal-CEfcaIQR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal-XxYobTKE.js","sources":["../../src/modal.ts"],"sourcesContent":["/**\n * Feedback message modal — extracted from embed.ts into its own lazily-loaded\n * chunk. It's fetched the first time a message modal opens (after feedback is\n * submitted), and pre-warmed while the user is targeting an element.\n *\n * The controller owns the modal/backdrop DOM and the message PATCH flow, and\n * talks back to the embed through the narrow `ModalHost` interface.\n */\n\nimport { calculateModalAndArrowPosition } from \"./modal-positioning\";\nimport { h } from \"./dom\";\nimport {\n THUMBS_UP_ICON,\n THUMBS_DOWN_ICON,\n FEEDBACK_ICON,\n DONE_ICON,\n ERROR_ICON,\n} from \"./icons\";\nimport type {\n ResolvedFeedbackConfig,\n EmbedState,\n FeedbackData,\n SelectedBounds,\n} from \"./types\";\n\n/** The slice of the embed the modal needs. */\nexport interface ModalHost {\n readonly config: ResolvedFeedbackConfig;\n readonly uid: string;\n readonly isMobile: boolean;\n readonly state: EmbedState;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n /** Live references — the toggle mutates feedbackType; positioning reads bounds. */\n readonly feedbackData: FeedbackData;\n readonly selectedBounds: SelectedBounds;\n readonly feedbackId: number | null;\n setFeedbackId(id: number | null): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void;\n closeDialogA11y(): void;\n resetFeedbackUi(): void;\n}\n\nexport class ModalController {\n private modalContainer: HTMLDivElement | null = null;\n private backdrop: HTMLDivElement | null = null;\n\n constructor(private host: ModalHost) {}\n\n open(): void {\n const root = this.host.ensureOverlayHost();\n\n // Create backdrop\n this.backdrop = document.createElement(\"div\");\n this.backdrop.className = \"qaid-backdrop\";\n this.backdrop.style.zIndex = String(this.host.config.zIndex + 2);\n this.backdrop.style.background = `rgba(0, 0, 0, ${this.host.config.backdropOpacity})`;\n this.backdrop.addEventListener(\"click\", () => this.close());\n this.host.applyVars(this.backdrop);\n\n if (this.host.isMobile) {\n this.showBottomSheet();\n } else {\n this.showPositionedModal();\n }\n\n root.appendChild(this.backdrop);\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n /** Escape / external close. Runs the finalize-PATCH + teardown. */\n close(): void {\n // If the modal is open and we're closing without submitting, still finalize.\n if (this.host.state === \"MODAL_OPEN\" && this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message: null }),\n }).catch((err) => console.error(\"Failed to finalize feedback:\", err));\n }\n\n this.teardown();\n this.host.resetFeedbackUi();\n }\n\n /** DOM/listener teardown only — no PATCH, no state reset (for embed destroy). */\n destroy(): void {\n this.teardown();\n }\n\n private teardown(): void {\n this.host.closeDialogA11y();\n if (this.modalContainer) {\n this.modalContainer.remove();\n this.modalContainer = null;\n }\n if (this.backdrop) {\n this.backdrop.remove();\n this.backdrop = null;\n }\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private showBottomSheet(): void {\n const root = this.host.ensureOverlayHost();\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"qaid-bottom-sheet\";\n sheet.style.zIndex = String(this.host.config.zIndex + 3);\n\n sheet.appendChild(\n h(\n \"div\",\n { class: \"qaid-bottom-sheet-content\" },\n h(\"div\", { class: \"qaid-bottom-sheet-handle\" }),\n this.buildModalContent()\n )\n );\n\n this.host.applyVars(sheet);\n root.appendChild(sheet);\n this.modalContainer = sheet;\n\n this.setupModalInteractions();\n }\n\n private showPositionedModal(): void {\n const root = this.host.ensureOverlayHost();\n\n const { modal, arrow } = calculateModalAndArrowPosition(\n this.host.selectedBounds,\n window.innerWidth,\n window.innerHeight,\n {\n width: this.host.config.modalWidth,\n height: 280,\n arrowHeight: 12,\n gap: 8,\n viewportPadding: 16,\n }\n );\n\n this.modalContainer = document.createElement(\"div\");\n this.modalContainer.className = `qaid-modal-container qaid-${modal.position}`;\n this.modalContainer.style.top = `${modal.top}px`;\n this.modalContainer.style.left = `${modal.left}px`;\n this.modalContainer.style.zIndex = String(this.host.config.zIndex + 3);\n\n const arrowEl = document.createElement(\"div\");\n arrowEl.className = \"qaid-modal-arrow\";\n arrowEl.style.left = `${arrow.left}px`;\n\n const box = document.createElement(\"div\");\n box.className = \"qaid-modal-box\";\n box.appendChild(this.buildModalContent());\n\n this.modalContainer.appendChild(arrowEl);\n this.modalContainer.appendChild(box);\n this.host.applyVars(this.modalContainer);\n root.appendChild(this.modalContainer);\n\n this.setupModalInteractions();\n }\n\n /**\n * The modal's contents, built as nodes rather than an HTML string.\n *\n * Every piece of copy (title, subtitle, placeholder, button) goes in as\n * text, so a `<`, `&` or `\"` in a translation shows as written instead of\n * being parsed as markup or cutting an attribute short. Only the icons are\n * markup, because they are documented as SVG/HTML strings.\n */\n private buildModalContent(): DocumentFragment {\n const { config, uid } = this.host;\n const text = config.text;\n const type = this.host.feedbackData.feedbackType;\n const isUp = type === \"up\";\n const positiveIcon = config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = config.negativeIcon || THUMBS_DOWN_ICON;\n const toggleClass = config.buttonClass\n ? `qaid-type-toggle qaid-type-toggle-custom ${config.buttonClass} ${isUp ? \"qaid-btn-up\" : \"qaid-btn-down\"}`\n : `qaid-type-toggle ${isUp ? \"qaid-type-up\" : \"qaid-type-down\"}`;\n const toggleLabel = isUp ? \"Feedback type: positive\" : \"Feedback type: negative\";\n // Neutral (single-button) feedback has no sentiment to toggle — show a\n // static feedback icon in place of the up/down toggle.\n const header =\n type === \"neutral\"\n ? h(\"span\", {\n class: \"qaid-type-static\",\n html: config.feedbackIcon || FEEDBACK_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n })\n : h(\"button\", {\n class: toggleClass,\n html: isUp ? positiveIcon : negativeIcon,\n attrs: {\n type: \"button\",\n title: \"Click to switch\",\n \"aria-pressed\": String(isUp),\n \"aria-label\": toggleLabel,\n },\n });\n\n const fragment = document.createDocumentFragment();\n fragment.append(\n h(\n \"div\",\n { class: \"qaid-modal-header\" },\n header,\n h(\n \"div\",\n { class: \"qaid-modal-header-text\" },\n h(\"h3\", {\n class: \"qaid-modal-title\",\n text: text.modalTitle,\n attrs: { id: `qaid-modal-title-${uid}` },\n }),\n h(\"p\", {\n class: \"qaid-modal-subtitle\",\n text: text.modalSubtitle,\n attrs: { id: `qaid-modal-subtitle-${uid}` },\n })\n )\n ),\n h(\"textarea\", {\n class: \"qaid-textarea\",\n attrs: { \"aria-label\": text.modalSubtitle, placeholder: text.placeholder },\n }),\n h(\n \"div\",\n { class: \"qaid-btn-row\" },\n h(\"button\", {\n class: \"qaid-btn-submit\",\n text: text.skipButton,\n attrs: { type: \"button\" },\n })\n )\n );\n return fragment;\n }\n\n private setupModalInteractions(): void {\n // Dialog semantics + focus trap + background inert\n this.host.openDialogA11y(this.modalContainer!, {\n labelledbyId: `qaid-modal-title-${this.host.uid}`,\n describedbyId: `qaid-modal-subtitle-${this.host.uid}`,\n });\n\n const textarea =\n this.modalContainer!.querySelector<HTMLTextAreaElement>(\".qaid-textarea\");\n const submitBtn =\n this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-btn-submit\");\n if (textarea) {\n // Auto-focus\n setTimeout(() => textarea.focus(), 100);\n\n // Update button text based on content\n textarea.addEventListener(\"input\", () => {\n if (submitBtn) {\n submitBtn.textContent = textarea.value.trim()\n ? this.host.config.text.submitButton\n : this.host.config.text.skipButton;\n }\n });\n }\n\n if (submitBtn) {\n submitBtn.addEventListener(\"click\", () => {\n const message = textarea?.value.trim() || null;\n this.submitMessage(message);\n });\n }\n\n // Handle feedback type toggle\n const typeToggle = this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-type-toggle\");\n if (typeToggle) {\n typeToggle.addEventListener(\"click\", () => {\n const newType = this.host.feedbackData.feedbackType === \"up\" ? \"down\" : \"up\";\n this.host.feedbackData.feedbackType = newType;\n\n // Update UI — use different classes depending on custom buttonClass\n if (this.host.config.buttonClass) {\n typeToggle.classList.toggle(\"qaid-btn-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-btn-down\", newType === \"down\");\n } else {\n typeToggle.classList.toggle(\"qaid-type-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-type-down\", newType === \"down\");\n }\n const positiveIcon = this.host.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.host.config.negativeIcon || THUMBS_DOWN_ICON;\n typeToggle.innerHTML = newType === \"up\" ? positiveIcon : negativeIcon;\n\n // Update accessible state and announce the change\n const toggleLabel = newType === \"up\" ? \"Feedback type: positive\" : \"Feedback type: negative\";\n typeToggle.setAttribute(\"aria-pressed\", String(newType === \"up\"));\n typeToggle.setAttribute(\"aria-label\", toggleLabel);\n this.host.announceMsg(toggleLabel);\n\n // Update feedback on server\n if (this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ feedbackType: newType }),\n }).catch((err) => console.error(\"Failed to update feedback type:\", err));\n }\n });\n }\n }\n\n private async submitMessage(message: string | null): Promise<void> {\n // null = nothing was attempted (the initial POST never produced an id);\n // otherwise whether the server accepted the PATCH.\n let delivered: boolean | null = null;\n\n if (this.host.feedbackId) {\n try {\n const response = await fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n // fetch resolves on any HTTP reply. A 4xx/5xx means the server\n // refused or lost the message, so it is not a success.\n delivered = response.ok;\n if (!response.ok) {\n console.error(\"Failed to submit feedback message: HTTP\", response.status);\n }\n } catch (error) {\n console.error(\"Failed to submit feedback message:\", error);\n delivered = false;\n }\n // Clear feedbackId so close() doesn't send another PATCH.\n this.host.setFeedbackId(null);\n }\n\n // The message was lost. Closing quietly reads as success, so say so —\n // even with hideConfirmation, which only opts out of the success screen.\n if (delivered === false) {\n this.showResult(false);\n return;\n }\n\n // A submit that simply removes the modal reads as a failure — nothing\n // acknowledges that the message was sent. Show a success screen unless the\n // host opted out.\n //\n // With no feedbackId the initial POST never landed and there was nothing\n // to send the message to; that path closes exactly as it did before.\n if (delivered && !this.host.config.hideConfirmation) {\n this.showResult(true);\n return;\n }\n this.close();\n }\n\n /**\n * Replace the modal's contents with the outcome: a checkmark and a short\n * acknowledgement, or an error mark and a \"not sent\" notice.\n *\n * The submit button that had focus is gone by this point, so focus moves to\n * the heading (WCAG 2.4.3) and the message is announced. Mirrors the quests\n * embed's thank-you screen so the two products confirm the same way. Copy\n * goes in as text, never markup.\n */\n private showResult(delivered: boolean): void {\n const box = this.modalContainer?.querySelector(\".qaid-modal-box\")\n ?? this.modalContainer?.querySelector(\".qaid-bottom-sheet-content\")\n ?? this.modalContainer;\n\n if (!box) {\n // Nothing to draw into — never strand the user with an open modal.\n this.close();\n return;\n }\n\n const text = this.host.config.text;\n const heading = delivered ? text.confirmationTitle : text.errorTitle;\n const body = delivered ? text.confirmationMessage : text.errorMessage;\n\n const title = h(\"h3\", {\n class: \"qaid-confirm-title\",\n text: heading,\n attrs: { id: `qaid-confirm-title-${this.host.uid}`, tabindex: \"-1\" },\n });\n\n box.textContent = \"\";\n box.appendChild(\n h(\n \"div\",\n { class: delivered ? \"qaid-confirm\" : \"qaid-confirm qaid-confirm-error\" },\n h(\"span\", {\n class: \"qaid-confirm-icon\",\n html: delivered ? DONE_ICON : ERROR_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n }),\n title,\n h(\"p\", { class: \"qaid-confirm-message\", text: body }),\n h(\"button\", {\n class: \"qaid-btn-submit qaid-confirm-close\",\n text: text.confirmationClose,\n attrs: { type: \"button\" },\n on: { click: () => this.close() },\n })\n )\n );\n\n // Focus after paint, matching the quests embed.\n requestAnimationFrame(() => title.focus());\n this.host.announceMsg(`${heading} ${body}`, true);\n }\n}\n"],"names":["ModalController","host","root","err","sheet","h","modal","arrow","calculateModalAndArrowPosition","arrowEl","box","config","uid","text","type","isUp","positiveIcon","THUMBS_UP_ICON","negativeIcon","THUMBS_DOWN_ICON","toggleClass","toggleLabel","header","FEEDBACK_ICON","fragment","textarea","submitBtn","message","typeToggle","newType","delivered","response","error","heading","body","title","DONE_ICON","ERROR_ICON"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"modal-CEfcaIQR.js","sources":["../../src/modal.ts"],"sourcesContent":["/**\n * Feedback message modal — extracted from embed.ts into its own lazily-loaded\n * chunk. It's fetched the first time a message modal opens (after feedback is\n * submitted), and pre-warmed while the user is targeting an element.\n *\n * The controller owns the modal/backdrop DOM and the message PATCH flow, and\n * talks back to the embed through the narrow `ModalHost` interface.\n */\n\nimport { calculateModalAndArrowPosition } from \"./modal-positioning\";\nimport { h } from \"./dom\";\nimport {\n THUMBS_UP_ICON,\n THUMBS_DOWN_ICON,\n FEEDBACK_ICON,\n DONE_ICON,\n ERROR_ICON,\n} from \"./icons\";\nimport type {\n ResolvedFeedbackConfig,\n EmbedState,\n FeedbackData,\n SelectedBounds,\n} from \"./types\";\n\n/** The slice of the embed the modal needs. */\nexport interface ModalHost {\n readonly config: ResolvedFeedbackConfig;\n readonly uid: string;\n readonly isMobile: boolean;\n readonly state: EmbedState;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n /** Live references — the toggle mutates feedbackType; positioning reads bounds. */\n readonly feedbackData: FeedbackData;\n readonly selectedBounds: SelectedBounds;\n readonly feedbackId: number | null;\n setFeedbackId(id: number | null): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void;\n closeDialogA11y(): void;\n resetFeedbackUi(): void;\n}\n\nexport class ModalController {\n private modalContainer: HTMLDivElement | null = null;\n private backdrop: HTMLDivElement | null = null;\n\n constructor(private host: ModalHost) {}\n\n open(): void {\n const root = this.host.ensureOverlayHost();\n\n // Create backdrop\n this.backdrop = document.createElement(\"div\");\n this.backdrop.className = \"qaid-backdrop\";\n this.backdrop.style.zIndex = String(this.host.config.zIndex + 2);\n this.backdrop.style.background = `rgba(0, 0, 0, ${this.host.config.backdropOpacity})`;\n this.backdrop.addEventListener(\"click\", () => this.close());\n this.host.applyVars(this.backdrop);\n\n if (this.host.isMobile) {\n this.showBottomSheet();\n } else {\n this.showPositionedModal();\n }\n\n root.appendChild(this.backdrop);\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n /** Escape / external close. Runs the finalize-PATCH + teardown. */\n close(): void {\n // If the modal is open and we're closing without submitting, still finalize.\n if (this.host.state === \"MODAL_OPEN\" && this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message: null }),\n }).catch((err) => console.error(\"Failed to finalize feedback:\", err));\n }\n\n this.teardown();\n this.host.resetFeedbackUi();\n }\n\n /** DOM/listener teardown only — no PATCH, no state reset (for embed destroy). */\n destroy(): void {\n this.teardown();\n }\n\n private teardown(): void {\n this.host.closeDialogA11y();\n if (this.modalContainer) {\n this.modalContainer.remove();\n this.modalContainer = null;\n }\n if (this.backdrop) {\n this.backdrop.remove();\n this.backdrop = null;\n }\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private showBottomSheet(): void {\n const root = this.host.ensureOverlayHost();\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"qaid-bottom-sheet\";\n sheet.style.zIndex = String(this.host.config.zIndex + 3);\n\n sheet.appendChild(\n h(\n \"div\",\n { class: \"qaid-bottom-sheet-content\" },\n h(\"div\", { class: \"qaid-bottom-sheet-handle\" }),\n this.buildModalContent()\n )\n );\n\n this.host.applyVars(sheet);\n root.appendChild(sheet);\n this.modalContainer = sheet;\n\n this.setupModalInteractions();\n }\n\n private showPositionedModal(): void {\n const root = this.host.ensureOverlayHost();\n\n const { modal, arrow } = calculateModalAndArrowPosition(\n this.host.selectedBounds,\n window.innerWidth,\n window.innerHeight,\n {\n width: this.host.config.modalWidth,\n height: 280,\n arrowHeight: 12,\n gap: 8,\n viewportPadding: 16,\n }\n );\n\n this.modalContainer = document.createElement(\"div\");\n this.modalContainer.className = `qaid-modal-container qaid-${modal.position}`;\n this.modalContainer.style.top = `${modal.top}px`;\n this.modalContainer.style.left = `${modal.left}px`;\n this.modalContainer.style.zIndex = String(this.host.config.zIndex + 3);\n\n const arrowEl = document.createElement(\"div\");\n arrowEl.className = \"qaid-modal-arrow\";\n arrowEl.style.left = `${arrow.left}px`;\n\n const box = document.createElement(\"div\");\n box.className = \"qaid-modal-box\";\n box.appendChild(this.buildModalContent());\n\n this.modalContainer.appendChild(arrowEl);\n this.modalContainer.appendChild(box);\n this.host.applyVars(this.modalContainer);\n root.appendChild(this.modalContainer);\n\n this.setupModalInteractions();\n }\n\n /**\n * The modal's contents, built as nodes rather than an HTML string.\n *\n * Every piece of copy (title, subtitle, placeholder, button) goes in as\n * text, so a `<`, `&` or `\"` in a translation shows as written instead of\n * being parsed as markup or cutting an attribute short. Only the icons are\n * markup, because they are documented as SVG/HTML strings.\n */\n private buildModalContent(): DocumentFragment {\n const { config, uid } = this.host;\n const text = config.text;\n const type = this.host.feedbackData.feedbackType;\n const isUp = type === \"up\";\n const positiveIcon = config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = config.negativeIcon || THUMBS_DOWN_ICON;\n const toggleClass = config.buttonClass\n ? `qaid-type-toggle qaid-type-toggle-custom ${config.buttonClass} ${isUp ? \"qaid-btn-up\" : \"qaid-btn-down\"}`\n : `qaid-type-toggle ${isUp ? \"qaid-type-up\" : \"qaid-type-down\"}`;\n const toggleLabel = isUp ? \"Feedback type: positive\" : \"Feedback type: negative\";\n // Neutral (single-button) feedback has no sentiment to toggle — show a\n // static feedback icon in place of the up/down toggle.\n const header =\n type === \"neutral\"\n ? h(\"span\", {\n class: \"qaid-type-static\",\n html: config.feedbackIcon || FEEDBACK_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n })\n : h(\"button\", {\n class: toggleClass,\n html: isUp ? positiveIcon : negativeIcon,\n attrs: {\n type: \"button\",\n title: \"Click to switch\",\n \"aria-pressed\": String(isUp),\n \"aria-label\": toggleLabel,\n },\n });\n\n const fragment = document.createDocumentFragment();\n fragment.append(\n h(\n \"div\",\n { class: \"qaid-modal-header\" },\n header,\n h(\n \"div\",\n { class: \"qaid-modal-header-text\" },\n h(\"h3\", {\n class: \"qaid-modal-title\",\n text: text.modalTitle,\n attrs: { id: `qaid-modal-title-${uid}` },\n }),\n h(\"p\", {\n class: \"qaid-modal-subtitle\",\n text: text.modalSubtitle,\n attrs: { id: `qaid-modal-subtitle-${uid}` },\n })\n )\n ),\n h(\"textarea\", {\n class: \"qaid-textarea\",\n attrs: { \"aria-label\": text.modalSubtitle, placeholder: text.placeholder },\n }),\n h(\n \"div\",\n { class: \"qaid-btn-row\" },\n h(\"button\", {\n class: \"qaid-btn-submit\",\n text: text.skipButton,\n attrs: { type: \"button\" },\n })\n )\n );\n return fragment;\n }\n\n private setupModalInteractions(): void {\n // Dialog semantics + focus trap + background inert\n this.host.openDialogA11y(this.modalContainer!, {\n labelledbyId: `qaid-modal-title-${this.host.uid}`,\n describedbyId: `qaid-modal-subtitle-${this.host.uid}`,\n });\n\n const textarea =\n this.modalContainer!.querySelector<HTMLTextAreaElement>(\".qaid-textarea\");\n const submitBtn =\n this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-btn-submit\");\n if (textarea) {\n // Auto-focus\n setTimeout(() => textarea.focus(), 100);\n\n // Update button text based on content\n textarea.addEventListener(\"input\", () => {\n if (submitBtn) {\n submitBtn.textContent = textarea.value.trim()\n ? this.host.config.text.submitButton\n : this.host.config.text.skipButton;\n }\n });\n }\n\n if (submitBtn) {\n submitBtn.addEventListener(\"click\", () => {\n const message = textarea?.value.trim() || null;\n this.submitMessage(message);\n });\n }\n\n // Handle feedback type toggle\n const typeToggle = this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-type-toggle\");\n if (typeToggle) {\n typeToggle.addEventListener(\"click\", () => {\n const newType = this.host.feedbackData.feedbackType === \"up\" ? \"down\" : \"up\";\n this.host.feedbackData.feedbackType = newType;\n\n // Update UI — use different classes depending on custom buttonClass\n if (this.host.config.buttonClass) {\n typeToggle.classList.toggle(\"qaid-btn-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-btn-down\", newType === \"down\");\n } else {\n typeToggle.classList.toggle(\"qaid-type-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-type-down\", newType === \"down\");\n }\n const positiveIcon = this.host.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.host.config.negativeIcon || THUMBS_DOWN_ICON;\n typeToggle.innerHTML = newType === \"up\" ? positiveIcon : negativeIcon;\n\n // Update accessible state and announce the change\n const toggleLabel = newType === \"up\" ? \"Feedback type: positive\" : \"Feedback type: negative\";\n typeToggle.setAttribute(\"aria-pressed\", String(newType === \"up\"));\n typeToggle.setAttribute(\"aria-label\", toggleLabel);\n this.host.announceMsg(toggleLabel);\n\n // Update feedback on server\n if (this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ feedbackType: newType }),\n }).catch((err) => console.error(\"Failed to update feedback type:\", err));\n }\n });\n }\n }\n\n private async submitMessage(message: string | null): Promise<void> {\n // null = nothing was attempted (the initial POST never produced an id);\n // otherwise whether the server accepted the PATCH.\n let delivered: boolean | null = null;\n\n if (this.host.feedbackId) {\n try {\n const response = await fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n // fetch resolves on any HTTP reply. A 4xx/5xx means the server\n // refused or lost the message, so it is not a success.\n delivered = response.ok;\n if (!response.ok) {\n console.error(\"Failed to submit feedback message: HTTP\", response.status);\n }\n } catch (error) {\n console.error(\"Failed to submit feedback message:\", error);\n delivered = false;\n }\n // Clear feedbackId so close() doesn't send another PATCH.\n this.host.setFeedbackId(null);\n }\n\n // The message was lost. Closing quietly reads as success, so say so —\n // even with hideConfirmation, which only opts out of the success screen.\n if (delivered === false) {\n this.showResult(false);\n return;\n }\n\n // A submit that simply removes the modal reads as a failure — nothing\n // acknowledges that the message was sent. Show a success screen unless the\n // host opted out.\n //\n // With no feedbackId the initial POST never landed and there was nothing\n // to send the message to; that path closes exactly as it did before.\n if (delivered && !this.host.config.hideConfirmation) {\n this.showResult(true);\n return;\n }\n this.close();\n }\n\n /**\n * Replace the modal's contents with the outcome: a checkmark and a short\n * acknowledgement, or an error mark and a \"not sent\" notice.\n *\n * The submit button that had focus is gone by this point, so focus moves to\n * the heading (WCAG 2.4.3) and the message is announced. Mirrors the quests\n * embed's thank-you screen so the two products confirm the same way. Copy\n * goes in as text, never markup.\n */\n private showResult(delivered: boolean): void {\n const box = this.modalContainer?.querySelector(\".qaid-modal-box\")\n ?? this.modalContainer?.querySelector(\".qaid-bottom-sheet-content\")\n ?? this.modalContainer;\n\n if (!box) {\n // Nothing to draw into — never strand the user with an open modal.\n this.close();\n return;\n }\n\n const text = this.host.config.text;\n const heading = delivered ? text.confirmationTitle : text.errorTitle;\n const body = delivered ? text.confirmationMessage : text.errorMessage;\n\n const title = h(\"h3\", {\n class: \"qaid-confirm-title\",\n text: heading,\n attrs: { id: `qaid-confirm-title-${this.host.uid}`, tabindex: \"-1\" },\n });\n\n box.textContent = \"\";\n box.appendChild(\n h(\n \"div\",\n { class: delivered ? \"qaid-confirm\" : \"qaid-confirm qaid-confirm-error\" },\n h(\"span\", {\n class: \"qaid-confirm-icon\",\n html: delivered ? DONE_ICON : ERROR_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n }),\n title,\n h(\"p\", { class: \"qaid-confirm-message\", text: body }),\n h(\"button\", {\n class: \"qaid-btn-submit qaid-confirm-close\",\n text: text.confirmationClose,\n attrs: { type: \"button\" },\n on: { click: () => this.close() },\n })\n )\n );\n\n // Focus after paint, matching the quests embed.\n requestAnimationFrame(() => title.focus());\n this.host.announceMsg(`${heading} ${body}`, true);\n }\n}\n"],"names":["ModalController","host","root","err","sheet","h","modal","arrow","calculateModalAndArrowPosition","arrowEl","box","config","uid","text","type","isUp","positiveIcon","THUMBS_UP_ICON","negativeIcon","THUMBS_DOWN_ICON","toggleClass","toggleLabel","header","FEEDBACK_ICON","fragment","textarea","submitBtn","message","typeToggle","newType","delivered","response","error","heading","body","title","DONE_ICON","ERROR_ICON"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { h as r } from "./dom-DvdtcUS8.js";
|
|
2
|
-
import { b as
|
|
2
|
+
import { b as v } from "./annotate-D-o8xCV9.js";
|
|
3
3
|
const g = 20, u = 4096;
|
|
4
4
|
function l(d) {
|
|
5
5
|
const t = typeof d == "string" ? d : JSON.stringify(d);
|
|
@@ -24,14 +24,14 @@ function R() {
|
|
|
24
24
|
s?.body && (a = l(s.body));
|
|
25
25
|
const h = await t.apply(window, [o, s]);
|
|
26
26
|
if (h.status >= 400) {
|
|
27
|
-
const
|
|
27
|
+
const f = await b(h);
|
|
28
28
|
p(d, {
|
|
29
29
|
url: n,
|
|
30
30
|
method: c.toUpperCase(),
|
|
31
31
|
status: h.status,
|
|
32
32
|
statusText: h.statusText,
|
|
33
33
|
requestBody: a,
|
|
34
|
-
responseBody:
|
|
34
|
+
responseBody: f,
|
|
35
35
|
timestamp: Date.now()
|
|
36
36
|
});
|
|
37
37
|
}
|
|
@@ -154,7 +154,7 @@ class m {
|
|
|
154
154
|
}
|
|
155
155
|
handleRedactPickClick(t) {
|
|
156
156
|
const i = t.target instanceof Element ? t.target : null;
|
|
157
|
-
if (!i ||
|
|
157
|
+
if (!i || v(i) || this.redactPickerRoot?.contains(i)) return;
|
|
158
158
|
t.preventDefault(), t.stopPropagation();
|
|
159
159
|
const e = this.redactPicks.indexOf(i);
|
|
160
160
|
e >= 0 ? this.redactPicks.splice(e, 1) : this.redactPicks.push(i);
|
|
@@ -196,7 +196,7 @@ class m {
|
|
|
196
196
|
attrs: { type: "button" },
|
|
197
197
|
on: { click: () => this.stopRecording() }
|
|
198
198
|
})
|
|
199
|
-
), this.host.applyVars(this.recordingIndicator),
|
|
199
|
+
), this.host.applyVars(this.recordingIndicator), t.appendChild(this.recordingIndicator);
|
|
200
200
|
}
|
|
201
201
|
updateRecordingTimer(t) {
|
|
202
202
|
if (!this.recordingIndicator) return;
|
|
@@ -318,4 +318,4 @@ class m {
|
|
|
318
318
|
export {
|
|
319
319
|
m as RecordingController
|
|
320
320
|
};
|
|
321
|
-
//# sourceMappingURL=recording-
|
|
321
|
+
//# sourceMappingURL=recording-CcfOfIC7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording-CcfOfIC7.js","sources":["../../src/network-capture.ts","../../src/recording.ts"],"sourcesContent":["/**\n * Network capture utilities\n * Intercepts fetch() and XMLHttpRequest to capture 4xx/5xx errors\n */\n\nimport type { NetworkError } from \"./types\";\n\nconst MAX_ENTRIES = 20;\nconst MAX_BODY_SIZE = 4096; // 4KB\n\nexport interface NetworkCapture {\n errors: NetworkError[];\n restore: () => void;\n}\n\n/**\n * Truncate a body to max size. Callers must guard against null/undefined.\n */\nexport function truncateBody(body: unknown): string {\n const str = typeof body === \"string\" ? body : JSON.stringify(body);\n if (str.length > MAX_BODY_SIZE) {\n return str.slice(0, MAX_BODY_SIZE) + \"…[truncated]\";\n }\n return str;\n}\n\n/**\n * Add an error to the buffer, evicting oldest if at capacity\n */\nexport function addEntry(errors: NetworkError[], entry: NetworkError): void {\n if (errors.length >= MAX_ENTRIES) {\n errors.shift();\n }\n errors.push(entry);\n}\n\n/**\n * Read response body as text, with size limit\n */\nasync function safeReadBody(response: Response): Promise<string | undefined> {\n try {\n const text = await response.clone().text();\n return truncateBody(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Intercept fetch() and XMLHttpRequest to capture network errors (4xx/5xx)\n */\nexport function captureNetworkErrors(): NetworkCapture {\n const errors: NetworkError[] = [];\n const originalFetch = window.fetch;\n\n // Monkey-patch fetch\n window.fetch = async function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url;\n const method = init?.method ?? (typeof input === \"object\" && \"method\" in input ? input.method : \"GET\");\n let requestBody: string | undefined;\n if (init?.body) {\n requestBody = truncateBody(init.body);\n }\n\n const response = await originalFetch.apply(window, [input, init]);\n\n if (response.status >= 400) {\n const responseBody = await safeReadBody(response);\n addEntry(errors, {\n url,\n method: method.toUpperCase(),\n status: response.status,\n statusText: response.statusText,\n requestBody,\n responseBody,\n timestamp: Date.now(),\n });\n }\n\n return response;\n };\n\n // Monkey-patch XMLHttpRequest\n const originalXHROpen = XMLHttpRequest.prototype.open;\n const originalXHRSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (method: string, url: string | URL, ...rest: unknown[]) {\n (this as XMLHttpRequest & { _qaid_method: string; _qaid_url: string })._qaid_method = method;\n (this as XMLHttpRequest & { _qaid_url: string })._qaid_url = typeof url === \"string\" ? url : url.toString();\n return originalXHROpen.apply(this, [method, url, ...rest] as Parameters<typeof originalXHROpen>);\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const xhr = this as XMLHttpRequest & { _qaid_method: string; _qaid_url: string };\n const requestBody = body ? truncateBody(body) : undefined;\n\n xhr.addEventListener(\"load\", function () {\n if (xhr.status >= 400) {\n addEntry(errors, {\n url: xhr._qaid_url,\n method: xhr._qaid_method.toUpperCase(),\n status: xhr.status,\n statusText: xhr.statusText,\n requestBody,\n responseBody: truncateBody(xhr.responseText),\n timestamp: Date.now(),\n });\n }\n });\n\n return originalXHRSend.apply(this, [body]);\n };\n\n return {\n errors,\n restore: () => {\n window.fetch = originalFetch;\n XMLHttpRequest.prototype.open = originalXHROpen;\n XMLHttpRequest.prototype.send = originalXHRSend;\n },\n };\n}\n","/**\n * Video-recording subsystem — extracted from embed.ts so it lives in its own\n * lazily-loaded chunk. A thumbs-only visitor never downloads any of this; the\n * embed dynamically imports it the first time the record button is used (and\n * warms it on hover — see embed.ts prewarmVideo).\n *\n * The controller owns all recording state and UI (recorder, redaction picker,\n * indicator, preview, submit). It talks back to the embed through the narrow\n * `RecordingHost` interface, so embed internals stay private and the coupling\n * is explicit.\n */\n\nimport { h } from \"./dom\";\nimport { isEmbedElement } from \"./dom-utils\";\nimport { captureNetworkErrors, type NetworkCapture } from \"./network-capture\";\nimport type { VideoRecorder } from \"./video-capture\";\nimport type { ResolvedFeedbackConfig, EmbedState } from \"./types\";\nimport type { ConsoleCapture } from \"./console-capture\";\n\n/** The slice of the embed the recording subsystem needs. */\nexport interface RecordingHost {\n readonly config: ResolvedFeedbackConfig;\n readonly visitorId: string;\n readonly uid: string;\n readonly overlayShadowHost: HTMLDivElement | null;\n readonly consoleCapture: ConsoleCapture | null;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n readonly state: EmbedState;\n setState(state: EmbedState): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void;\n closeDialogA11y(): void;\n setButtonsDisabled(disabled: boolean): void;\n tryLaunchQuest(\n type: \"up\" | \"down\" | \"video\",\n feedbackId: number | string | null\n ): Promise<boolean>;\n}\n\nexport class RecordingController {\n private videoRecorder: VideoRecorder | null = null;\n private networkCapture: NetworkCapture | null = null;\n private recordedBlob: Blob | null = null;\n private recordingIndicator: HTMLDivElement | null = null;\n private videoPreview: HTMLDivElement | null = null;\n private isRecording = false;\n private isSendingVideo = false;\n\n // Pre-recording redaction picking.\n private redactPicks: Element[] = [];\n private redactPickerRoot: HTMLDivElement | null = null;\n private redactRafId = 0;\n private boundRedactClick = (e: MouseEvent): void => this.handleRedactPickClick(e);\n\n constructor(private host: RecordingHost) {}\n\n /** Escape-key handler; returns true when it consumed the key. */\n handleEscape(): boolean {\n if (this.isRecording) {\n void this.stopRecording();\n return true;\n }\n if (this.videoPreview) {\n this.cancelRecordingPreview();\n return true;\n }\n if (this.redactPickerRoot) {\n this.finishRedactionPicking(false);\n return true;\n }\n return false;\n }\n\n destroy(): void {\n if (this.redactRafId) cancelAnimationFrame(this.redactRafId);\n this.redactRafId = 0;\n document.removeEventListener(\"click\", this.boundRedactClick, true);\n if (this.redactPickerRoot) {\n this.redactPickerRoot.remove();\n this.redactPickerRoot = null;\n }\n this.redactPicks = [];\n this.cleanupRecording();\n this.removeVideoPreview();\n }\n\n async startRecording(redactionElements: Element[] = []): Promise<void> {\n // Don't start if already recording or in a targeting/modal flow.\n if (this.isRecording || this.host.state !== \"IDLE\") return;\n\n try {\n this.networkCapture = captureNetworkErrors();\n\n const { createVideoRecorder } = await import(\"./video-capture\");\n this.videoRecorder = createVideoRecorder({\n maxDuration: this.host.config.videoOptions.maxDuration,\n redactionElements,\n });\n\n this.videoRecorder.onTick((elapsed) => {\n this.updateRecordingTimer(elapsed);\n });\n\n // Handle unexpected stops (browser stop button, max duration).\n this.videoRecorder.onStop((blob) => {\n if (this.isRecording) {\n this.recordedBlob = blob;\n this.isRecording = false;\n this.host.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n this.host.setButtonsDisabled(false);\n\n if (blob && blob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n });\n\n await this.videoRecorder.start();\n this.isRecording = true;\n this.host.announceMsg(\"Recording started\");\n this.host.setButtonsDisabled(true);\n this.showRecordingIndicator();\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n } catch (error) {\n // User denied screen share, shared a disallowed surface (window / whole\n // screen), the browser cannot blur the picked areas, or another error\n // occurred. Surface the tab-only and no-blur messages so neither is a\n // silent no-op. (Matched by text: video-capture is a separate lazy\n // chunk, so importing its constant here would merge the two.)\n if (error instanceof Error && /current tab|cannot blur/i.test(error.message)) {\n this.host.announceMsg(error.message);\n }\n this.cleanupRecording();\n }\n }\n\n private async stopRecording(): Promise<void> {\n try {\n this.recordedBlob = await this.videoRecorder!.stop();\n } catch {\n this.recordedBlob = null;\n }\n\n this.isRecording = false;\n this.host.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n\n if (this.recordedBlob && this.recordedBlob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n\n /**\n * Pre-recording redaction picker. The user clicks page areas to blur; each\n * gets an outline that tracks its position, then \"Start recording\" hands the\n * picks to the recorder, which blurs their live bounding boxes so the\n * redaction follows the content as the page scrolls. \"Cancel\"/Escape aborts.\n */\n startPicking(): void {\n if (this.isRecording || this.host.state !== \"IDLE\") return;\n this.host.setState(\"REDACT_PICKING\");\n this.redactPicks = [];\n\n const root = this.host.ensureOverlayHost();\n\n // Layer holding the per-pick outlines (repositioned every frame).\n const boxHost = h(\"div\", { style: \"position:absolute;inset:0;\" });\n\n const container = h(\n \"div\",\n { class: \"qaid-redact-picker\", style: \"position:fixed;inset:0;pointer-events:none;\" },\n boxHost,\n // Control bar — the only pointer-interactive part of the overlay.\n h(\n \"div\",\n {\n style:\n \"position:fixed;left:50%;bottom:24px;transform:translateX(-50%);\" +\n \"display:flex;gap:12px;align-items:center;pointer-events:auto;\" +\n \"background:#0b1220;color:#fff;border:1px solid #ff6b6b;border-radius:9999px;\" +\n \"padding:10px 16px;font:600 13px system-ui,sans-serif;box-shadow:0 8px 30px rgba(0,0,0,.5);\",\n },\n h(\"span\", {\n text: \"Click sensitive areas to blur\",\n attrs: { \"data-qaid-redact-label\": \"\" },\n }),\n h(\"button\", {\n text: \"Start recording\",\n attrs: { type: \"button\" },\n style:\n \"cursor:pointer;border:0;border-radius:9999px;padding:8px 14px;background:#ff6b6b;color:#0b1220;font:inherit;\",\n on: { click: () => this.finishRedactionPicking(true) },\n }),\n h(\"button\", {\n text: \"Cancel\",\n attrs: { type: \"button\" },\n style: \"cursor:pointer;border:0;background:transparent;color:#9fb3c8;font:inherit;\",\n on: { click: () => this.finishRedactionPicking(false) },\n }),\n ),\n );\n\n root.appendChild(container);\n this.redactPickerRoot = container;\n\n // Keep the outlines glued to their elements while the user scrolls/picks.\n const tick = (): void => {\n this.renderRedactPicks(boxHost);\n this.redactRafId = requestAnimationFrame(tick);\n };\n tick();\n\n document.addEventListener(\"click\", this.boundRedactClick, true);\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n this.host.announceMsg(\"Click sensitive areas to blur, then start recording\");\n }\n\n private handleRedactPickClick(e: MouseEvent): void {\n const target = e.target instanceof Element ? e.target : null;\n // Ignore clicks on the embed's own UI. isEmbedElement catches the retargeted\n // shadow host (real browsers compose click events to it); the contains()\n // check catches the picker's own control-bar buttons when the event isn't\n // retargeted, so their handlers still fire instead of being swallowed here.\n if (!target || isEmbedElement(target) || this.redactPickerRoot?.contains(target)) return;\n e.preventDefault();\n e.stopPropagation();\n\n const idx = this.redactPicks.indexOf(target);\n if (idx >= 0) {\n this.redactPicks.splice(idx, 1);\n } else {\n this.redactPicks.push(target);\n }\n\n const label = this.redactPickerRoot?.querySelector(\"[data-qaid-redact-label]\");\n if (label) {\n const n = this.redactPicks.length;\n label.textContent =\n n === 0 ? \"Click sensitive areas to blur\" : `${n} area${n === 1 ? \"\" : \"s\"} will be blurred`;\n }\n }\n\n private renderRedactPicks(layer: HTMLElement): void {\n // Rebuild the outline boxes to match current picks and their live positions.\n layer.textContent = \"\";\n for (const el of this.redactPicks) {\n const r = el.getBoundingClientRect();\n if (r.width <= 0 || r.height <= 0) continue;\n layer.appendChild(\n h(\"div\", {\n style:\n `position:fixed;left:${r.left}px;top:${r.top}px;width:${r.width}px;height:${r.height}px;` +\n \"border:2px solid #ff6b6b;border-radius:4px;background:rgba(255,107,107,.18);pointer-events:none;\",\n }),\n );\n }\n }\n\n private finishRedactionPicking(record: boolean): void {\n if (this.redactRafId) cancelAnimationFrame(this.redactRafId);\n this.redactRafId = 0;\n document.removeEventListener(\"click\", this.boundRedactClick, true);\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n if (this.redactPickerRoot) {\n this.redactPickerRoot.remove();\n this.redactPickerRoot = null;\n }\n const picks = this.redactPicks;\n this.redactPicks = [];\n this.host.setState(\"IDLE\");\n if (record) {\n void this.startRecording(picks);\n }\n }\n\n private showRecordingIndicator(): void {\n const root = this.host.ensureOverlayHost();\n\n this.recordingIndicator = h(\n \"div\",\n { class: \"qaid-recording-indicator\", style: `z-index:${this.host.config.zIndex + 100}` },\n h(\"div\", { class: \"qaid-recording-dot\" }),\n h(\"span\", {\n class: \"qaid-recording-time\",\n text: this.formatTime(this.host.config.videoOptions.maxDuration),\n }),\n h(\"button\", {\n class: \"qaid-recording-stop\",\n text: \"Stop\",\n attrs: { type: \"button\" },\n on: { click: () => this.stopRecording() },\n }),\n );\n\n this.host.applyVars(this.recordingIndicator);\n\n // The overlay host stays pointer-events:none. It covers the whole viewport,\n // so turning it on made a clear sheet over the page for the length of the\n // recording and every click the visitor made to show the bug died on it.\n // The pill takes its own clicks (pointer-events:auto in its CSS), the way\n // the redaction picker's control bar does.\n root.appendChild(this.recordingIndicator);\n }\n\n private updateRecordingTimer(elapsed: number): void {\n if (!this.recordingIndicator) return;\n const timer = this.recordingIndicator.querySelector<HTMLSpanElement>(\".qaid-recording-time\");\n if (timer) {\n const remaining = Math.max(0, this.host.config.videoOptions.maxDuration - elapsed);\n timer.textContent = this.formatTime(remaining);\n // Announce the final countdown so it is perceivable non-visually.\n if (remaining > 0 && remaining <= 5) {\n this.host.announceMsg(`${remaining} second${remaining === 1 ? \"\" : \"s\"} remaining`);\n }\n }\n }\n\n private formatTime(seconds: number): string {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${m}:${s.toString().padStart(2, \"0\")}`;\n }\n\n private removeRecordingIndicator(): void {\n if (this.recordingIndicator) {\n this.recordingIndicator.remove();\n this.recordingIndicator = null;\n }\n }\n\n private showRecordingPreview(): void {\n const root = this.host.ensureOverlayHost();\n\n const videoUrl = URL.createObjectURL(this.recordedBlob!);\n const titleId = `qaid-video-title-${this.host.uid}`;\n\n const title = h(\"h3\", { text: \"Review your recording\", attrs: { id: titleId } });\n\n // Boolean media props must be set as properties (the `muted` attribute is\n // unreliable for autoplay), so build the element then assign them.\n const videoEl = h(\"video\");\n videoEl.src = videoUrl;\n videoEl.controls = true;\n videoEl.autoplay = true;\n videoEl.muted = true;\n\n const textarea = h(\"textarea\", {\n attrs: {\n placeholder: \"Optional: Describe the issue you recorded...\",\n \"aria-label\": \"Describe the issue you recorded\",\n },\n });\n\n const sendBtn = h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-send\",\n text: \"Send\",\n attrs: { type: \"button\" },\n on: { click: () => this.submitVideoFeedback(textarea.value.trim() || null, sendBtn) },\n });\n\n const box = h(\n \"div\",\n { class: \"qaid-video-preview-box\" },\n title,\n videoEl,\n textarea,\n h(\n \"div\",\n { class: \"qaid-video-preview-actions\" },\n h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-cancel\",\n text: \"Cancel\",\n attrs: { type: \"button\" },\n on: { click: () => this.cancelRecordingPreview() },\n }),\n h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-rerecord\",\n text: \"Re-record\",\n attrs: { type: \"button\" },\n on: {\n click: () => {\n this.cancelRecordingPreview();\n this.startRecording();\n },\n },\n }),\n sendBtn,\n ),\n );\n\n this.videoPreview = h(\n \"div\",\n { class: \"qaid-video-preview\", style: `z-index:${this.host.config.zIndex + 100}` },\n box,\n );\n\n this.host.applyVars(this.videoPreview);\n\n // Enable pointer events on the overlay host for the video preview.\n if (this.host.overlayShadowHost) {\n this.host.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n\n root.appendChild(this.videoPreview);\n\n // Dialog semantics + focus trap + background inert.\n this.host.openDialogA11y(box, { labelledbyId: title.id });\n\n // Listen for the escape key.\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private cancelRecordingPreview(): void {\n this.removeVideoPreview();\n this.cleanupRecording();\n }\n\n private removeVideoPreview(): void {\n // Release focus trap / inert and restore focus before removing the DOM.\n this.host.closeDialogA11y();\n if (this.videoPreview) {\n const videoEl = this.videoPreview.querySelector<HTMLVideoElement>(\"video\");\n if (videoEl?.src) {\n URL.revokeObjectURL(videoEl.src);\n }\n this.videoPreview.remove();\n this.videoPreview = null;\n }\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private async submitVideoFeedback(message: string | null, sendBtn: HTMLButtonElement): Promise<void> {\n if (!this.recordedBlob || this.isSendingVideo) return;\n\n this.isSendingVideo = true;\n sendBtn.disabled = true;\n sendBtn.textContent = \"Sending...\";\n\n const formData = new FormData();\n formData.append(\"video\", this.recordedBlob, `recording.${this.recordedBlob.type.includes(\"mp4\") ? \"mp4\" : \"webm\"}`);\n formData.append(\"pageUrl\", window.location.href);\n formData.append(\"visitorId\", this.host.visitorId);\n\n if (this.host.config.apiKey) {\n formData.append(\"apiKey\", this.host.config.apiKey);\n }\n if (message) {\n formData.append(\"message\", message);\n }\n if (this.host.consoleCapture) {\n formData.append(\"consoleErrors\", JSON.stringify(this.host.consoleCapture.errors));\n }\n if (this.networkCapture) {\n formData.append(\"networkErrors\", JSON.stringify(this.networkCapture.errors));\n }\n\n let videoFeedbackId: string | number | null = null;\n // Set when the upload failed: the reason shown to the visitor.\n let failure: string | null = null;\n try {\n const response = await fetch(`${this.host.config.endpoint}/video`, {\n method: \"POST\",\n body: formData,\n });\n\n if (response.ok) {\n this.host.announceMsg(\"Recording sent\");\n try {\n const data = (await response.json()) as { id?: string | number };\n videoFeedbackId = data?.id ?? null;\n } catch {\n // Non-JSON / no id — the quest can still launch, just unlinked.\n }\n } else {\n // A refusal (403 on a plan without video, 400, 413 too large, 429…)\n // used to close the preview exactly like a success.\n console.error(\"Failed to submit video feedback:\", await response.text());\n failure =\n response.status === 413\n ? \"This recording is too large to send. Try a shorter one.\"\n : \"Your recording could not be sent.\";\n }\n } catch (error) {\n console.error(\"Failed to submit video feedback:\", error);\n failure = \"Your recording could not be sent. Check your connection and try again.\";\n }\n\n this.isSendingVideo = false;\n\n if (failure) {\n this.showSendError(failure, sendBtn);\n return;\n }\n\n this.removeVideoPreview();\n this.cleanupRecording();\n\n // Once the recording is safely sent, launch the linked quest (if any),\n // passing the new feedback record id so its response is joinable.\n await this.host.tryLaunchQuest(\"video\", videoFeedbackId);\n }\n\n /**\n * Keep the preview open and say the upload failed. Closing it, as a success\n * does, told the visitor their recording had arrived when it had not. Send\n * is re-enabled for another try; Cancel and Re-record still work.\n */\n private showSendError(message: string, sendBtn: HTMLButtonElement): void {\n this.host.announceMsg(`Failed to send recording. ${message}`, true);\n sendBtn.disabled = false;\n sendBtn.textContent = \"Send\";\n\n const actions = this.videoPreview?.querySelector(\".qaid-video-preview-actions\");\n if (actions) {\n let note = this.videoPreview!.querySelector<HTMLElement>(\".qaid-video-error\");\n if (!note) {\n note = h(\"p\", { class: \"qaid-video-error\" });\n actions.before(note);\n }\n note.textContent = message;\n }\n }\n\n private cleanupRecording(): void {\n this.isRecording = false;\n this.removeRecordingIndicator();\n\n if (this.videoRecorder) {\n this.videoRecorder.destroy();\n this.videoRecorder = null;\n }\n if (this.networkCapture) {\n this.networkCapture.restore();\n this.networkCapture = null;\n }\n if (this.recordedBlob) {\n this.recordedBlob = null;\n }\n\n this.host.setButtonsDisabled(false);\n\n // Reset overlay host pointer events if nothing else needs them.\n if (this.host.overlayShadowHost && this.host.state === \"IDLE\") {\n this.host.overlayShadowHost.style.pointerEvents = \"none\";\n }\n }\n}\n"],"names":["MAX_ENTRIES","MAX_BODY_SIZE","truncateBody","body","str","addEntry","errors","entry","safeReadBody","response","text","captureNetworkErrors","originalFetch","input","init","url","method","requestBody","responseBody","originalXHROpen","originalXHRSend","rest","xhr","RecordingController","host","e","redactionElements","createVideoRecorder","elapsed","blob","error","root","boxHost","h","container","tick","target","isEmbedElement","idx","label","n","layer","el","r","record","picks","timer","remaining","seconds","m","s","videoUrl","titleId","title","videoEl","textarea","sendBtn","box","message","formData","videoFeedbackId","failure","actions","note"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -16,13 +16,13 @@ const V = `<svg xmlns="http://www.w3.org/2000/svg" class="qaid-icon" fill="none"
|
|
|
16
16
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3s6 6.4 6 10a6 6 0 01-12 0c0-3.6 6-10 6-10z"/>
|
|
17
17
|
</svg>`, ue = `<svg xmlns="http://www.w3.org/2000/svg" class="qaid-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
18
18
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-4"/>
|
|
19
|
-
</svg>`,
|
|
19
|
+
</svg>`, Tt = `<svg xmlns="http://www.w3.org/2000/svg" class="qaid-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
20
20
|
<path stroke-linecap="round" stroke-linejoin="round" d="M20 6L9 17l-5-5"/>
|
|
21
21
|
</svg>`, pe = `<svg xmlns="http://www.w3.org/2000/svg" class="qaid-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
22
22
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v8m0 4h.01"/>
|
|
23
23
|
</svg>`, fe = `<svg xmlns="http://www.w3.org/2000/svg" class="qaid-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
24
24
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6l12 12M18 6L6 18"/>
|
|
25
|
-
</svg>`, me = "button{cursor:pointer}.qaid-buttons{display:flex;gap:.5rem}.qaid-buttons.qaid-vertical{flex-direction:column}.qaid-buttons.qaid-auto-container{position:fixed;z-index:50}.qaid-buttons.qaid-auto-container.qaid-bottom-right{bottom:1rem;right:1rem}.qaid-buttons.qaid-auto-container.qaid-bottom-left{bottom:1rem;left:1rem}.qaid-buttons.qaid-auto-container.qaid-top-right{top:1rem;right:1rem}.qaid-buttons.qaid-auto-container.qaid-top-left{top:1rem;left:1rem}.qaid-buttons.qaid-incognito{opacity:0;transition:opacity .2s ease-in-out}.qaid-buttons.qaid-incognito:hover{opacity:1}.qaid-buttons.qaid-dismissed{display:none!important}.qaid-dismiss-btn{width:20px;height:20px;padding:0;border:none;border-radius:50%;background:#0006;color:#fff;display:flex;align-items:center;justify-content:center;align-self:center;opacity:0;transition:opacity .15s,background .15s;cursor:pointer;pointer-events:auto;-webkit-appearance:none;appearance:none}.qaid-dismiss-btn:hover{background:#0009}.qaid-buttons:hover .qaid-dismiss-btn{opacity:1}button.qaid-btn-structural{display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-appearance:none;appearance:none}.qaid-icon{width:24px;height:24px}:where(button.qaid-btn-structural)>svg:where(:not(.qaid-icon):not([width])){width:var(--qaid-icon-size, 24px);height:var(--qaid-icon-size, 24px)}.qaid-btn-structural:not(:has(svg)),.qaid-btn:not(:has(svg)){font-size:var(--qaid-icon-size, 24px);line-height:1}.qaid-emoji-icon{font-size:var(--qaid-icon-size, 24px);line-height:1}.qaid-buttons,.qaid-buttons *,.qaid-modal-container,.qaid-modal-container *{cursor:pointer!important}.qaid-tooltip-wrapper{position:relative}.qaid-tooltip-text{position:fixed;padding:.5rem .75rem;background:#1f2937;color:#fff;font-size:var(--qaid-font-size, 16px);font-weight:600;border-radius:.5rem;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:99999}.qaid-tooltip-text.qaid-tooltip-visible{opacity:1}.qaid-targeting-overlay{position:fixed;inset:0;z-index:40;pointer-events:none}.qaid-capture-layer{position:fixed;inset:0;pointer-events:none;z-index:9999}@keyframes qaid-slideDown{0%{transform:translateY(-100%)}to{transform:translateY(0)}}.qaid-vignette{position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at center,transparent 30%,rgba(0,0,0,.3) 70%,rgba(0,0,0,.6) 100%);z-index:41}.qaid-crosshair-h,.qaid-crosshair-v{position:fixed;pointer-events:none;z-index:42}.qaid-crosshair-h{left:0;right:0;height:1px;background:color-mix(in srgb,var(--qaid-negative) 60%,transparent)}.qaid-crosshair-v{top:0;bottom:0;width:1px;background:color-mix(in srgb,var(--qaid-negative) 60%,transparent)}.qaid-type-up .qaid-crosshair-h,.qaid-type-up .qaid-crosshair-v{background:color-mix(in srgb,var(--qaid-positive) 60%,transparent)}.qaid-highlight-box{position:fixed;top:0;left:0;pointer-events:none;z-index:41;display:none;border:3px solid color-mix(in srgb,var(--qaid-negative) 80%,transparent);border-radius:2px;background:color-mix(in srgb,var(--qaid-negative) 8%,transparent);will-change:transform,width,height}.qaid-type-up .qaid-highlight-box{border-color:color-mix(in srgb,var(--qaid-positive) 80%,transparent);background:color-mix(in srgb,var(--qaid-positive) 8%,transparent)}.qaid-scope{position:fixed;width:80px;height:80px;pointer-events:none;z-index:43;transform:translate(-50%,-50%)}.qaid-scope-ring{position:absolute;inset:10px;border:2px solid color-mix(in srgb,var(--qaid-negative) 80%,transparent);border-radius:50%}.qaid-type-up .qaid-scope-ring{border-color:color-mix(in srgb,var(--qaid-positive) 80%,transparent)}.qaid-scope-ring-inner{position:absolute;inset:20px;border:1px solid color-mix(in srgb,var(--qaid-negative) 50%,transparent);border-radius:50%}.qaid-type-up .qaid-scope-ring-inner{border-color:color-mix(in srgb,var(--qaid-positive) 50%,transparent)}.qaid-scope-dot{position:absolute;top:50%;left:50%;width:4px;height:4px;background:var(--qaid-negative);border-radius:50%;transform:translate(-50%,-50%)}.qaid-type-up .qaid-scope-dot{background:var(--qaid-positive)}.qaid-selected-marker{position:fixed;border:3px solid var(--qaid-marker, #6365f1);border-radius:50%;pointer-events:none;z-index:44;animation:qaid-markerPulse 1.5s ease-in-out infinite}@keyframes qaid-markerPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(1.05)}}.qaid-backdrop{position:fixed;inset:0;z-index:45;background:#0000004d}.qaid-modal-container{position:fixed;z-index:50;display:flex;flex-direction:column;align-items:flex-start;max-height:calc(100vh - 32px);font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-modal-container.qaid-above{flex-direction:column-reverse}.qaid-modal-arrow{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;position:relative;align-self:flex-start}.qaid-modal-container.qaid-below .qaid-modal-arrow{border-bottom:12px solid light-dark(#ffffff,#1f2937)}.qaid-modal-container.qaid-above .qaid-modal-arrow{border-top:12px solid light-dark(#ffffff,#1f2937)}.qaid-modal-box{color-scheme:inherit;background:light-dark(#ffffff,#1f2937);border-radius:1rem;padding:1.5rem;box-shadow:0 25px 50px -12px light-dark(rgba(0,0,0,.25),rgba(0,0,0,.5));width:var(--qaid-modal-width, 400px);max-width:calc(100vw - 32px);max-height:calc(100vh - 60px);overflow-y:auto}.qaid-modal-header{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:1rem}button.qaid-type-toggle{border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;flex-shrink:0;-webkit-appearance:none;appearance:none}button.qaid-type-toggle:hover{transform:scale(1.1)}button.qaid-type-toggle:not(.qaid-type-toggle-custom){width:2.5rem;height:2.5rem;border-radius:50%}button.qaid-type-toggle:not(.qaid-type-toggle-custom) svg{width:1.25rem;height:1.25rem}button.qaid-type-toggle.qaid-type-up{background:var(--qaid-positive);color:#fff}button.qaid-type-toggle.qaid-type-down{background:var(--qaid-negative);color:#fff}.qaid-type-static{width:2.5rem;height:2.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;background:var(--qaid-marker, #6365f1);color:var(--qaid-marker-text, white)}.qaid-type-static svg{width:1.25rem;height:1.25rem}.qaid-modal-header-text{flex:1;min-width:0}.qaid-modal-title{font-size:calc(var(--qaid-font-size, 16px) * 1.125);font-weight:700;margin:0 0 .25rem;color:light-dark(#1f2937,#f9fafb)}.qaid-modal-subtitle{color:light-dark(#6b7280,#9ca3af);margin:0;font-size:calc(var(--qaid-font-size, 16px) * .875)}.qaid-textarea{width:100%;height:6rem;padding:.75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;font-family:inherit;font-size:var(--qaid-font-size, 16px);resize:vertical;margin-bottom:1rem;box-sizing:border-box;background:light-dark(#ffffff,#111827);color:light-dark(#1f2937,#f9fafb)}.qaid-textarea:focus{outline:none;border-color:var(--qaid-marker);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 20%,transparent)}.qaid-btn-row{display:flex;gap:.5rem;justify-content:flex-end}button.qaid-btn-submit{padding:.5rem 1rem;background:var(--qaid-marker);color:var(--qaid-marker-text, white);border:none;border-radius:.5rem;font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;cursor:pointer;transition:background-color .2s,filter .2s;-webkit-appearance:none;appearance:none}button.qaid-btn-submit:hover{filter:brightness(.85)}button.qaid-btn-submit:focus{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 30%,transparent)}.qaid-bottom-sheet{position:fixed;bottom:0;left:0;right:0;z-index:50;animation:qaid-slideUpSheet .3s ease-out;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-bottom-sheet-content{background:light-dark(#ffffff,#1f2937);border-radius:1rem 1rem 0 0;padding:1.5rem;padding-bottom:max(1.5rem,env(safe-area-inset-bottom))}.qaid-bottom-sheet-handle{width:36px;height:4px;background:light-dark(rgba(0,0,0,.2),rgba(255,255,255,.2));border-radius:2px;margin:0 auto 1rem}@keyframes qaid-slideUpSheet{0%{transform:translateY(100%)}to{transform:translateY(0)}}button.qaid-btn-record:hover{background:#dc2626;color:#fff}.qaid-recording-indicator{position:fixed;top:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:light-dark(#1f2937,#374151);color:#fff;border-radius:9999px;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;box-shadow:0 4px 12px #0000004d;z-index:99999;animation:qaid-slideDown .2s ease-out}.qaid-recording-dot{width:10px;height:10px;background:#dc2626;border-radius:50%;animation:qaid-dotPulse 1.5s ease-in-out infinite}@keyframes qaid-dotPulse{0%,to{opacity:1}50%{opacity:.3}}.qaid-recording-time{font-variant-numeric:tabular-nums;min-width:2.5rem;text-align:center}button.qaid-recording-stop{padding:.25rem .75rem;background:#dc2626;color:#fff;border:none;border-radius:9999px;font-size:calc(var(--qaid-font-size, 16px) * .75);font-weight:600;cursor:pointer;transition:background-color .2s;-webkit-appearance:none;appearance:none}button.qaid-recording-stop:hover{background:#b91c1c}.qaid-video-preview{position:fixed;inset:0 0 auto;height:100vh;height:100dvh;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top));padding-bottom:max(16px,env(safe-area-inset-bottom));box-sizing:border-box;background:#0009;z-index:99998;animation:qaid-fadeIn .2s ease-out}@keyframes qaid-fadeIn{0%{opacity:0}to{opacity:1}}.qaid-video-preview-box{color-scheme:inherit;background:light-dark(#ffffff,#1f2937);border-radius:1rem;padding:1.5rem;box-shadow:0 25px 50px -12px #00000080;width:560px;max-width:100%;max-height:100%;overflow-y:auto;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-video-preview-box h3{font-size:calc(var(--qaid-font-size, 16px) * 1.125);font-weight:700;margin:0 0 1rem;color:light-dark(#1f2937,#f9fafb)}.qaid-video-preview-box video{width:100%;max-height:50vh;max-height:50dvh;object-fit:contain;border-radius:.5rem;background:#000;margin-bottom:1rem}.qaid-video-preview-box textarea{width:100%;height:4rem;padding:.75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;font-family:inherit;font-size:calc(var(--qaid-font-size, 16px) * .875);resize:vertical;margin-bottom:1rem;box-sizing:border-box;background:light-dark(#ffffff,#111827);color:light-dark(#1f2937,#f9fafb)}.qaid-video-preview-box textarea:focus{outline:none;border-color:var(--qaid-marker);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 20%,transparent)}.qaid-video-error{margin:0 0 .75rem;font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;color:light-dark(#b91c1c,#fca5a5)}.qaid-video-preview-actions{display:flex;gap:.5rem;justify-content:flex-end}button.qaid-video-btn{padding:.5rem 1rem;border:none;border-radius:.5rem;font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;cursor:pointer;transition:background-color .2s,filter .2s;-webkit-appearance:none;appearance:none}button.qaid-video-btn-cancel{background:light-dark(#f3f4f6,#374151);color:light-dark(#374151,#d1d5db)}button.qaid-video-btn-cancel:hover{background:light-dark(#e5e7eb,#4b5563)}button.qaid-video-btn-rerecord{background:light-dark(#fef3c7,#78350f);color:light-dark(#92400e,#fde68a)}button.qaid-video-btn-rerecord:hover{filter:brightness(.9)}button.qaid-video-btn-send{background:var(--qaid-marker);color:var(--qaid-marker-text, white)}button.qaid-video-btn-send:hover{filter:brightness(.85)}button.qaid-video-btn-send:disabled{opacity:.5;cursor:not-allowed}.qaid-video-sending{display:flex;align-items:center;gap:.5rem;font-size:calc(var(--qaid-font-size, 16px) * .875);color:light-dark(#6b7280,#9ca3af)}.qaid-annotate{position:fixed;inset:0;z-index:2147483000;display:flex;flex-direction:column;gap:.75rem;padding:16px;padding-top:max(16px,env(safe-area-inset-top));padding-bottom:max(16px,env(safe-area-inset-bottom));box-sizing:border-box;background:#000000d9;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px);animation:qaid-fadeIn .2s ease-out}.qaid-annotate-title{margin:0;font-size:var(--qaid-font-size, 16px);font-weight:700;text-align:center;color:#f9fafb}.qaid-annotate-desc{margin:0;font-size:calc(var(--qaid-font-size, 16px) * .8125);line-height:1.4;text-align:center;color:#f9fafbbf}.qaid-annotate-stage{flex:1 1 auto;min-height:0;display:flex;align-items:center;justify-content:center;overflow:auto}.qaid-annotate-canvas{max-width:100%;max-height:100%;object-fit:contain;background:light-dark(#ffffff,#111827);border-radius:.5rem;box-shadow:0 10px 30px #00000080;touch-action:none;cursor:crosshair}.qaid-annotate-toolbar{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center;justify-content:center;padding:.5rem .75rem;background:light-dark(#ffffff,#1f2937);border-radius:.75rem;box-shadow:0 10px 30px light-dark(rgba(0,0,0,.25),rgba(0,0,0,.5))}.qaid-annotate-tools,.qaid-annotate-actions{display:flex;gap:.375rem;align-items:center}button.qaid-annotate-tool,button.qaid-annotate-action{display:inline-flex;align-items:center;justify-content:center;gap:.375rem;min-width:44px;min-height:44px;padding:0 .75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;background:light-dark(#f9fafb,#111827);color:light-dark(#1f2937,#f9fafb);font-family:inherit;font-size:calc(var(--qaid-font-size, 16px) * .8125);font-weight:600;cursor:pointer;-webkit-appearance:none;appearance:none;transition:background-color .15s,color .15s,border-color .15s,filter .15s}button.qaid-annotate-tool svg,button.qaid-annotate-action svg{width:20px;height:20px}.qaid-annotate-colors{display:flex;gap:.25rem;align-items:center}button.qaid-annotate-swatch{width:28px;height:28px;min-width:28px;padding:0;border-radius:9999px;border:2px solid light-dark(#d1d5db,#4b5563);background:var(--qaid-swatch, #000);cursor:pointer;-webkit-appearance:none;appearance:none;transition:transform .1s,box-shadow .15s,border-color .15s}button.qaid-annotate-swatch:hover{transform:scale(1.12)}button.qaid-annotate-swatch[aria-pressed=true]{border-color:light-dark(#111827,#f9fafb);box-shadow:0 0 0 2px var(--qaid-swatch)}@media(forced-colors:active){button.qaid-annotate-swatch[aria-pressed=true]{outline:2px solid Highlight;outline-offset:1px}}button.qaid-annotate-tool:hover,button.qaid-annotate-action:hover{background:light-dark(#eef2ff,#312e81)}button.qaid-annotate-tool[aria-pressed=true],button.qaid-annotate-done{background:var(--qaid-marker, #6365f1);color:var(--qaid-marker-text, #fff);border-color:var(--qaid-marker, #6365f1)}button.qaid-annotate-done:hover{filter:brightness(.9);background:var(--qaid-marker, #6365f1)}button.qaid-annotate-action:disabled{opacity:.45;cursor:not-allowed}@media(forced-colors:active){button.qaid-annotate-tool[aria-pressed=true],button.qaid-annotate-done{border:2px solid Highlight}.qaid-annotate-canvas{border:1px solid CanvasText}}button:focus-visible,textarea:focus-visible,a:focus-visible,[tabindex]:focus-visible,[role=button]:focus-visible{outline:2px solid var(--qaid-marker, #6365f1);outline-offset:2px}.qaid-textarea:focus-visible,button.qaid-btn-submit:focus-visible,.qaid-video-preview-box textarea:focus-visible{outline:2px solid var(--qaid-marker, #6365f1);outline-offset:2px}.qaid-buttons.qaid-incognito:focus-within{opacity:1}.qaid-buttons:focus-within .qaid-dismiss-btn,.qaid-dismiss-btn:focus-visible{opacity:1}.qaid-dismiss-btn{min-width:24px;min-height:24px}button.qaid-recording-stop{min-height:24px}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}@media(forced-colors:active){button:focus-visible,textarea:focus-visible,a:focus-visible,[tabindex]:focus-visible,[role=button]:focus-visible,.qaid-textarea:focus,button.qaid-btn-submit:focus,.qaid-video-preview-box textarea:focus{outline:2px solid CanvasText;outline-offset:2px}.qaid-selected-marker,.qaid-highlight-box{border-color:Highlight}}.qaid-confirm{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.5rem;padding:.5rem 0}.qaid-confirm-icon{display:flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:9999px;color:#fff;background:var(--qaid-success, #22c55e);margin-bottom:.25rem;animation:qaid-confirm-pop .28s cubic-bezier(.34,1.56,.64,1)}.qaid-confirm-icon .qaid-icon{width:24px;height:24px}.qaid-confirm-error .qaid-confirm-icon{background:var(--qaid-error, #dc2626)}.qaid-confirm-title{margin:0;font-size:var(--qaid-font-size, 16px);font-weight:600;color:var(--qaid-text, light-dark(#111827, #f9fafb))}.qaid-confirm-title:focus-visible{outline:2px solid var(--qaid-primary, #6365f1);outline-offset:3px;border-radius:4px}.qaid-confirm-message{margin:0;font-size:calc(var(--qaid-font-size, 16px) * .875);color:var(--qaid-text-muted, light-dark(#6b7280, #9ca3af))}.qaid-confirm .qaid-confirm-close{margin-top:.5rem;min-width:96px}@keyframes qaid-confirm-pop{0%{transform:scale(.6);opacity:0}to{transform:scale(1);opacity:1}}@media(prefers-reduced-motion:reduce){.qaid-confirm-icon{animation:none}}", be = "body.qaid-targeting,body.qaid-targeting *{cursor:none!important}";
|
|
25
|
+
</svg>`, me = "button{cursor:pointer}.qaid-buttons{display:flex;gap:.5rem}.qaid-buttons.qaid-vertical{flex-direction:column}.qaid-buttons.qaid-auto-container{position:fixed;z-index:50}.qaid-buttons.qaid-auto-container.qaid-bottom-right{bottom:1rem;right:1rem}.qaid-buttons.qaid-auto-container.qaid-bottom-left{bottom:1rem;left:1rem}.qaid-buttons.qaid-auto-container.qaid-top-right{top:1rem;right:1rem}.qaid-buttons.qaid-auto-container.qaid-top-left{top:1rem;left:1rem}.qaid-buttons.qaid-incognito{opacity:0;transition:opacity .2s ease-in-out}.qaid-buttons.qaid-incognito:hover{opacity:1}.qaid-buttons.qaid-dismissed{display:none!important}.qaid-dismiss-btn{width:20px;height:20px;padding:0;border:none;border-radius:50%;background:#0006;color:#fff;display:flex;align-items:center;justify-content:center;align-self:center;opacity:0;transition:opacity .15s,background .15s;cursor:pointer;pointer-events:auto;-webkit-appearance:none;appearance:none}.qaid-dismiss-btn:hover{background:#0009}.qaid-buttons:hover .qaid-dismiss-btn{opacity:1}button.qaid-btn-structural{display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-appearance:none;appearance:none}.qaid-icon{width:24px;height:24px}:where(button.qaid-btn-structural)>svg:where(:not(.qaid-icon):not([width])){width:var(--qaid-icon-size, 24px);height:var(--qaid-icon-size, 24px)}.qaid-btn-structural:not(:has(svg)),.qaid-btn:not(:has(svg)){font-size:var(--qaid-icon-size, 24px);line-height:1}.qaid-emoji-icon{font-size:var(--qaid-icon-size, 24px);line-height:1}.qaid-buttons,.qaid-buttons *,.qaid-modal-container,.qaid-modal-container *{cursor:pointer!important}.qaid-tooltip-wrapper{position:relative}.qaid-tooltip-text{position:fixed;padding:.5rem .75rem;background:#1f2937;color:#fff;font-size:var(--qaid-font-size, 16px);font-weight:600;border-radius:.5rem;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:99999}.qaid-tooltip-text.qaid-tooltip-visible{opacity:1}.qaid-targeting-overlay{position:fixed;inset:0;z-index:40;pointer-events:none}.qaid-capture-layer{position:fixed;inset:0;pointer-events:none;z-index:9999}@keyframes qaid-slideDown{0%{transform:translateY(-100%)}to{transform:translateY(0)}}.qaid-vignette{position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at center,transparent 30%,rgba(0,0,0,.3) 70%,rgba(0,0,0,.6) 100%);z-index:41}.qaid-crosshair-h,.qaid-crosshair-v{position:fixed;pointer-events:none;z-index:42}.qaid-crosshair-h{left:0;right:0;height:1px;background:color-mix(in srgb,var(--qaid-negative) 60%,transparent)}.qaid-crosshair-v{top:0;bottom:0;width:1px;background:color-mix(in srgb,var(--qaid-negative) 60%,transparent)}.qaid-type-up .qaid-crosshair-h,.qaid-type-up .qaid-crosshair-v{background:color-mix(in srgb,var(--qaid-positive) 60%,transparent)}.qaid-highlight-box{position:fixed;top:0;left:0;pointer-events:none;z-index:41;display:none;border:3px solid color-mix(in srgb,var(--qaid-negative) 80%,transparent);border-radius:2px;background:color-mix(in srgb,var(--qaid-negative) 8%,transparent);will-change:transform,width,height}.qaid-type-up .qaid-highlight-box{border-color:color-mix(in srgb,var(--qaid-positive) 80%,transparent);background:color-mix(in srgb,var(--qaid-positive) 8%,transparent)}.qaid-scope{position:fixed;width:80px;height:80px;pointer-events:none;z-index:43;transform:translate(-50%,-50%)}.qaid-scope-ring{position:absolute;inset:10px;border:2px solid color-mix(in srgb,var(--qaid-negative) 80%,transparent);border-radius:50%}.qaid-type-up .qaid-scope-ring{border-color:color-mix(in srgb,var(--qaid-positive) 80%,transparent)}.qaid-scope-ring-inner{position:absolute;inset:20px;border:1px solid color-mix(in srgb,var(--qaid-negative) 50%,transparent);border-radius:50%}.qaid-type-up .qaid-scope-ring-inner{border-color:color-mix(in srgb,var(--qaid-positive) 50%,transparent)}.qaid-scope-dot{position:absolute;top:50%;left:50%;width:4px;height:4px;background:var(--qaid-negative);border-radius:50%;transform:translate(-50%,-50%)}.qaid-type-up .qaid-scope-dot{background:var(--qaid-positive)}.qaid-selected-marker{position:fixed;border:3px solid var(--qaid-marker, #6365f1);border-radius:50%;pointer-events:none;z-index:44;animation:qaid-markerPulse 1.5s ease-in-out infinite}@keyframes qaid-markerPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(1.05)}}.qaid-backdrop{position:fixed;inset:0;z-index:45;background:#0000004d}.qaid-modal-container{position:fixed;z-index:50;display:flex;flex-direction:column;align-items:flex-start;max-height:calc(100vh - 32px);font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-modal-container.qaid-above{flex-direction:column-reverse}.qaid-modal-arrow{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;position:relative;align-self:flex-start}.qaid-modal-container.qaid-below .qaid-modal-arrow{border-bottom:12px solid light-dark(#ffffff,#1f2937)}.qaid-modal-container.qaid-above .qaid-modal-arrow{border-top:12px solid light-dark(#ffffff,#1f2937)}.qaid-modal-box{color-scheme:inherit;background:light-dark(#ffffff,#1f2937);border-radius:1rem;padding:1.5rem;box-shadow:0 25px 50px -12px light-dark(rgba(0,0,0,.25),rgba(0,0,0,.5));width:var(--qaid-modal-width, 400px);max-width:calc(100vw - 32px);max-height:calc(100vh - 60px);overflow-y:auto}.qaid-modal-header{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:1rem}button.qaid-type-toggle{border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;flex-shrink:0;-webkit-appearance:none;appearance:none}button.qaid-type-toggle:hover{transform:scale(1.1)}button.qaid-type-toggle:not(.qaid-type-toggle-custom){width:2.5rem;height:2.5rem;border-radius:50%}button.qaid-type-toggle:not(.qaid-type-toggle-custom) svg{width:1.25rem;height:1.25rem}button.qaid-type-toggle.qaid-type-up{background:var(--qaid-positive);color:#fff}button.qaid-type-toggle.qaid-type-down{background:var(--qaid-negative);color:#fff}.qaid-type-static{width:2.5rem;height:2.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;background:var(--qaid-marker, #6365f1);color:var(--qaid-marker-text, white)}.qaid-type-static svg{width:1.25rem;height:1.25rem}.qaid-modal-header-text{flex:1;min-width:0}.qaid-modal-title{font-size:calc(var(--qaid-font-size, 16px) * 1.125);font-weight:700;margin:0 0 .25rem;color:light-dark(#1f2937,#f9fafb)}.qaid-modal-subtitle{color:light-dark(#6b7280,#9ca3af);margin:0;font-size:calc(var(--qaid-font-size, 16px) * .875)}.qaid-textarea{width:100%;height:6rem;padding:.75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;font-family:inherit;font-size:var(--qaid-font-size, 16px);resize:vertical;margin-bottom:1rem;box-sizing:border-box;background:light-dark(#ffffff,#111827);color:light-dark(#1f2937,#f9fafb)}.qaid-textarea:focus{outline:none;border-color:var(--qaid-marker);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 20%,transparent)}.qaid-btn-row{display:flex;gap:.5rem;justify-content:flex-end}button.qaid-btn-submit{padding:.5rem 1rem;background:var(--qaid-marker);color:var(--qaid-marker-text, white);border:none;border-radius:.5rem;font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;cursor:pointer;transition:background-color .2s,filter .2s;-webkit-appearance:none;appearance:none}button.qaid-btn-submit:hover{filter:brightness(.85)}button.qaid-btn-submit:focus{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 30%,transparent)}.qaid-bottom-sheet{position:fixed;bottom:0;left:0;right:0;z-index:50;animation:qaid-slideUpSheet .3s ease-out;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-bottom-sheet-content{background:light-dark(#ffffff,#1f2937);border-radius:1rem 1rem 0 0;padding:1.5rem;padding-bottom:max(1.5rem,env(safe-area-inset-bottom))}.qaid-bottom-sheet-handle{width:36px;height:4px;background:light-dark(rgba(0,0,0,.2),rgba(255,255,255,.2));border-radius:2px;margin:0 auto 1rem}@keyframes qaid-slideUpSheet{0%{transform:translateY(100%)}to{transform:translateY(0)}}button.qaid-btn-record:hover{background:#dc2626;color:#fff}.qaid-recording-indicator{position:fixed;pointer-events:auto;top:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:light-dark(#1f2937,#374151);color:#fff;border-radius:9999px;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;box-shadow:0 4px 12px #0000004d;z-index:99999;animation:qaid-slideDown .2s ease-out}.qaid-recording-dot{width:10px;height:10px;background:#dc2626;border-radius:50%;animation:qaid-dotPulse 1.5s ease-in-out infinite}@keyframes qaid-dotPulse{0%,to{opacity:1}50%{opacity:.3}}.qaid-recording-time{font-variant-numeric:tabular-nums;min-width:2.5rem;text-align:center}button.qaid-recording-stop{padding:.25rem .75rem;background:#dc2626;color:#fff;border:none;border-radius:9999px;font-size:calc(var(--qaid-font-size, 16px) * .75);font-weight:600;cursor:pointer;transition:background-color .2s;-webkit-appearance:none;appearance:none}button.qaid-recording-stop:hover{background:#b91c1c}.qaid-video-preview{position:fixed;inset:0 0 auto;height:100vh;height:100dvh;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top));padding-bottom:max(16px,env(safe-area-inset-bottom));box-sizing:border-box;background:#0009;z-index:99998;animation:qaid-fadeIn .2s ease-out}@keyframes qaid-fadeIn{0%{opacity:0}to{opacity:1}}.qaid-video-preview-box{color-scheme:inherit;background:light-dark(#ffffff,#1f2937);border-radius:1rem;padding:1.5rem;box-shadow:0 25px 50px -12px #00000080;width:560px;max-width:100%;max-height:100%;overflow-y:auto;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-video-preview-box h3{font-size:calc(var(--qaid-font-size, 16px) * 1.125);font-weight:700;margin:0 0 1rem;color:light-dark(#1f2937,#f9fafb)}.qaid-video-preview-box video{width:100%;max-height:50vh;max-height:50dvh;object-fit:contain;border-radius:.5rem;background:#000;margin-bottom:1rem}.qaid-video-preview-box textarea{width:100%;height:4rem;padding:.75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;font-family:inherit;font-size:calc(var(--qaid-font-size, 16px) * .875);resize:vertical;margin-bottom:1rem;box-sizing:border-box;background:light-dark(#ffffff,#111827);color:light-dark(#1f2937,#f9fafb)}.qaid-video-preview-box textarea:focus{outline:none;border-color:var(--qaid-marker);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 20%,transparent)}.qaid-video-error{margin:0 0 .75rem;font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;color:light-dark(#b91c1c,#fca5a5)}.qaid-video-preview-actions{display:flex;gap:.5rem;justify-content:flex-end}button.qaid-video-btn{padding:.5rem 1rem;border:none;border-radius:.5rem;font-size:calc(var(--qaid-font-size, 16px) * .875);font-weight:500;cursor:pointer;transition:background-color .2s,filter .2s;-webkit-appearance:none;appearance:none}button.qaid-video-btn-cancel{background:light-dark(#f3f4f6,#374151);color:light-dark(#374151,#d1d5db)}button.qaid-video-btn-cancel:hover{background:light-dark(#e5e7eb,#4b5563)}button.qaid-video-btn-rerecord{background:light-dark(#fef3c7,#78350f);color:light-dark(#92400e,#fde68a)}button.qaid-video-btn-rerecord:hover{filter:brightness(.9)}button.qaid-video-btn-send{background:var(--qaid-marker);color:var(--qaid-marker-text, white)}button.qaid-video-btn-send:hover{filter:brightness(.85)}button.qaid-video-btn-send:disabled{opacity:.5;cursor:not-allowed}.qaid-video-sending{display:flex;align-items:center;gap:.5rem;font-size:calc(var(--qaid-font-size, 16px) * .875);color:light-dark(#6b7280,#9ca3af)}.qaid-annotate{position:fixed;inset:0;z-index:2147483000;display:flex;flex-direction:column;gap:.75rem;padding:16px;padding-top:max(16px,env(safe-area-inset-top));padding-bottom:max(16px,env(safe-area-inset-bottom));box-sizing:border-box;background:#000000d9;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px);animation:qaid-fadeIn .2s ease-out}.qaid-annotate-title{margin:0;font-size:var(--qaid-font-size, 16px);font-weight:700;text-align:center;color:#f9fafb}.qaid-annotate-desc{margin:0;font-size:calc(var(--qaid-font-size, 16px) * .8125);line-height:1.4;text-align:center;color:#f9fafbbf}.qaid-annotate-stage{flex:1 1 auto;min-height:0;display:flex;align-items:center;justify-content:center;overflow:auto}.qaid-annotate-canvas{max-width:100%;max-height:100%;object-fit:contain;background:light-dark(#ffffff,#111827);border-radius:.5rem;box-shadow:0 10px 30px #00000080;touch-action:none;cursor:crosshair}.qaid-annotate-toolbar{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center;justify-content:center;padding:.5rem .75rem;background:light-dark(#ffffff,#1f2937);border-radius:.75rem;box-shadow:0 10px 30px light-dark(rgba(0,0,0,.25),rgba(0,0,0,.5))}.qaid-annotate-tools,.qaid-annotate-actions{display:flex;gap:.375rem;align-items:center}button.qaid-annotate-tool,button.qaid-annotate-action{display:inline-flex;align-items:center;justify-content:center;gap:.375rem;min-width:44px;min-height:44px;padding:0 .75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;background:light-dark(#f9fafb,#111827);color:light-dark(#1f2937,#f9fafb);font-family:inherit;font-size:calc(var(--qaid-font-size, 16px) * .8125);font-weight:600;cursor:pointer;-webkit-appearance:none;appearance:none;transition:background-color .15s,color .15s,border-color .15s,filter .15s}button.qaid-annotate-tool svg,button.qaid-annotate-action svg{width:20px;height:20px}.qaid-annotate-colors{display:flex;gap:.25rem;align-items:center}button.qaid-annotate-swatch{width:28px;height:28px;min-width:28px;padding:0;border-radius:9999px;border:2px solid light-dark(#d1d5db,#4b5563);background:var(--qaid-swatch, #000);cursor:pointer;-webkit-appearance:none;appearance:none;transition:transform .1s,box-shadow .15s,border-color .15s}button.qaid-annotate-swatch:hover{transform:scale(1.12)}button.qaid-annotate-swatch[aria-pressed=true]{border-color:light-dark(#111827,#f9fafb);box-shadow:0 0 0 2px var(--qaid-swatch)}@media(forced-colors:active){button.qaid-annotate-swatch[aria-pressed=true]{outline:2px solid Highlight;outline-offset:1px}}button.qaid-annotate-tool:hover,button.qaid-annotate-action:hover{background:light-dark(#eef2ff,#312e81)}button.qaid-annotate-tool[aria-pressed=true],button.qaid-annotate-done{background:var(--qaid-marker, #6365f1);color:var(--qaid-marker-text, #fff);border-color:var(--qaid-marker, #6365f1)}button.qaid-annotate-done:hover{filter:brightness(.9);background:var(--qaid-marker, #6365f1)}button.qaid-annotate-action:disabled{opacity:.45;cursor:not-allowed}@media(forced-colors:active){button.qaid-annotate-tool[aria-pressed=true],button.qaid-annotate-done{border:2px solid Highlight}.qaid-annotate-canvas{border:1px solid CanvasText}}button:focus-visible,textarea:focus-visible,a:focus-visible,[tabindex]:focus-visible,[role=button]:focus-visible{outline:2px solid var(--qaid-marker, #6365f1);outline-offset:2px}.qaid-textarea:focus-visible,button.qaid-btn-submit:focus-visible,.qaid-video-preview-box textarea:focus-visible{outline:2px solid var(--qaid-marker, #6365f1);outline-offset:2px}.qaid-buttons.qaid-incognito:focus-within{opacity:1}.qaid-buttons:focus-within .qaid-dismiss-btn,.qaid-dismiss-btn:focus-visible{opacity:1}.qaid-dismiss-btn{min-width:24px;min-height:24px}button.qaid-recording-stop{min-height:24px}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}@media(forced-colors:active){button:focus-visible,textarea:focus-visible,a:focus-visible,[tabindex]:focus-visible,[role=button]:focus-visible,.qaid-textarea:focus,button.qaid-btn-submit:focus,.qaid-video-preview-box textarea:focus{outline:2px solid CanvasText;outline-offset:2px}.qaid-selected-marker,.qaid-highlight-box{border-color:Highlight}}.qaid-confirm{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.5rem;padding:.5rem 0}.qaid-confirm-icon{display:flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:9999px;color:#fff;background:var(--qaid-success, #22c55e);margin-bottom:.25rem;animation:qaid-confirm-pop .28s cubic-bezier(.34,1.56,.64,1)}.qaid-confirm-icon .qaid-icon{width:24px;height:24px}.qaid-confirm-error .qaid-confirm-icon{background:var(--qaid-error, #dc2626)}.qaid-confirm-title{margin:0;font-size:var(--qaid-font-size, 16px);font-weight:600;color:var(--qaid-text, light-dark(#111827, #f9fafb))}.qaid-confirm-title:focus-visible{outline:2px solid var(--qaid-primary, #6365f1);outline-offset:3px;border-radius:4px}.qaid-confirm-message{margin:0;font-size:calc(var(--qaid-font-size, 16px) * .875);color:var(--qaid-text-muted, light-dark(#6b7280, #9ca3af))}.qaid-confirm .qaid-confirm-close{margin-top:.5rem;min-width:96px}@keyframes qaid-confirm-pop{0%{transform:scale(.6);opacity:0}to{transform:scale(1);opacity:1}}@media(prefers-reduced-motion:reduce){.qaid-confirm-icon{animation:none}}", be = "body.qaid-targeting,body.qaid-targeting *{cursor:none!important}";
|
|
26
26
|
let D = 0;
|
|
27
27
|
const ge = "button.qaid-btn{width:var(--qaid-btn-size);height:var(--qaid-btn-size);border-radius:50%;border:none;background:#f3f4f6;color:#374151;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);transition:background-color .2s,color .2s,transform .2s;-webkit-appearance:none;appearance:none;--qaid-hover-up-bg:var(--qaid-positive);--qaid-hover-up-color:#fff;--qaid-hover-down-bg:var(--qaid-negative);--qaid-hover-down-color:#fff}button.qaid-btn:hover{transform:scale(1.05)}button.qaid-btn-up:hover{background:var(--qaid-hover-up-bg);color:var(--qaid-hover-up-color)}button.qaid-btn-down:hover{background:var(--qaid-hover-down-bg);color:var(--qaid-hover-down-color)}button.qaid-btn-feedback:hover{background:var(--qaid-marker,#6365f1);color:var(--qaid-marker-text,#fff)}button.qaid-btn svg{width:var(--qaid-icon-size);height:var(--qaid-icon-size)}", ve = {
|
|
28
28
|
small: 36,
|
|
@@ -93,10 +93,10 @@ function Se() {
|
|
|
93
93
|
const i = document.createElement("style");
|
|
94
94
|
i.id = "qaid-styles", i.textContent = be, document.head.appendChild(i);
|
|
95
95
|
}
|
|
96
|
-
function
|
|
96
|
+
function Te() {
|
|
97
97
|
D <= 0 || (D--, D === 0 && document.getElementById("qaid-styles")?.remove());
|
|
98
98
|
}
|
|
99
|
-
const rt = "data-qaid-a11y-live",
|
|
99
|
+
const rt = "data-qaid-a11y-live", Ee = "position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;", Ae = [
|
|
100
100
|
"a[href]",
|
|
101
101
|
"button",
|
|
102
102
|
"input",
|
|
@@ -113,7 +113,7 @@ function Le(i, t) {
|
|
|
113
113
|
);
|
|
114
114
|
if (o) return o;
|
|
115
115
|
const s = Ie(i).createElement("div");
|
|
116
|
-
return s.setAttribute(rt, e), s.setAttribute("role", t ? "alert" : "status"), s.setAttribute("aria-live", t ? "assertive" : "polite"), s.setAttribute("aria-atomic", "true"), s.style.cssText =
|
|
116
|
+
return s.setAttribute(rt, e), s.setAttribute("role", t ? "alert" : "status"), s.setAttribute("aria-live", t ? "assertive" : "polite"), s.setAttribute("aria-atomic", "true"), s.style.cssText = Ee, i.appendChild(s), s;
|
|
117
117
|
}
|
|
118
118
|
function De(i, t, e = {}) {
|
|
119
119
|
const o = Le(i, !!e.assertive);
|
|
@@ -138,7 +138,7 @@ function dt(i) {
|
|
|
138
138
|
i.querySelectorAll(Ae)
|
|
139
139
|
).filter((e) => !(e.getAttribute("tabindex") === "-1" || e instanceof HTMLInputElement && e.type === "hidden" || e instanceof HTMLAnchorElement && !e.getAttribute("href") || Me(e) || Re(e)));
|
|
140
140
|
}
|
|
141
|
-
function
|
|
141
|
+
function Et(i) {
|
|
142
142
|
let t = i.activeElement;
|
|
143
143
|
for (; t && t.shadowRoot && t.shadowRoot.activeElement; )
|
|
144
144
|
t = t.shadowRoot.activeElement;
|
|
@@ -154,7 +154,7 @@ function Pe(i) {
|
|
|
154
154
|
s.preventDefault(), i.focus();
|
|
155
155
|
return;
|
|
156
156
|
}
|
|
157
|
-
const c = a[0], l = a[a.length - 1], d =
|
|
157
|
+
const c = a[0], l = a[a.length - 1], d = Et(t), r = d ? a.indexOf(d) !== -1 : !1;
|
|
158
158
|
s.shiftKey ? (!r || d === c) && (s.preventDefault(), l.focus()) : (!r || d === l) && (s.preventDefault(), c.focus());
|
|
159
159
|
};
|
|
160
160
|
i.addEventListener("keydown", o);
|
|
@@ -166,7 +166,7 @@ function Pe(i) {
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
function Be() {
|
|
169
|
-
return
|
|
169
|
+
return Et(document);
|
|
170
170
|
}
|
|
171
171
|
function He(i) {
|
|
172
172
|
if (!(!i || typeof i.focus != "function"))
|
|
@@ -1098,7 +1098,7 @@ class ni {
|
|
|
1098
1098
|
const t = window;
|
|
1099
1099
|
this.prewarmIsTimeout ? clearTimeout(this.prewarmHandle) : t.cancelIdleCallback?.(this.prewarmHandle), this.prewarmHandle = null;
|
|
1100
1100
|
}
|
|
1101
|
-
this.recording?.destroy(), this.recording = null, this.modal?.destroy(), this.modal = null, this.activeQuest?.destroy(), this.activeQuest = null, this.consoleCapture && (this.consoleCapture.restore(), this.consoleCapture = null), window.removeEventListener("resize", this.boundResize), document.removeEventListener("keydown", this.boundKeyDown), document.body.classList.remove("qaid-targeting", "qaid-type-up"), document.body.style.removeProperty("--qaid-positive"), document.body.style.removeProperty("--qaid-negative"), this.shadowHost && (this.shadowHost.remove(), this.shadowHost = null, this.shadowRoot = null), this.overlayShadowHost && (this.overlayShadowHost.remove(), this.overlayShadowHost = null, this.overlayShadowRoot = null), this.buttonsContainer = null, this.dismissBtn = null, this.tooltipElement = null,
|
|
1101
|
+
this.recording?.destroy(), this.recording = null, this.modal?.destroy(), this.modal = null, this.activeQuest?.destroy(), this.activeQuest = null, this.consoleCapture && (this.consoleCapture.restore(), this.consoleCapture = null), window.removeEventListener("resize", this.boundResize), document.removeEventListener("keydown", this.boundKeyDown), document.body.classList.remove("qaid-targeting", "qaid-type-up"), document.body.style.removeProperty("--qaid-positive"), document.body.style.removeProperty("--qaid-negative"), this.shadowHost && (this.shadowHost.remove(), this.shadowHost = null, this.shadowRoot = null), this.overlayShadowHost && (this.overlayShadowHost.remove(), this.overlayShadowHost = null, this.overlayShadowRoot = null), this.buttonsContainer = null, this.dismissBtn = null, this.tooltipElement = null, Te();
|
|
1102
1102
|
}
|
|
1103
1103
|
}
|
|
1104
1104
|
function It(i) {
|
|
@@ -1156,7 +1156,7 @@ function M(i, t) {
|
|
|
1156
1156
|
function di(i) {
|
|
1157
1157
|
const t = i.getAttribute("data-endpoint");
|
|
1158
1158
|
if (!t) return null;
|
|
1159
|
-
const e = i.getAttribute("data-position"), o = i.getAttribute("data-zindex"), n = i.getAttribute("data-positive-color"), s = i.getAttribute("data-negative-color"), a = i.getAttribute("data-marker-color"), c = i.getAttribute("data-container"), l = i.getAttribute("data-button-class"), d = i.getAttribute("data-skip-targeting"), r = i.getAttribute("data-hide-confirmation"), h = i.getAttribute("data-single-button"), f = i.getAttribute("data-feedback-mode"), b = i.getAttribute("data-incognito"), y = i.getAttribute("data-button-size"), g = i.getAttribute("data-offset-x"), w = i.getAttribute("data-offset-y"), x = i.getAttribute("data-modal-width"), u = i.getAttribute("data-backdrop-opacity"), k = i.getAttribute("data-font-family"), v = i.getAttribute("data-font-size"), C = i.getAttribute("data-tooltip"), A = i.getAttribute("data-modal-title"), S = i.getAttribute("data-confirmation-title"), m = i.getAttribute("data-confirmation-message"),
|
|
1159
|
+
const e = i.getAttribute("data-position"), o = i.getAttribute("data-zindex"), n = i.getAttribute("data-positive-color"), s = i.getAttribute("data-negative-color"), a = i.getAttribute("data-marker-color"), c = i.getAttribute("data-container"), l = i.getAttribute("data-button-class"), d = i.getAttribute("data-skip-targeting"), r = i.getAttribute("data-hide-confirmation"), h = i.getAttribute("data-single-button"), f = i.getAttribute("data-feedback-mode"), b = i.getAttribute("data-incognito"), y = i.getAttribute("data-button-size"), g = i.getAttribute("data-offset-x"), w = i.getAttribute("data-offset-y"), x = i.getAttribute("data-modal-width"), u = i.getAttribute("data-backdrop-opacity"), k = i.getAttribute("data-font-family"), v = i.getAttribute("data-font-size"), C = i.getAttribute("data-tooltip"), A = i.getAttribute("data-modal-title"), S = i.getAttribute("data-confirmation-title"), m = i.getAttribute("data-confirmation-message"), T = i.getAttribute("data-confirmation-close"), K = i.getAttribute("data-error-title"), W = i.getAttribute("data-error-message"), X = i.getAttribute("data-modal-subtitle"), Y = i.getAttribute("data-placeholder"), Q = i.getAttribute("data-submit-button"), J = i.getAttribute("data-skip-button"), Nt = i.getAttribute("data-positive-icon"), Ft = i.getAttribute("data-negative-icon"), Vt = i.getAttribute("data-feedback-icon"), G = i.getAttribute("data-feedback-label"), Ut = i.getAttribute("data-api-key"), jt = i.getAttribute("data-capture-screenshot"), Kt = i.getAttribute("data-annotate"), Wt = i.getAttribute("data-annotation-color"), O = i.getAttribute("data-screenshot-quality"), z = i.getAttribute("data-screenshot-max-width"), $ = i.getAttribute("data-screenshot-max-height"), Xt = i.getAttribute("data-capture-video"), Z = i.getAttribute("data-video-redaction"), Yt = i.getAttribute("data-hide-thumbs"), Qt = i.getAttribute("data-hide-dismiss"), _ = i.getAttribute("data-video-max-duration"), Jt = i.getAttribute("data-screenshot-method"), Gt = i.getAttribute("data-direction"), tt = i.getAttribute("data-css-selector"), Zt = i.getAttribute("data-css"), et = M("data-positive-label", i.getAttribute("data-positive-label")), it = M("data-negative-label", i.getAttribute("data-negative-label")), ot = M("data-record-label", i.getAttribute("data-record-label")), nt = M("data-dismiss-label", i.getAttribute("data-dismiss-label")), te = ri(
|
|
1160
1160
|
"data-annotation-palette",
|
|
1161
1161
|
i.getAttribute("data-annotation-palette")
|
|
1162
1162
|
), ee = i.getAttribute("data-record-icon"), st = i.getAttribute("data-quest-base"), ie = i.getAttribute("data-quest-up"), oe = i.getAttribute("data-quest-down"), ne = i.getAttribute("data-quest-video"), se = i.getAttribute("data-quest-api-key"), ae = i.getAttribute("data-quest-module-url");
|
|
@@ -1200,7 +1200,7 @@ function di(i) {
|
|
|
1200
1200
|
negative: s ?? void 0,
|
|
1201
1201
|
marker: a ?? void 0
|
|
1202
1202
|
},
|
|
1203
|
-
text: C || A || X || Y || Q || J || G || S || m ||
|
|
1203
|
+
text: C || A || X || Y || Q || J || G || S || m || T || K || W || et || it || ot || nt ? {
|
|
1204
1204
|
tooltip: C ?? void 0,
|
|
1205
1205
|
modalTitle: A ?? void 0,
|
|
1206
1206
|
modalSubtitle: X ?? void 0,
|
|
@@ -1210,7 +1210,7 @@ function di(i) {
|
|
|
1210
1210
|
feedbackLabel: G ?? void 0,
|
|
1211
1211
|
confirmationTitle: S ?? void 0,
|
|
1212
1212
|
confirmationMessage: m ?? void 0,
|
|
1213
|
-
confirmationClose:
|
|
1213
|
+
confirmationClose: T ?? void 0,
|
|
1214
1214
|
errorTitle: K ?? void 0,
|
|
1215
1215
|
errorMessage: W ?? void 0,
|
|
1216
1216
|
positiveLabel: et,
|
|
@@ -1479,7 +1479,7 @@ function xi(i = {}) {
|
|
|
1479
1479
|
blurRadius: n
|
|
1480
1480
|
});
|
|
1481
1481
|
if (!m)
|
|
1482
|
-
throw a && (a.stop(), a = null), s.getTracks().forEach((
|
|
1482
|
+
throw a && (a.stop(), a = null), s.getTracks().forEach((T) => T.stop()), s = null, new Error(Rt);
|
|
1483
1483
|
c = m, S = m.stream;
|
|
1484
1484
|
}
|
|
1485
1485
|
l = new MediaRecorder(S, {
|
|
@@ -1489,7 +1489,7 @@ function xi(i = {}) {
|
|
|
1489
1489
|
m.data.size > 0 && d.push(m.data);
|
|
1490
1490
|
}, l.onstop = () => {
|
|
1491
1491
|
const m = new Blob(d, { type: v });
|
|
1492
|
-
y && y(m), h && h(m), a && (a.stop(), a = null), c && (c.stop(), c = null), s && s.getTracks().forEach((
|
|
1492
|
+
y && y(m), h && h(m), a && (a.stop(), a = null), c && (c.stop(), c = null), s && s.getTracks().forEach((T) => T.stop());
|
|
1493
1493
|
}, l.onerror = () => {
|
|
1494
1494
|
g && g(new Error("MediaRecorder error"));
|
|
1495
1495
|
}, l.start(1e3), b = Date.now(), f = setInterval(() => {
|
|
@@ -1529,7 +1529,7 @@ function Si() {
|
|
|
1529
1529
|
const i = document.createElement("canvas");
|
|
1530
1530
|
return typeof i.getContext == "function" && !!i.getContext("2d");
|
|
1531
1531
|
}
|
|
1532
|
-
function
|
|
1532
|
+
function Ti() {
|
|
1533
1533
|
return window.html2canvas ? Promise.resolve(!0) : L || (L = new Promise((i) => {
|
|
1534
1534
|
if (!document.querySelector(
|
|
1535
1535
|
`script[src="${gt}"]`
|
|
@@ -1551,10 +1551,10 @@ function Ei() {
|
|
|
1551
1551
|
o();
|
|
1552
1552
|
}), L);
|
|
1553
1553
|
}
|
|
1554
|
-
async function
|
|
1554
|
+
async function Ei(i = {}) {
|
|
1555
1555
|
const { quality: t = 0.8, maxWidth: e = 1280, maxHeight: o = 800 } = i;
|
|
1556
1556
|
try {
|
|
1557
|
-
if (!await
|
|
1557
|
+
if (!await Ti() || !window.html2canvas)
|
|
1558
1558
|
return console.warn("html2canvas failed to load"), null;
|
|
1559
1559
|
const s = await window.html2canvas(document.body, {
|
|
1560
1560
|
useCORS: !0,
|
|
@@ -1581,9 +1581,9 @@ async function Ti(i = {}) {
|
|
|
1581
1581
|
}
|
|
1582
1582
|
const vt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1583
1583
|
__proto__: null,
|
|
1584
|
-
captureDomScreenshot:
|
|
1584
|
+
captureDomScreenshot: Ei,
|
|
1585
1585
|
isDomScreenshotSupported: Si
|
|
1586
|
-
}, Symbol.toStringTag, { value: "Module" })),
|
|
1586
|
+
}, Symbol.toStringTag, { value: "Module" })), E = 12, Ai = "#ef4444", Ii = ["#ef4444", "#f59e0b", "#22c55e", "#3b82f6", "#111827", "#ffffff"], Li = {
|
|
1587
1587
|
"#ef4444": "red",
|
|
1588
1588
|
"#f59e0b": "amber",
|
|
1589
1589
|
"#22c55e": "green",
|
|
@@ -1644,13 +1644,13 @@ function Hi(i, t) {
|
|
|
1644
1644
|
}
|
|
1645
1645
|
function Oi(i, t, e, o, n) {
|
|
1646
1646
|
i.fillStyle = "rgb(15, 23, 42)", i.fillRect(t, e, o, n), i.fillStyle = "rgb(37, 47, 63)";
|
|
1647
|
-
for (let s = e; s < e + n; s +=
|
|
1648
|
-
for (let a = t; a < t + o; a +=
|
|
1649
|
-
(Math.floor((a - t) /
|
|
1647
|
+
for (let s = e; s < e + n; s += E)
|
|
1648
|
+
for (let a = t; a < t + o; a += E)
|
|
1649
|
+
(Math.floor((a - t) / E) + Math.floor((s - e) / E)) % 2 === 0 && i.fillRect(
|
|
1650
1650
|
a,
|
|
1651
1651
|
s,
|
|
1652
|
-
Math.min(
|
|
1653
|
-
Math.min(
|
|
1652
|
+
Math.min(E, t + o - a),
|
|
1653
|
+
Math.min(E, e + n - s)
|
|
1654
1654
|
);
|
|
1655
1655
|
}
|
|
1656
1656
|
function H(i, t) {
|
|
@@ -1790,7 +1790,7 @@ class zt {
|
|
|
1790
1790
|
});
|
|
1791
1791
|
for (const h of this.palette) c.appendChild(this.swatchButton(h));
|
|
1792
1792
|
this.undoBtn = this.actionButton("undo", ue, this.labels.undo, "qaid-annotate-undo"), this.undoBtn.disabled = !0;
|
|
1793
|
-
const l = this.actionButton("skip", fe, this.labels.skip, "qaid-annotate-skip"), d = this.actionButton("done",
|
|
1793
|
+
const l = this.actionButton("skip", fe, this.labels.skip, "qaid-annotate-skip"), d = this.actionButton("done", Tt, this.labels.done, "qaid-annotate-done"), r = q("div", { class: "qaid-annotate-actions" });
|
|
1794
1794
|
r.appendChild(this.undoBtn), r.appendChild(l), r.appendChild(d), this.toolbar.appendChild(a), this.toolbar.appendChild(c), this.toolbar.appendChild(r), this.container.appendChild(o), this.container.appendChild(n), this.container.appendChild(s), this.container.appendChild(this.toolbar), this.opts.root.appendChild(this.container), this.toolbar.addEventListener("click", this.onToolbarClick), this.canvas.addEventListener("pointerdown", this.onPointerDown), this.container.addEventListener("pointermove", this.onPointerMove), this.container.addEventListener("pointerup", this.onPointerUp), this.container.addEventListener("keydown", this.onKeyDown), this.opts.openDialog?.(this.container, {
|
|
1795
1795
|
labelledbyId: t,
|
|
1796
1796
|
describedbyId: e
|
|
@@ -2087,7 +2087,7 @@ class Fi {
|
|
|
2087
2087
|
attrs: { type: "button" },
|
|
2088
2088
|
on: { click: () => this.stopRecording() }
|
|
2089
2089
|
})
|
|
2090
|
-
), this.host.applyVars(this.recordingIndicator),
|
|
2090
|
+
), this.host.applyVars(this.recordingIndicator), t.appendChild(this.recordingIndicator);
|
|
2091
2091
|
}
|
|
2092
2092
|
updateRecordingTimer(t) {
|
|
2093
2093
|
if (!this.recordingIndicator) return;
|
|
@@ -2434,7 +2434,7 @@ class Ui {
|
|
|
2434
2434
|
{ class: t ? "qaid-confirm" : "qaid-confirm qaid-confirm-error" },
|
|
2435
2435
|
p("span", {
|
|
2436
2436
|
class: "qaid-confirm-icon",
|
|
2437
|
-
html: t ?
|
|
2437
|
+
html: t ? Tt : pe,
|
|
2438
2438
|
attrs: { "aria-hidden": "true" }
|
|
2439
2439
|
}),
|
|
2440
2440
|
a,
|
|
@@ -2750,7 +2750,7 @@ const Ct = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
|
2750
2750
|
export {
|
|
2751
2751
|
zt as AnnotationEditor,
|
|
2752
2752
|
ni as QaidFeedback,
|
|
2753
|
-
|
|
2753
|
+
Ei as captureDomScreenshot,
|
|
2754
2754
|
ui as captureNetworkErrors,
|
|
2755
2755
|
Ot as compositeAnnotations,
|
|
2756
2756
|
xi as createVideoRecorder,
|