@siteping/widget 0.9.2 → 0.9.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../../../node_modules/.bun/@medv+finder@3.2.0/node_modules/@medv/finder/finder.js","../src/dom/fingerprint.ts","../src/dom/text-context.ts","../src/dom/xpath.ts","../src/dom/anchor.ts","../src/dom-utils.ts","../src/icons.ts","../src/styles/theme.ts","../src/popup.ts","../src/annotator.ts","../src/api-client.ts","../src/events.ts","../src/fab.ts","../src/i18n/en.ts","../src/i18n/fr.ts","../src/i18n/index.ts","../src/identity.ts","../src/dom/fuzzy.ts","../src/dom/resolver.ts","../src/markers.ts","../src/panel.ts","../../core/src/types.ts","../src/store-client.ts","../src/styles/animations.ts","../src/styles/base.ts","../src/tooltip.ts","../src/launcher.ts"],"sourcesContent":["import type { SitepingConfig, SitepingInstance } from \"@siteping/core\";\nimport { launch } from \"./launcher.js\";\n\nexport type {\n AnchorData,\n AnnotationPayload,\n AnnotationResponse,\n FeedbackPayload,\n FeedbackResponse,\n FeedbackStatus,\n FeedbackType,\n RectData,\n SitepingConfig,\n SitepingInstance,\n SitepingPublicEvents,\n SitepingStore,\n} from \"@siteping/core\";\n\nexport type { Identity } from \"./identity.js\";\n\n/**\n * Initialize the Siteping feedback widget.\n *\n * @example\n * ```ts\n * import { initSiteping } from '@siteping/widget'\n *\n * const { destroy } = initSiteping({\n * endpoint: '/api/siteping',\n * projectName: 'my-project',\n * })\n * ```\n */\nexport function initSiteping(config: SitepingConfig): SitepingInstance {\n return launch(config);\n}\n","// License: MIT\n// Author: Anton Medvedev <anton@medv.io>\n// Source: https://github.com/antonmedv/finder\nlet config;\nlet rootDocument;\nlet start;\nexport function finder(input, options) {\n start = new Date();\n if (input.nodeType !== Node.ELEMENT_NODE) {\n throw new Error(`Can't generate CSS selector for non-element node type.`);\n }\n if ('html' === input.tagName.toLowerCase()) {\n return 'html';\n }\n const defaults = {\n root: document.body,\n idName: (name) => true,\n className: (name) => true,\n tagName: (name) => true,\n attr: (name, value) => false,\n seedMinLength: 1,\n optimizedMinLength: 2,\n threshold: 1000,\n maxNumberOfTries: 10000,\n timeoutMs: undefined,\n };\n config = { ...defaults, ...options };\n rootDocument = findRootDocument(config.root, defaults);\n let path = bottomUpSearch(input, 'all', () => bottomUpSearch(input, 'two', () => bottomUpSearch(input, 'one', () => bottomUpSearch(input, 'none'))));\n if (path) {\n const optimized = sort(optimize(path, input));\n if (optimized.length > 0) {\n path = optimized[0];\n }\n return selector(path);\n }\n else {\n throw new Error(`Selector was not found.`);\n }\n}\nfunction findRootDocument(rootNode, defaults) {\n if (rootNode.nodeType === Node.DOCUMENT_NODE) {\n return rootNode;\n }\n if (rootNode === defaults.root) {\n return rootNode.ownerDocument;\n }\n return rootNode;\n}\nfunction bottomUpSearch(input, limit, fallback) {\n let path = null;\n let stack = [];\n let current = input;\n let i = 0;\n while (current) {\n const elapsedTime = new Date().getTime() - start.getTime();\n if (config.timeoutMs !== undefined && elapsedTime > config.timeoutMs) {\n throw new Error(`Timeout: Can't find a unique selector after ${elapsedTime}ms`);\n }\n let level = maybe(id(current)) ||\n maybe(...attr(current)) ||\n maybe(...classNames(current)) ||\n maybe(tagName(current)) || [any()];\n const nth = index(current);\n if (limit == 'all') {\n if (nth) {\n level = level.concat(level.filter(dispensableNth).map((node) => nthChild(node, nth)));\n }\n }\n else if (limit == 'two') {\n level = level.slice(0, 1);\n if (nth) {\n level = level.concat(level.filter(dispensableNth).map((node) => nthChild(node, nth)));\n }\n }\n else if (limit == 'one') {\n const [node] = (level = level.slice(0, 1));\n if (nth && dispensableNth(node)) {\n level = [nthChild(node, nth)];\n }\n }\n else if (limit == 'none') {\n level = [any()];\n if (nth) {\n level = [nthChild(level[0], nth)];\n }\n }\n for (let node of level) {\n node.level = i;\n }\n stack.push(level);\n if (stack.length >= config.seedMinLength) {\n path = findUniquePath(stack, fallback);\n if (path) {\n break;\n }\n }\n current = current.parentElement;\n i++;\n }\n if (!path) {\n path = findUniquePath(stack, fallback);\n }\n if (!path && fallback) {\n return fallback();\n }\n return path;\n}\nfunction findUniquePath(stack, fallback) {\n const paths = sort(combinations(stack));\n if (paths.length > config.threshold) {\n return fallback ? fallback() : null;\n }\n for (let candidate of paths) {\n if (unique(candidate)) {\n return candidate;\n }\n }\n return null;\n}\nfunction selector(path) {\n let node = path[0];\n let query = node.name;\n for (let i = 1; i < path.length; i++) {\n const level = path[i].level || 0;\n if (node.level === level - 1) {\n query = `${path[i].name} > ${query}`;\n }\n else {\n query = `${path[i].name} ${query}`;\n }\n node = path[i];\n }\n return query;\n}\nfunction penalty(path) {\n return path.map((node) => node.penalty).reduce((acc, i) => acc + i, 0);\n}\nfunction unique(path) {\n const css = selector(path);\n switch (rootDocument.querySelectorAll(css).length) {\n case 0:\n throw new Error(`Can't select any node with this selector: ${css}`);\n case 1:\n return true;\n default:\n return false;\n }\n}\nfunction id(input) {\n const elementId = input.getAttribute('id');\n if (elementId && config.idName(elementId)) {\n return {\n name: '#' + CSS.escape(elementId),\n penalty: 0,\n };\n }\n return null;\n}\nfunction attr(input) {\n const attrs = Array.from(input.attributes).filter((attr) => config.attr(attr.name, attr.value));\n return attrs.map((attr) => ({\n name: `[${CSS.escape(attr.name)}=\"${CSS.escape(attr.value)}\"]`,\n penalty: 0.5,\n }));\n}\nfunction classNames(input) {\n const names = Array.from(input.classList).filter(config.className);\n return names.map((name) => ({\n name: '.' + CSS.escape(name),\n penalty: 1,\n }));\n}\nfunction tagName(input) {\n const name = input.tagName.toLowerCase();\n if (config.tagName(name)) {\n return {\n name,\n penalty: 2,\n };\n }\n return null;\n}\nfunction any() {\n return {\n name: '*',\n penalty: 3,\n };\n}\nfunction index(input) {\n const parent = input.parentNode;\n if (!parent) {\n return null;\n }\n let child = parent.firstChild;\n if (!child) {\n return null;\n }\n let i = 0;\n while (child) {\n if (child.nodeType === Node.ELEMENT_NODE) {\n i++;\n }\n if (child === input) {\n break;\n }\n child = child.nextSibling;\n }\n return i;\n}\nfunction nthChild(node, i) {\n return {\n name: node.name + `:nth-child(${i})`,\n penalty: node.penalty + 1,\n };\n}\nfunction dispensableNth(node) {\n return node.name !== 'html' && !node.name.startsWith('#');\n}\nfunction maybe(...level) {\n const list = level.filter(notEmpty);\n if (list.length > 0) {\n return list;\n }\n return null;\n}\nfunction notEmpty(value) {\n return value !== null && value !== undefined;\n}\nfunction* combinations(stack, path = []) {\n if (stack.length > 0) {\n for (let node of stack[0]) {\n yield* combinations(stack.slice(1, stack.length), path.concat(node));\n }\n }\n else {\n yield path;\n }\n}\nfunction sort(paths) {\n return [...paths].sort((a, b) => penalty(a) - penalty(b));\n}\nfunction* optimize(path, input, scope = {\n counter: 0,\n visited: new Map(),\n}) {\n if (path.length > 2 && path.length > config.optimizedMinLength) {\n for (let i = 1; i < path.length - 1; i++) {\n if (scope.counter > config.maxNumberOfTries) {\n return; // Okay At least I tried!\n }\n scope.counter += 1;\n const newPath = [...path];\n newPath.splice(i, 1);\n const newPathKey = selector(newPath);\n if (scope.visited.has(newPathKey)) {\n return;\n }\n if (unique(newPath) && same(newPath, input)) {\n yield newPath;\n scope.visited.set(newPathKey, true);\n yield* optimize(newPath, input, scope);\n }\n }\n }\n}\nfunction same(path, input) {\n return rootDocument.querySelector(selector(path)) === input;\n}\n","/**\n * Element fingerprinting for robust DOM re-anchoring.\n *\n * Captures structural properties (child count, sibling index, stable attributes)\n * that survive CSS class changes and minor DOM reshuffling.\n * Inspired by Similo (academic state-of-the-art, 98.8% accuracy).\n */\n\nconst STABLE_ATTRS = [\"role\", \"aria-label\", \"type\", \"name\", \"href\", \"src\", \"data-testid\", \"data-id\"] as const;\n\n/** Simple 32-bit hash (djb2). */\nfunction djb2(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) + hash + str.charCodeAt(i)) | 0;\n }\n return (hash >>> 0).toString(36);\n}\n\n/**\n * Generate a compact structural fingerprint for a DOM element.\n *\n * Format: `\"childCount:siblingIdx:attrHash\"`\n * - `childCount` — number of direct child elements\n * - `siblingIdx` — position among same-tag siblings (0-based)\n * - `attrHash` — djb2 hash of stable attributes (role, aria-label, type, etc.)\n *\n * Tag name is NOT included — it's stored separately in `AnchorData.elementTag`.\n */\nexport function generateFingerprint(element: Element): string {\n const childCount = element.children.length;\n\n // Position among same-tag siblings\n let siblingIdx = 0;\n const parent = element.parentElement;\n if (parent) {\n for (const child of parent.children) {\n if (child === element) break;\n if (child.tagName === element.tagName) siblingIdx++;\n }\n }\n\n // Hash stable attributes\n const attrs: string[] = [];\n for (const attr of STABLE_ATTRS) {\n const val = element.getAttribute(attr);\n if (val) attrs.push(`${attr}=${val}`);\n }\n const attrHash = attrs.length > 0 ? djb2(attrs.join(\",\")) : \"0\";\n\n return `${childCount}:${siblingIdx}:${attrHash}`;\n}\n\n/**\n * Score how well a candidate element matches a stored fingerprint.\n * Returns 0–1.\n *\n * Weights:\n * - Child count match: 0.2 (tolerant — ±2 gets partial credit)\n * - Sibling index match: 0.4 (positional — most discriminating)\n * - Attribute hash match: 0.4 (identity — exact or nothing)\n */\nexport function scoreFingerprint(candidate: Element, storedFingerprint: string): number {\n const parts = storedFingerprint.split(\":\");\n if (parts.length !== 3) return 0;\n\n const [storedChildren, storedSibIdx, storedAttrHash] = parts;\n const storedChildCount = Number(storedChildren);\n const storedSibIndex = Number(storedSibIdx);\n if (Number.isNaN(storedChildCount) || Number.isNaN(storedSibIndex)) return 0;\n\n const candidateFp = generateFingerprint(candidate);\n const [candChildren, candSibIdx, candAttrHash] = candidateFp.split(\":\");\n\n let score = 0;\n\n // Child count (0.2)\n const childDiff = Math.abs(Number(candChildren) - storedChildCount);\n if (childDiff === 0) score += 0.2;\n else if (childDiff <= 2) score += 0.1;\n else if (childDiff <= 5) score += 0.03;\n\n // Sibling index (0.4)\n const sibDiff = Math.abs(Number(candSibIdx) - storedSibIndex);\n if (sibDiff === 0) score += 0.4;\n else if (sibDiff === 1) score += 0.2;\n else if (sibDiff <= 3) score += 0.08;\n\n // Attribute hash (0.4)\n if (candAttrHash === storedAttrHash) score += 0.4;\n\n return score;\n}\n","/**\n * Shared text-context helpers for DOM anchoring.\n * Used by both anchor generation (anchor.ts) and resolution (resolver.ts).\n */\n\n/**\n * Extract ~32 chars of text from the nearest sibling with content.\n * Walks up to 3 siblings in the given direction.\n */\nexport function adjacentText(element: Element, direction: \"before\" | \"after\"): string {\n const prop = direction === \"before\" ? \"previousElementSibling\" : \"nextElementSibling\";\n let sibling: Element | null = element[prop];\n let attempts = 3;\n\n while (sibling && attempts > 0) {\n const text = sibling.textContent?.trim();\n if (text) {\n return direction === \"before\" ? text.slice(-32) : text.slice(0, 32);\n }\n sibling = sibling[prop];\n attempts--;\n }\n\n return \"\";\n}\n\n/** Collect text from immediate siblings for disambiguation context. */\nexport function neighborText(element: Element): string {\n const prev = element.previousElementSibling?.textContent?.trim().slice(0, 40) ?? \"\";\n const next = element.nextElementSibling?.textContent?.trim().slice(0, 40) ?? \"\";\n return [prev, next].filter(Boolean).join(\" | \");\n}\n","/**\n * Generate an optimized XPath for a DOM element.\n *\n * Strategy:\n * - If the element has a unique id → //tag[@id='value']\n * - Otherwise, walk up the tree building /tag[position] segments\n * until we hit an ancestor with an id or reach <body>\n * - Cap depth at 6 levels to keep paths short\n */\nexport function generateXPath(element: Element): string {\n if (element.id) {\n const safeId = element.id.includes(\"'\") ? `concat('${element.id.replace(/'/g, \"',\\\"'\\\",'\")}')` : `'${element.id}'`;\n return `//${element.localName}[@id=${safeId}]`;\n }\n\n const segments: string[] = [];\n let current: Element | null = element;\n\n while (current && current !== document.body && segments.length < 6) {\n const tag = current.localName;\n const parent: Element | null = current.parentElement;\n\n if (current.id) {\n const safeId = current.id.includes(\"'\")\n ? `concat('${current.id.replace(/'/g, \"',\\\"'\\\",'\")}')`\n : `'${current.id}'`;\n segments.unshift(`/${tag}[@id=${safeId}]`);\n return \"/\" + segments.join(\"\");\n }\n\n // Compute position among same-tag siblings\n let position = 1;\n if (parent) {\n for (const sibling of parent.children) {\n if (sibling === current) break;\n if (sibling.localName === tag) position++;\n }\n }\n\n segments.unshift(`/${tag}[${position}]`);\n current = parent;\n }\n\n return \"/html/body\" + segments.join(\"\");\n}\n","import { finder } from \"@medv/finder\";\nimport type { AnchorData, RectData } from \"@siteping/core\";\nimport { generateFingerprint } from \"./fingerprint.js\";\nimport { adjacentText, neighborText } from \"./text-context.js\";\nimport { generateXPath } from \"./xpath.js\";\n\n/**\n * Generate a multi-selector anchor for a DOM element.\n *\n * Uses three complementary strategies (Hypothesis-inspired):\n * 1. CSS selector via @medv/finder (primary — fast, compact)\n * 2. XPath (fallback — survives class changes)\n * 3. Text snippet (fallback — survives structural changes)\n */\nexport function generateAnchor(element: Element): AnchorData {\n const cssSelector = finder(element, {\n // Filter out CSS-in-JS hashed class names\n className: (name: string) => !/^(css|sc|emotion|styled)-/.test(name) && !/^[a-z]{1,3}[A-Za-z0-9]{4,8}$/.test(name),\n // Prefer stable attributes\n attr: (name: string) => [\"data-testid\", \"data-id\", \"role\", \"aria-label\"].includes(name),\n // Exclude framework-generated dynamic IDs\n idName: (name: string) => !name.startsWith(\"radix-\") && !/^:r[0-9]+:$/.test(name),\n seedMinLength: 3,\n optimizedMinLength: 2,\n });\n\n const xpath = generateXPath(element);\n\n const rawText = element.textContent?.trim() ?? \"\";\n const textSnippet = rawText.slice(0, 120);\n\n const textPrefix = adjacentText(element, \"before\");\n const textSuffix = adjacentText(element, \"after\");\n const fingerprint = generateFingerprint(element);\n const neighbor = neighborText(element);\n\n return {\n cssSelector,\n xpath,\n textSnippet,\n textPrefix,\n textSuffix,\n fingerprint,\n neighborText: neighbor,\n elementTag: element.tagName,\n elementId: element.id || undefined,\n };\n}\n\n/**\n * Find the deepest DOM element that fully contains the drawn rectangle.\n * Walks from the center of the rect down through overlapping elements.\n */\nexport function findAnchorElement(rect: DOMRect, root: Element = document.documentElement): Element {\n const centerX = rect.x + rect.width / 2;\n const centerY = rect.y + rect.height / 2;\n\n // Get the element at the center point\n const elementAtCenter = document.elementFromPoint(centerX, centerY);\n if (!elementAtCenter || elementAtCenter === root) return document.body;\n\n // Walk up to find the smallest element whose bounding box contains the full rect\n let candidate: Element = elementAtCenter;\n let current: Element | null = elementAtCenter;\n\n while (current && current !== document.body) {\n const bounds = current.getBoundingClientRect();\n if (\n bounds.left <= rect.x &&\n bounds.top <= rect.y &&\n bounds.right >= rect.x + rect.width &&\n bounds.bottom >= rect.y + rect.height\n ) {\n candidate = current;\n break;\n }\n current = current.parentElement;\n }\n\n return candidate;\n}\n\n/**\n * Convert absolute rectangle coordinates to percentages\n * relative to an anchor element's bounding box.\n */\nexport function rectToPercentages(rect: DOMRect, anchorBounds: DOMRect): RectData {\n // Guard against zero-dimension anchors (collapsed/hidden elements)\n if (anchorBounds.width <= 0 || anchorBounds.height <= 0) {\n return { xPct: 0, yPct: 0, wPct: 1, hPct: 1 };\n }\n return {\n xPct: (rect.x - anchorBounds.x) / anchorBounds.width,\n yPct: (rect.y - anchorBounds.y) / anchorBounds.height,\n wPct: rect.width / anchorBounds.width,\n hPct: rect.height / anchorBounds.height,\n };\n}\n","/**\n * Safe DOM creation utilities.\n * All user content is set via textContent (never innerHTML).\n * SVG icons use a DOMParser for trusted static strings.\n */\n\n/**\n * Parse a trusted SVG string into an SVGElement.\n * Only use with hardcoded icon constants — never with user input.\n * Uses createContextualFragment for native document-context parsing\n * (DOMParser creates nodes in a foreign document that don't render in Shadow DOM).\n */\nexport function parseSvg(svgString: string): SVGSVGElement {\n const range = document.createRange();\n const fragment = range.createContextualFragment(svgString);\n const svg = fragment.firstElementChild;\n if (!svg || svg.nodeName.toLowerCase() !== \"svg\") {\n throw new Error(\"[siteping] Invalid SVG string\");\n }\n // Safety: strip any event handlers in case of accidental misuse\n for (const attr of [...svg.attributes]) {\n if (attr.name.startsWith(\"on\")) svg.removeAttribute(attr.name);\n }\n // Also strip from all descendants\n for (const el of svg.querySelectorAll(\"*\")) {\n for (const attr of [...el.attributes]) {\n if (attr.name.startsWith(\"on\")) el.removeAttribute(attr.name);\n }\n }\n return svg as SVGSVGElement;\n}\n\n/** Create an element with optional class and style */\nexport function el(tag: string, attrs?: Record<string, string>): HTMLElement {\n const element = document.createElement(tag);\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"class\") {\n element.className = value;\n } else if (key === \"style\") {\n element.style.cssText = value;\n } else {\n element.setAttribute(key, value);\n }\n }\n }\n return element;\n}\n\n/** Set text content safely (no HTML injection possible) */\nexport function setText(element: HTMLElement | SVGElement, text: string): void {\n element.textContent = text;\n}\n\n/** Format a relative date string using Intl.RelativeTimeFormat for locale support */\nexport function formatRelativeDate(isoString: string, locale = \"fr\"): string {\n const diff = Date.now() - new Date(isoString).getTime();\n const seconds = Math.floor(diff / 1000);\n\n if (seconds < 60) {\n return new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" }).format(0, \"second\");\n }\n\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"always\", style: \"narrow\" });\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return rtf.format(-minutes, \"minute\");\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return rtf.format(-hours, \"hour\");\n\n const days = Math.floor(hours / 24);\n if (days < 7) return rtf.format(-days, \"day\");\n\n return new Date(isoString).toLocaleDateString(locale);\n}\n","/** SVG icon strings for the widget UI. Kept as template strings to avoid DOM parsing overhead. */\n\nexport const ICON_SITEPING = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/><circle cx=\"12\" cy=\"10\" r=\"1\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"8\" cy=\"10\" r=\"1\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"16\" cy=\"10\" r=\"1\" fill=\"currentColor\" stroke=\"none\"/></svg>`;\n\nexport const ICON_CHAT = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>`;\n\nexport const ICON_ANNOTATE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M3 9h18\"/><path d=\"M9 3v18\"/></svg>`;\n\nexport const ICON_EYE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>`;\n\nexport const ICON_EYE_OFF = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\"/><path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\"/><line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"/></svg>`;\n\nexport const ICON_CLOSE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`;\n\nexport const ICON_SEARCH = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/></svg>`;\n\nexport const ICON_CHECK = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\n\nexport const ICON_QUESTION = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>`;\n\nexport const ICON_CHANGE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>`;\n\nexport const ICON_BUG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"8\" y=\"6\" width=\"8\" height=\"14\" rx=\"4\"/><path d=\"M19 9h2\"/><path d=\"M3 9h2\"/><path d=\"M19 13h2\"/><path d=\"M3 13h2\"/><path d=\"M19 17h2\"/><path d=\"M3 17h2\"/><path d=\"M10 2h4\"/></svg>`;\n\nexport const ICON_OTHER = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"1\"/><circle cx=\"19\" cy=\"12\" r=\"1\"/><circle cx=\"5\" cy=\"12\" r=\"1\"/></svg>`;\n\nexport const ICON_UNDO = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>`;\n\nexport const ICON_TRASH = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/></svg>`;\n","/** Color palette and glassmorphism tokens derived from the accent color */\nexport interface ThemeColors {\n accent: string;\n accentLight: string;\n accentDark: string;\n accentGlow: string;\n accentGradient: string;\n bg: string;\n bgHover: string;\n text: string;\n textSecondary: string;\n textTertiary: string;\n border: string;\n shadow: string;\n // Glass tokens\n glassBg: string;\n glassBgHeavy: string;\n glassBorder: string;\n glassBorderSubtle: string;\n // Feedback type colors\n typeQuestion: string;\n typeChange: string;\n typeBug: string;\n typeOther: string;\n // Soft type backgrounds (pastel)\n typeQuestionBg: string;\n typeChangeBg: string;\n typeBugBg: string;\n typeOtherBg: string;\n}\n\nconst DEFAULT_ACCENT = \"#0066ff\";\nconst HEX6_RE = /^#[0-9a-fA-F]{6}$/;\nconst HEX3_RE = /^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/;\nconst HEX8_RE = /^#[0-9a-fA-F]{8}$/;\n\n/**\n * Normalize an accent color to a 6-digit hex string.\n *\n * **Only hex formats are accepted:**\n * - `#RGB` (3-digit shorthand, expanded to 6-digit)\n * - `#RRGGBB` (standard 6-digit)\n * - `#RRGGBBAA` (8-digit with alpha, alpha is stripped)\n *\n * Any other CSS color format (named colors like `\"red\"`, `hsl()`, `rgb()`,\n * `oklch()`, etc.) is **not** supported and will fall back to the default\n * accent color with a console warning.\n */\nfunction normalizeHex(raw: string): string {\n if (HEX6_RE.test(raw)) return raw;\n const short = HEX3_RE.test(raw) ? raw.match(HEX3_RE) : null;\n if (short) return `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}`;\n if (HEX8_RE.test(raw)) return raw.slice(0, 7);\n\n console.warn(\n `[siteping] Invalid accentColor \"${raw}\" — only hex colors (#RGB, #RRGGBB, #RRGGBBAA) are supported. Using default.`,\n );\n return DEFAULT_ACCENT;\n}\n\n/** Darken a hex color by a percentage (0-1) */\nfunction darkenHex(hex: string, amount: number): string {\n const r = Math.max(0, Math.round(parseInt(hex.slice(1, 3), 16) * (1 - amount)));\n const g = Math.max(0, Math.round(parseInt(hex.slice(3, 5), 16) * (1 - amount)));\n const b = Math.max(0, Math.round(parseInt(hex.slice(5, 7), 16) * (1 - amount)));\n return `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\n}\n\n/** Detect if user prefers dark mode via media query */\nfunction prefersDark(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n\n/** Resolve 'auto' theme to 'light' or 'dark' based on system preference */\nexport function resolveTheme(theme?: \"light\" | \"dark\" | \"auto\"): \"light\" | \"dark\" {\n if (theme === \"dark\") return \"dark\";\n if (theme === \"auto\") return prefersDark() ? \"dark\" : \"light\";\n return \"light\";\n}\n\nexport function buildThemeColors(accent: string = DEFAULT_ACCENT, theme?: \"light\" | \"dark\" | \"auto\"): ThemeColors {\n const hex = normalizeHex(accent);\n const dark = darkenHex(hex, 0.15);\n const resolved = resolveTheme(theme);\n\n if (resolved === \"dark\") {\n return {\n accent: hex,\n accentLight: hex + \"22\", // slightly more visible on dark bg\n accentDark: dark,\n accentGlow: hex + \"44\",\n accentGradient: `linear-gradient(135deg, ${hex}, ${dark})`,\n bg: \"#0f172a\",\n bgHover: \"#1e293b\",\n text: \"#f1f5f9\",\n textSecondary: \"#94a3b8\",\n textTertiary: \"#64748b\",\n border: \"#334155\",\n shadow: \"rgba(0, 0, 0, 0.3)\",\n // Glass tokens — dark frosted glass\n glassBg: \"rgba(15, 23, 42, 0.78)\",\n glassBgHeavy: \"rgba(15, 23, 42, 0.88)\",\n glassBorder: \"rgba(51, 65, 85, 0.5)\",\n glassBorderSubtle: \"rgba(51, 65, 85, 0.3)\",\n // Type colors stay vibrant on dark\n typeQuestion: \"#60a5fa\",\n typeChange: \"#fbbf24\",\n typeBug: \"#f87171\",\n typeOther: \"#94a3b8\",\n // Dark pastel backgrounds\n typeQuestionBg: \"rgba(59, 130, 246, 0.15)\",\n typeChangeBg: \"rgba(245, 158, 11, 0.15)\",\n typeBugBg: \"rgba(239, 68, 68, 0.15)\",\n typeOtherBg: \"rgba(100, 116, 139, 0.15)\",\n };\n }\n\n return {\n accent: hex,\n accentLight: hex + \"14\", // 8% opacity\n accentDark: dark,\n accentGlow: hex + \"33\", // 20% opacity\n accentGradient: `linear-gradient(135deg, ${hex}, ${dark})`,\n bg: \"#ffffff\",\n bgHover: \"#f8f9fb\",\n text: \"#0f172a\",\n textSecondary: \"#475569\",\n textTertiary: \"#64748b\",\n border: \"#e2e8f0\",\n shadow: \"rgba(0, 0, 0, 0.06)\",\n // Glass tokens\n glassBg: \"rgba(255, 255, 255, 0.72)\",\n glassBgHeavy: \"rgba(255, 255, 255, 0.85)\",\n glassBorder: \"rgba(255, 255, 255, 0.35)\",\n glassBorderSubtle: \"rgba(255, 255, 255, 0.18)\",\n // Vibrant type colors\n typeQuestion: \"#3b82f6\",\n typeChange: \"#b45309\",\n typeBug: \"#ef4444\",\n typeOther: \"#64748b\",\n // Pastel backgrounds\n typeQuestionBg: \"#eff6ff\",\n typeChangeBg: \"#fffbeb\",\n typeBugBg: \"#fef2f2\",\n typeOtherBg: \"#f8fafc\",\n };\n}\n\nexport function getTypeColor(type: string, colors: ThemeColors): string {\n switch (type) {\n case \"question\":\n return colors.typeQuestion;\n case \"change\":\n return colors.typeChange;\n case \"bug\":\n return colors.typeBug;\n default:\n return colors.typeOther;\n }\n}\n\nexport function getTypeBgColor(type: string, colors: ThemeColors): string {\n switch (type) {\n case \"question\":\n return colors.typeQuestionBg;\n case \"change\":\n return colors.typeChangeBg;\n case \"bug\":\n return colors.typeBugBg;\n default:\n return colors.typeOtherBg;\n }\n}\n\nexport function cssVariables(colors: ThemeColors): string {\n return `\n --sp-accent: ${colors.accent};\n --sp-accent-light: ${colors.accentLight};\n --sp-accent-dark: ${colors.accentDark};\n --sp-accent-glow: ${colors.accentGlow};\n --sp-accent-gradient: ${colors.accentGradient};\n --sp-bg: ${colors.bg};\n --sp-bg-hover: ${colors.bgHover};\n --sp-text: ${colors.text};\n --sp-text-secondary: ${colors.textSecondary};\n --sp-text-tertiary: ${colors.textTertiary};\n --sp-border: ${colors.border};\n --sp-shadow: ${colors.shadow};\n --sp-glass-bg: ${colors.glassBg};\n --sp-glass-bg-heavy: ${colors.glassBgHeavy};\n --sp-glass-border: ${colors.glassBorder};\n --sp-glass-border-subtle: ${colors.glassBorderSubtle};\n --sp-type-question: ${colors.typeQuestion};\n --sp-type-change: ${colors.typeChange};\n --sp-type-bug: ${colors.typeBug};\n --sp-type-other: ${colors.typeOther};\n --sp-type-question-bg: ${colors.typeQuestionBg};\n --sp-type-change-bg: ${colors.typeChangeBg};\n --sp-type-bug-bg: ${colors.typeBugBg};\n --sp-type-other-bg: ${colors.typeOtherBg};\n --sp-radius: 12px;\n --sp-radius-lg: 16px;\n --sp-radius-xl: 20px;\n --sp-radius-full: 9999px;\n --sp-blur: 20px;\n --sp-blur-heavy: 32px;\n --sp-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04);\n --sp-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.04);\n --sp-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04);\n --sp-shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.04);\n --sp-shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.06);\n --sp-font: \"Inter\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n `;\n}\n","import type { FeedbackType } from \"@siteping/core\";\nimport { el, parseSvg, setText } from \"./dom-utils.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport { ICON_BUG, ICON_CHANGE, ICON_OTHER, ICON_QUESTION } from \"./icons.js\";\nimport { getTypeBgColor, getTypeColor, type ThemeColors } from \"./styles/theme.js\";\n\ninterface PopupResult {\n type: FeedbackType;\n message: string;\n}\n\ninterface TypeOption {\n type: FeedbackType;\n label: string;\n icon: string;\n}\n\n/**\n * Popup form shown after drawing an annotation rectangle.\n *\n * Glassmorphism design: frosted glass background, soft shadows,\n * pill-shaped type buttons, gradient submit button.\n * Lives outside Shadow DOM.\n */\nexport class Popup {\n private root: HTMLElement;\n private selectedType: FeedbackType | null = null;\n private textarea: HTMLTextAreaElement;\n private submitBtn: HTMLButtonElement;\n private resolve: ((result: PopupResult | null) => void) | null = null;\n private previouslyFocused: HTMLElement | null = null;\n private onKeydownTrap: ((e: KeyboardEvent) => void) | null = null;\n\n constructor(\n private readonly colors: ThemeColors,\n private readonly t: TFunction,\n ) {\n this.root = el(\"div\", {\n style: `\n position:fixed;\n z-index:2147483647;\n width:300px;\n padding:16px;\n border-radius:16px;\n background:${this.colors.glassBg};\n backdrop-filter:blur(24px);\n -webkit-backdrop-filter:blur(24px);\n border:1px solid ${this.colors.glassBorder};\n box-shadow:0 8px 32px ${this.colors.shadow}, 0 2px 8px ${this.colors.shadow};\n font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n opacity:0;\n transform:translateY(8px) scale(0.98);\n transition:opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1),transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);\n display:none;\n -webkit-font-smoothing:antialiased;\n `,\n });\n\n this.root.setAttribute(\"role\", \"dialog\");\n this.root.setAttribute(\"aria-modal\", \"true\");\n this.root.setAttribute(\"aria-label\", this.t(\"popup.ariaLabel\"));\n\n // Type selector grid (2x2)\n const typeOptions: TypeOption[] = [\n { type: \"question\", label: this.t(\"type.question\"), icon: ICON_QUESTION },\n { type: \"change\", label: this.t(\"type.change\"), icon: ICON_CHANGE },\n { type: \"bug\", label: this.t(\"type.bug\"), icon: ICON_BUG },\n { type: \"other\", label: this.t(\"type.other\"), icon: ICON_OTHER },\n ];\n const typeRow = el(\"div\", { style: \"display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:12px;\" });\n for (const option of typeOptions) {\n const btn = document.createElement(\"button\");\n btn.style.cssText = `\n height:44px;\n border-radius:9999px;border:1px solid ${this.colors.border};\n background:${this.colors.glassBg};cursor:pointer;\n display:flex;align-items:center;justify-content:center;gap:5px;\n font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n font-size:13px;font-weight:500;color:${this.colors.textTertiary};\n transition:all 0.2s ease;\n padding:0 12px;\n `;\n const icon = parseSvg(option.icon);\n icon.setAttribute(\"style\", \"width:13px;height:13px;flex-shrink:0;\");\n btn.appendChild(icon);\n const labelSpan = document.createElement(\"span\");\n setText(labelSpan, option.label);\n btn.appendChild(labelSpan);\n btn.dataset.type = option.type;\n btn.setAttribute(\"aria-pressed\", \"false\");\n\n btn.addEventListener(\"click\", () => {\n this.selectType(option.type, typeRow);\n });\n\n btn.addEventListener(\"mouseenter\", () => {\n if (btn.dataset.type !== this.selectedType) {\n const bgColor = getTypeBgColor(btn.dataset.type ?? \"\", this.colors);\n btn.style.background = bgColor;\n btn.style.borderColor = getTypeColor(btn.dataset.type ?? \"\", this.colors) + \"40\";\n }\n });\n\n btn.addEventListener(\"mouseleave\", () => {\n if (btn.dataset.type !== this.selectedType) {\n btn.style.background = this.colors.glassBg;\n btn.style.borderColor = this.colors.border;\n }\n });\n\n typeRow.appendChild(btn);\n }\n\n // Textarea\n this.textarea = document.createElement(\"textarea\");\n this.textarea.style.cssText = `\n width:100%;min-height:72px;max-height:152px;\n padding:10px 12px;border-radius:12px;\n border:1px solid ${this.colors.border};\n background:${this.colors.glassBgHeavy};\n color:${this.colors.text};font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n font-size:13px;line-height:1.5;resize:vertical;\n outline:none;transition:all 0.2s ease;\n box-sizing:border-box;\n `;\n this.textarea.placeholder = this.t(\"popup.placeholder\");\n this.textarea.maxLength = 5000;\n this.textarea.setAttribute(\"aria-label\", this.t(\"popup.textareaAria\"));\n\n // Keyboard shortcut hint\n const hint = el(\"div\", {\n style: `\n font-size:11px;color:${this.colors.textTertiary};\n text-align:right;margin-top:4px;\n font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n letter-spacing:0.01em;\n `,\n });\n // navigator.userAgentData is preferred; navigator.platform is deprecated\n // but still needed as fallback. If both are unavailable, fall back to user agent string parsing.\n const uaData = (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData;\n const isMac = uaData\n ? uaData.platform === \"macOS\"\n : (navigator.platform?.includes(\"Mac\") ?? /Macintosh|Mac OS X/i.test(navigator.userAgent));\n setText(hint, isMac ? this.t(\"popup.submitHintMac\") : this.t(\"popup.submitHintOther\"));\n\n this.textarea.addEventListener(\"focus\", () => {\n this.textarea.style.borderColor = this.colors.accent;\n this.textarea.style.boxShadow = `0 0 0 3px ${this.colors.accent}14`;\n this.textarea.style.background = this.colors.bg;\n });\n this.textarea.addEventListener(\"blur\", () => {\n this.textarea.style.borderColor = this.colors.border;\n this.textarea.style.boxShadow = \"none\";\n this.textarea.style.background = this.colors.glassBgHeavy;\n });\n this.textarea.addEventListener(\"input\", () => {\n this.updateSubmitState();\n });\n this.textarea.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && (e.ctrlKey || e.metaKey)) {\n e.preventDefault();\n this.submit();\n }\n if (e.key === \"Escape\") {\n this.cancel();\n }\n });\n\n // Button row\n const btnRow = el(\"div\", { style: \"display:flex;justify-content:flex-end;gap:8px;margin-top:12px;\" });\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.style.cssText = `\n height:34px;padding:0 16px;border-radius:9999px;\n border:1px solid ${this.colors.border};\n background:${this.colors.glassBg};\n color:${this.colors.textTertiary};font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n font-size:13px;font-weight:500;cursor:pointer;\n transition:all 0.2s ease;\n `;\n setText(cancelBtn, this.t(\"popup.cancel\"));\n cancelBtn.addEventListener(\"click\", () => this.cancel());\n cancelBtn.addEventListener(\"mouseenter\", () => {\n cancelBtn.style.borderColor = this.colors.accent;\n cancelBtn.style.color = this.colors.accent;\n });\n cancelBtn.addEventListener(\"mouseleave\", () => {\n cancelBtn.style.borderColor = this.colors.border;\n cancelBtn.style.color = this.colors.textTertiary;\n });\n\n this.submitBtn = document.createElement(\"button\");\n this.submitBtn.style.cssText = `\n height:34px;padding:0 18px;border-radius:9999px;\n border:none;background:${this.colors.accentGradient};\n color:#fff;font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n font-size:13px;font-weight:600;cursor:pointer;\n opacity:0.35;pointer-events:none;\n transition:all 0.2s ease;\n box-shadow:0 2px 8px ${this.colors.accentGlow};\n `;\n setText(this.submitBtn, this.t(\"popup.submit\"));\n this.submitBtn.addEventListener(\"click\", () => this.submit());\n\n btnRow.appendChild(cancelBtn);\n btnRow.appendChild(this.submitBtn);\n\n this.root.appendChild(typeRow);\n this.root.appendChild(this.textarea);\n this.root.appendChild(hint);\n this.root.appendChild(btnRow);\n document.body.appendChild(this.root);\n }\n\n /**\n * Show the popup near a drawn rectangle and return the user's input.\n * Returns null if cancelled.\n */\n show(rectBounds: DOMRect): Promise<PopupResult | null> {\n return new Promise((resolve) => {\n this.resolve = resolve;\n this.selectedType = null;\n this.textarea.value = \"\";\n this.updateSubmitState();\n this.resetTypeButtons();\n\n // Save focus to restore on close\n this.previouslyFocused = document.activeElement as HTMLElement | null;\n\n // Position: bottom-left of rect, 8px below\n let top = rectBounds.bottom + 8;\n let left = rectBounds.left;\n\n // Collision: flip up if not enough space below\n if (top + 220 > window.innerHeight) {\n top = rectBounds.top - 220 - 8;\n }\n // Collision: flip right if not enough space on left\n if (left + 300 > window.innerWidth) {\n left = rectBounds.right - 300;\n }\n left = Math.max(8, left);\n top = Math.max(8, top);\n\n this.root.style.top = `${top}px`;\n this.root.style.left = `${left}px`;\n this.root.style.display = \"block\";\n\n // Install focus trap\n this.onKeydownTrap = (e: KeyboardEvent) => {\n if (e.key === \"Tab\") {\n const focusableEls = Array.from(\n this.root.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), textarea, input, [tabindex]:not([tabindex=\"-1\"])',\n ),\n );\n if (focusableEls.length === 0) return;\n const first = focusableEls[0];\n const last = focusableEls[focusableEls.length - 1];\n if (!first || !last) return;\n if (e.shiftKey) {\n if (document.activeElement === first || !this.root.contains(document.activeElement)) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (document.activeElement === last || !this.root.contains(document.activeElement)) {\n e.preventDefault();\n first.focus();\n }\n }\n }\n };\n this.root.addEventListener(\"keydown\", this.onKeydownTrap);\n\n // Check prefers-reduced-motion live (not cached at construction time)\n const reduceMotion =\n typeof window !== \"undefined\" && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n this.root.style.transition = reduceMotion ? \"none\" : \"\";\n\n // Trigger animation\n requestAnimationFrame(() => {\n this.root.style.opacity = \"1\";\n this.root.style.transform = \"translateY(0) scale(1)\";\n this.textarea.focus();\n });\n });\n }\n\n private selectType(type: FeedbackType, container: HTMLElement): void {\n this.selectedType = type;\n const buttons = container.querySelectorAll<HTMLButtonElement>(\"button\");\n for (const btn of buttons) {\n const isActive = btn.dataset.type === type;\n const color = getTypeColor(btn.dataset.type ?? \"\", this.colors);\n const bgColor = getTypeBgColor(btn.dataset.type ?? \"\", this.colors);\n btn.style.background = isActive ? bgColor : this.colors.glassBg;\n btn.style.borderColor = isActive ? color + \"60\" : this.colors.border;\n btn.style.color = isActive ? color : this.colors.textTertiary;\n btn.style.fontWeight = isActive ? \"600\" : \"500\";\n btn.setAttribute(\"aria-pressed\", String(isActive));\n }\n this.updateSubmitState();\n }\n\n private resetTypeButtons(): void {\n const buttons = this.root.querySelectorAll<HTMLButtonElement>(\"button[data-type]\");\n for (const btn of buttons) {\n btn.setAttribute(\"aria-pressed\", \"false\");\n btn.style.background = this.colors.glassBg;\n btn.style.borderColor = this.colors.border;\n btn.style.color = this.colors.textTertiary;\n btn.style.fontWeight = \"500\";\n }\n }\n\n private updateSubmitState(): void {\n const enabled = this.selectedType !== null && this.textarea.value.trim().length > 0;\n this.submitBtn.disabled = !enabled;\n this.submitBtn.style.opacity = enabled ? \"1\" : \"0.35\";\n this.submitBtn.style.pointerEvents = enabled ? \"auto\" : \"none\";\n }\n\n private submit(): void {\n if (!this.selectedType || !this.textarea.value.trim()) return;\n this.resolve?.({ type: this.selectedType, message: this.textarea.value.trim() });\n this.resolve = null;\n this.hideElement();\n }\n\n private cancel(): void {\n this.resolve?.(null);\n this.resolve = null;\n this.hideElement();\n }\n\n private hideElement(): void {\n // Remove focus trap\n if (this.onKeydownTrap) {\n this.root.removeEventListener(\"keydown\", this.onKeydownTrap);\n this.onKeydownTrap = null;\n }\n this.root.style.opacity = \"0\";\n this.root.style.transform = \"translateY(8px) scale(0.98)\";\n // Restore focus to the previously focused element\n this.previouslyFocused?.focus();\n this.previouslyFocused = null;\n setTimeout(() => {\n this.root.style.display = \"none\";\n }, 250);\n }\n\n destroy(): void {\n this.root.remove();\n }\n}\n","import type { AnnotationPayload, FeedbackType } from \"@siteping/core\";\nimport { findAnchorElement, generateAnchor, rectToPercentages } from \"./dom/anchor.js\";\nimport { el, setText } from \"./dom-utils.js\";\nimport type { EventBus, WidgetEvents } from \"./events.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport { Popup } from \"./popup.js\";\nimport type { ThemeColors } from \"./styles/theme.js\";\n\nexport interface AnnotationComplete {\n annotation: AnnotationPayload;\n type: FeedbackType;\n message: string;\n}\n\n/**\n * Annotation mode: full-page overlay with rectangle drawing.\n *\n * Glassmorphism design:\n * - Frosted glass toolbar at top\n * - Subtle tinted overlay\n * - Accent-colored drawing rectangle with glow\n */\nexport class Annotator {\n private overlay: HTMLElement | null = null;\n private toolbar: HTMLElement | null = null;\n private drawingRect: HTMLElement | null = null;\n private startX = 0;\n private startY = 0;\n private isDrawing = false;\n private isActive = false;\n private popup: Popup;\n private savedOverflow = \"\";\n private preActiveFocusElement: Element | null = null;\n private rafId: number | null = null;\n private pendingMoveEvent: MouseEvent | Touch | null = null;\n\n constructor(\n private readonly colors: ThemeColors,\n private readonly bus: EventBus<WidgetEvents>,\n private readonly t: TFunction,\n ) {\n this.popup = new Popup(colors, t);\n\n this.bus.on(\"annotation:start\", () => this.activate());\n }\n\n private activate(): void {\n if (this.isActive) return;\n this.isActive = true;\n\n // Capture the focused element before activation for keyboard annotation\n this.preActiveFocusElement = document.activeElement;\n\n // Lock page scroll\n this.savedOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n // Overlay — subtle blue tint for depth\n this.overlay = el(\"div\", {\n style: `\n position:fixed;inset:0;\n z-index:2147483646;\n background:rgba(15, 23, 42, 0.04);\n cursor:crosshair;\n `,\n });\n this.overlay.setAttribute(\"aria-hidden\", \"true\");\n\n // Toolbar — glassmorphism bar\n this.toolbar = el(\"div\", {\n style: `\n position:fixed;top:0;left:0;right:0;\n z-index:2147483647;\n height:52px;\n background:${this.colors.glassBg};\n backdrop-filter:blur(24px);\n -webkit-backdrop-filter:blur(24px);\n border-bottom:1px solid ${this.colors.glassBorder};\n display:flex;align-items:center;justify-content:center;gap:16px;\n font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n font-size:14px;color:${this.colors.text};\n box-shadow:0 4px 16px ${this.colors.shadow};\n -webkit-font-smoothing:antialiased;\n `,\n });\n\n const dot = el(\"span\", {\n style: `\n width:8px;height:8px;border-radius:50%;\n background:${this.colors.accent};\n box-shadow:0 0 8px ${this.colors.accentGlow};\n animation:pulse 1.5s ease-in-out infinite;\n `,\n });\n\n // Add pulse animation inline (respects prefers-reduced-motion)\n const style = document.createElement(\"style\");\n style.textContent = [\n \"@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}\",\n \"@media(prefers-reduced-motion:reduce){@keyframes pulse{from,to{opacity:1}}}\",\n ].join(\"\");\n this.toolbar.appendChild(style);\n\n const instruction = el(\"span\", { style: \"font-weight:500;letter-spacing:-0.01em;\" });\n setText(instruction, this.t(\"annotator.instruction\"));\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.style.cssText = `\n height:34px;padding:0 18px;border-radius:9999px;\n border:1px solid ${this.colors.border};\n background:${this.colors.glassBg};\n color:${this.colors.textTertiary};font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n font-size:13px;font-weight:500;cursor:pointer;\n transition:all 0.2s ease;\n `;\n setText(cancelBtn, this.t(\"annotator.cancel\"));\n cancelBtn.addEventListener(\"click\", () => this.deactivate());\n cancelBtn.addEventListener(\"mouseenter\", () => {\n cancelBtn.style.borderColor = this.colors.typeBug;\n cancelBtn.style.color = this.colors.typeBug;\n cancelBtn.style.background = this.colors.typeBugBg;\n });\n cancelBtn.addEventListener(\"mouseleave\", () => {\n cancelBtn.style.borderColor = this.colors.border;\n cancelBtn.style.color = this.colors.textTertiary;\n cancelBtn.style.background = this.colors.glassBg;\n });\n\n this.toolbar.appendChild(dot);\n this.toolbar.appendChild(instruction);\n this.toolbar.appendChild(cancelBtn);\n\n // Mouse events\n this.overlay.addEventListener(\"mousedown\", this.onMouseDown);\n this.overlay.addEventListener(\"mousemove\", this.onMouseMove);\n this.overlay.addEventListener(\"mouseup\", this.onMouseUp);\n\n // Touch events (Surface Pro, iPad, etc.)\n this.overlay.addEventListener(\"touchstart\", this.onTouchStart, { passive: false });\n this.overlay.addEventListener(\"touchmove\", this.onTouchMove, { passive: false });\n this.overlay.addEventListener(\"touchend\", this.onTouchEnd);\n\n // Keyboard annotation: Enter selects the pre-activation focused element\n this.overlay.addEventListener(\"keydown\", this.onOverlayKeyDown);\n\n // Allow tab-through so keyboard users can reach underlying elements\n this.overlay.setAttribute(\"tabindex\", \"0\");\n\n // Escape to cancel\n document.addEventListener(\"keydown\", this.onKeyDown);\n\n document.body.appendChild(this.overlay);\n document.body.appendChild(this.toolbar);\n }\n\n private deactivate(): void {\n if (!this.isActive) return;\n this.isActive = false;\n this.isDrawing = false;\n this.preActiveFocusElement = null;\n\n // Cancel any pending rAF to prevent stale callbacks\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.pendingMoveEvent = null;\n\n document.body.style.overflow = this.savedOverflow;\n document.removeEventListener(\"keydown\", this.onKeyDown);\n\n this.overlay?.remove();\n this.toolbar?.remove();\n this.drawingRect?.remove();\n this.overlay = null;\n this.toolbar = null;\n this.drawingRect = null;\n\n this.bus.emit(\"annotation:end\");\n }\n\n private onKeyDown = (e: KeyboardEvent): void => {\n if (e.key === \"Escape\") this.deactivate();\n };\n\n /**\n * Keyboard annotation: pressing Enter while the overlay is active selects\n * the element that was focused before activation and creates a full-bounds\n * annotation covering that element (WCAG 2.1.1 Level A).\n */\n private onOverlayKeyDown = async (e: KeyboardEvent): Promise<void> => {\n if (e.key !== \"Enter\") return;\n e.preventDefault();\n\n const target = this.preActiveFocusElement;\n if (!target || !(target instanceof HTMLElement)) return;\n\n const bounds = target.getBoundingClientRect();\n if (bounds.width <= 0 || bounds.height <= 0) return;\n\n const rectBounds = new DOMRect(bounds.x, bounds.y, bounds.width, bounds.height);\n\n const result = await this.popup.show(rectBounds);\n if (!result) return;\n\n const anchor = generateAnchor(target);\n const annotation: AnnotationPayload = {\n anchor,\n rect: { xPct: 0, yPct: 0, wPct: 1, hPct: 1 },\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n viewportW: window.innerWidth,\n viewportH: window.innerHeight,\n devicePixelRatio: window.devicePixelRatio,\n };\n\n this.deactivate();\n\n this.bus.emit(\"annotation:complete\", {\n annotation,\n type: result.type,\n message: result.message,\n });\n };\n\n private onMouseDown = (e: MouseEvent): void => {\n this.startDrawing(e.clientX, e.clientY);\n };\n\n private onTouchStart = (e: TouchEvent): void => {\n e.preventDefault();\n const touch = e.touches[0];\n if (touch) this.startDrawing(touch.clientX, touch.clientY);\n };\n\n private startDrawing(clientX: number, clientY: number): void {\n this.isDrawing = true;\n this.startX = clientX;\n this.startY = clientY;\n\n this.drawingRect?.remove();\n this.drawingRect = el(\"div\", {\n style: `\n position:fixed;\n border:2px solid ${this.colors.accent};\n background:${this.colors.accent}12;\n pointer-events:none;\n border-radius:8px;\n box-shadow:0 0 16px ${this.colors.accentGlow};\n transition:box-shadow 0.15s ease;\n `,\n });\n this.overlay?.appendChild(this.drawingRect);\n }\n\n private onMouseMove = (e: MouseEvent): void => {\n this.scheduleRectUpdate(e);\n };\n\n private onTouchMove = (e: TouchEvent): void => {\n e.preventDefault();\n if (e.touches[0]) this.scheduleRectUpdate(e.touches[0]);\n };\n\n private scheduleRectUpdate(source: MouseEvent | Touch): void {\n if (!this.isDrawing || !this.drawingRect) return;\n\n this.pendingMoveEvent = source;\n if (this.rafId !== null) return;\n\n this.rafId = requestAnimationFrame(() => {\n this.rafId = null;\n const evt = this.pendingMoveEvent;\n if (!evt || !this.drawingRect) return;\n\n const x = Math.min(evt.clientX, this.startX);\n const y = Math.min(evt.clientY, this.startY);\n const w = Math.abs(evt.clientX - this.startX);\n const h = Math.abs(evt.clientY - this.startY);\n\n this.drawingRect.style.left = `${x}px`;\n this.drawingRect.style.top = `${y}px`;\n this.drawingRect.style.width = `${w}px`;\n this.drawingRect.style.height = `${h}px`;\n });\n }\n\n private onTouchEnd = async (e: TouchEvent): Promise<void> => {\n const touch = e.changedTouches[0];\n if (touch) await this.finishDrawing(touch.clientX, touch.clientY);\n };\n\n private onMouseUp = async (e: MouseEvent): Promise<void> => {\n await this.finishDrawing(e.clientX, e.clientY);\n };\n\n private finishDrawing = async (clientX: number, clientY: number): Promise<void> => {\n if (!this.isDrawing || !this.drawingRect) return;\n this.isDrawing = false;\n\n const x = Math.min(clientX, this.startX);\n const y = Math.min(clientY, this.startY);\n const w = Math.abs(clientX - this.startX);\n const h = Math.abs(clientY - this.startY);\n\n // Ignore tiny rectangles (accidental clicks)\n if (w < 10 || h < 10) {\n this.drawingRect.remove();\n this.drawingRect = null;\n return;\n }\n\n const rectBounds = new DOMRect(x, y, w, h);\n\n // Show popup for type + message\n const result = await this.popup.show(rectBounds);\n\n if (!result) {\n this.drawingRect?.remove();\n this.drawingRect = null;\n return;\n }\n\n // Build annotation payload BEFORE deactivating (needs overlay for elementFromPoint)\n const annotation = this.buildAnnotation(rectBounds);\n this.drawingRect?.remove();\n this.drawingRect = null;\n this.deactivate();\n\n // Emit via event bus (not DOM — overlay is already null after deactivate)\n this.bus.emit(\"annotation:complete\", {\n annotation,\n type: result.type,\n message: result.message,\n });\n };\n\n /**\n * Build an AnnotationPayload from a drawn rectangle.\n * Temporarily hides the overlay to access the real DOM underneath.\n */\n private buildAnnotation(rectBounds: DOMRect): AnnotationPayload {\n // Temporarily hide overlay to find the real element underneath\n if (this.overlay) this.overlay.style.pointerEvents = \"none\";\n const anchorElement = findAnchorElement(rectBounds);\n if (this.overlay) this.overlay.style.pointerEvents = \"auto\";\n\n const anchor = generateAnchor(anchorElement);\n const anchorBounds = anchorElement.getBoundingClientRect();\n const rect = rectToPercentages(rectBounds, anchorBounds);\n\n return {\n anchor,\n rect,\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n viewportW: window.innerWidth,\n viewportH: window.innerHeight,\n devicePixelRatio: window.devicePixelRatio,\n };\n }\n destroy(): void {\n this.deactivate();\n this.popup.destroy();\n }\n}\n","import type { FeedbackPayload, FeedbackResponse, FeedbackStatus, FeedbackType } from \"@siteping/core\";\n\n/**\n * Abstract client interface used by the widget internals.\n *\n * `ApiClient` (HTTP mode) and `StoreClient` (direct store mode) both satisfy\n * this interface, allowing the widget to work identically in either mode.\n */\nexport interface WidgetClient {\n sendFeedback(payload: FeedbackPayload): Promise<FeedbackResponse>;\n getFeedbacks(\n projectName: string,\n options?: { page?: number; limit?: number; type?: FeedbackType; status?: FeedbackStatus; search?: string },\n ): Promise<{ feedbacks: FeedbackResponse[]; total: number }>;\n resolveFeedback(id: string, resolved: boolean): Promise<FeedbackResponse>;\n deleteFeedback(id: string): Promise<void>;\n deleteAllFeedbacks(projectName: string): Promise<void>;\n}\n\nconst MAX_RETRIES = 3;\nconst TIMEOUT_MS = 10_000;\nconst RETRY_QUEUE_KEY = \"siteping_retry_queue\";\nconst MAX_QUEUE_SIZE = 20;\n\n// ---------------------------------------------------------------------------\n// Core fetch with retry + exponential backoff + jitter\n// ---------------------------------------------------------------------------\n\nasync function resilientFetch(url: string, init: RequestInit, retries = MAX_RETRIES): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);\n\n try {\n const response = await fetch(url, {\n ...init,\n signal: controller.signal,\n });\n clearTimeout(timeout);\n\n // Don't retry client errors (4xx) — only server errors (5xx)\n if (response.ok || (response.status >= 400 && response.status < 500)) {\n return response;\n }\n\n if (attempt === retries) return response;\n } catch (error) {\n clearTimeout(timeout);\n if (attempt === retries) throw error;\n }\n\n // Exponential backoff with jitter: 1s, 2s, 4s + random ±500ms\n const baseDelay = 1000 * 2 ** attempt;\n const jitter = Math.random() * 1000 - 500;\n await new Promise((r) => setTimeout(r, baseDelay + jitter));\n }\n\n throw new Error(\"Max retries exceeded\");\n}\n\n// ---------------------------------------------------------------------------\n// Retry queue — persist failed feedbacks for retry on next page load\n// ---------------------------------------------------------------------------\n\nfunction queueForRetry(endpoint: string, payload: FeedbackPayload): void {\n try {\n const raw = localStorage.getItem(RETRY_QUEUE_KEY);\n const parsed: unknown = raw ? JSON.parse(raw) : [];\n const queue: Array<{ endpoint: string; payload: FeedbackPayload }> = Array.isArray(parsed)\n ? (parsed as Array<{ endpoint: string; payload: FeedbackPayload }>)\n : [];\n\n // Cap queue size to prevent unbounded localStorage growth\n if (queue.length >= MAX_QUEUE_SIZE) {\n queue.shift(); // Drop oldest entry\n }\n\n queue.push({ endpoint, payload });\n localStorage.setItem(RETRY_QUEUE_KEY, JSON.stringify(queue));\n } catch {\n // localStorage full or unavailable — silently drop\n }\n}\n\nexport async function flushRetryQueue(endpoint: string): Promise<void> {\n try {\n const raw = localStorage.getItem(RETRY_QUEUE_KEY);\n if (!raw) return;\n\n const parsed: unknown = JSON.parse(raw);\n const queue: Array<{ endpoint: string; payload: FeedbackPayload }> = Array.isArray(parsed)\n ? (parsed as Array<{ endpoint: string; payload: FeedbackPayload }>)\n : [];\n\n const toRetry = queue.filter((e) => e.endpoint === endpoint);\n if (toRetry.length === 0) return;\n\n // Process items sequentially to avoid overwhelming the server\n const failed: Array<{ endpoint: string; payload: FeedbackPayload }> = [];\n for (const entry of toRetry) {\n try {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(entry.payload),\n });\n if (!res.ok) {\n failed.push(entry);\n }\n } catch {\n failed.push(entry);\n }\n }\n\n // Rebuild queue: keep unrelated entries + failed retries\n const remaining = queue.filter((e) => e.endpoint !== endpoint).concat(failed);\n if (remaining.length > 0) {\n localStorage.setItem(RETRY_QUEUE_KEY, JSON.stringify(remaining));\n } else {\n localStorage.removeItem(RETRY_QUEUE_KEY);\n }\n } catch {\n // Ignore — localStorage may be unavailable\n }\n}\n\n// ---------------------------------------------------------------------------\n// API client\n// ---------------------------------------------------------------------------\n\nexport class ApiClient {\n constructor(\n private readonly endpoint: string,\n private readonly projectName: string,\n ) {}\n\n async sendFeedback(payload: FeedbackPayload): Promise<FeedbackResponse> {\n try {\n const response = await resilientFetch(this.endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"Unknown error\");\n throw new Error(`Failed to send feedback: ${response.status} ${text}`);\n }\n\n return (await response.json()) as FeedbackResponse; // Server validates via Zod\n } catch (error) {\n queueForRetry(this.endpoint, payload);\n throw error;\n }\n }\n\n async getFeedbacks(\n projectName: string,\n options?: {\n page?: number;\n limit?: number;\n type?: FeedbackType;\n status?: FeedbackStatus;\n search?: string;\n },\n ): Promise<{ feedbacks: FeedbackResponse[]; total: number }> {\n const params = new URLSearchParams({ projectName });\n if (options?.page) params.set(\"page\", String(options.page));\n if (options?.limit) params.set(\"limit\", String(options.limit));\n if (options?.type) params.set(\"type\", options.type);\n if (options?.status) params.set(\"status\", options.status);\n if (options?.search) params.set(\"search\", options.search);\n\n const response = await resilientFetch(`${this.endpoint}?${params.toString()}`, { method: \"GET\" });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch feedbacks: ${response.status}`);\n }\n\n return (await response.json()) as { feedbacks: FeedbackResponse[]; total: number }; // Server validates via Zod\n }\n\n async resolveFeedback(id: string, resolved: boolean): Promise<FeedbackResponse> {\n const response = await resilientFetch(this.endpoint, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ id, projectName: this.projectName, status: resolved ? \"resolved\" : \"open\" }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to update feedback: ${response.status}`);\n }\n\n return (await response.json()) as FeedbackResponse; // Server validates via Zod\n }\n\n async deleteFeedback(id: string): Promise<void> {\n const response = await resilientFetch(this.endpoint, {\n method: \"DELETE\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ id, projectName: this.projectName }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete feedback: ${response.status}`);\n }\n }\n\n async deleteAllFeedbacks(projectName: string): Promise<void> {\n const response = await resilientFetch(this.endpoint, {\n method: \"DELETE\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ projectName, deleteAll: true }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete all feedbacks: ${response.status}`);\n }\n }\n}\n","type Listener = (...args: unknown[]) => void;\n\n/**\n * Lightweight typed EventEmitter — zero dependencies.\n */\nexport class EventBus<E extends { [K in keyof E]: unknown[] }> {\n private listeners = new Map<keyof E, Set<Listener>>();\n\n on<K extends keyof E>(event: K, listener: (...args: E[K]) => void): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const set = this.listeners.get(event)!;\n set.add(listener as Listener);\n\n return () => {\n set.delete(listener as Listener);\n };\n }\n\n off<K extends keyof E>(event: K, listener: (...args: E[K]) => void): void {\n const set = this.listeners.get(event);\n if (set) {\n set.delete(listener as Listener);\n }\n }\n\n emit<K extends keyof E>(event: K, ...args: E[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const fn of set) {\n try {\n fn(...args);\n } catch (err) {\n // Isolate listener errors — one bad listener must not kill others\n console.error(`[siteping] Error in event listener for \"${String(event)}\":`, err);\n }\n }\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Widget event types\n// ---------------------------------------------------------------------------\n\nexport interface WidgetEvents {\n open: [];\n close: [];\n \"feedback:sent\": [import(\"@siteping/core\").FeedbackResponse];\n \"feedback:deleted\": [string];\n \"feedback:all-deleted\": [];\n \"feedback:error\": [Error];\n \"annotation:start\": [];\n \"annotation:end\": [];\n \"annotation:complete\": [import(\"./annotator.js\").AnnotationComplete];\n \"annotations:toggle\": [boolean];\n \"panel:toggle\": [boolean];\n}\n\n/** Subset of WidgetEvents exposed to consumers via SitepingInstance */\nexport interface PublicWidgetEvents {\n \"feedback:sent\": [import(\"@siteping/core\").FeedbackResponse];\n \"feedback:deleted\": [string];\n \"panel:open\": [];\n \"panel:close\": [];\n}\n","import type { SitepingConfig } from \"@siteping/core\";\nimport { parseSvg, setText } from \"./dom-utils.js\";\nimport type { EventBus, WidgetEvents } from \"./events.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport { ICON_ANNOTATE, ICON_CHAT, ICON_CLOSE, ICON_EYE, ICON_EYE_OFF, ICON_SITEPING } from \"./icons.js\";\n\ninterface RadialItem {\n id: string;\n icon: string;\n iconAlt?: string;\n label: string;\n}\n\nconst ITEM_GAP = 54;\n\n/**\n * Floating Action Button with radial menu and notification badge.\n *\n * Glassmorphism: gradient background, glow shadow, glass radial items.\n * Badge shows unresolved feedback count.\n */\nexport class Fab {\n private root: HTMLElement;\n private fab: HTMLButtonElement;\n private radialContainer: HTMLElement;\n private badgeEl: HTMLElement | null = null;\n private isOpen = false;\n private annotationsVisible = true;\n private items: RadialItem[];\n\n constructor(\n shadowRoot: ShadowRoot,\n config: SitepingConfig,\n private readonly bus: EventBus<WidgetEvents>,\n private readonly t: TFunction,\n ) {\n const position = config.position ?? \"bottom-right\";\n const isRight = position === \"bottom-right\";\n\n // Vertical stack above the FAB\n this.items = [\n { id: \"chat\", icon: ICON_CHAT, label: t(\"fab.messages\") },\n { id: \"annotate\", icon: ICON_ANNOTATE, label: t(\"fab.annotate\") },\n { id: \"toggle-annotations\", icon: ICON_EYE, iconAlt: ICON_EYE_OFF, label: t(\"fab.annotations\") },\n ];\n\n // FAB button — needs position:relative for badge positioning\n this.fab = document.createElement(\"button\");\n this.fab.className = `sp-fab sp-fab--${position} sp-anim-fab-in`;\n this.fab.style.position = \"fixed\"; // ensure fixed even with relative children\n this.fab.appendChild(parseSvg(ICON_SITEPING));\n this.fab.setAttribute(\"aria-label\", t(\"fab.aria\"));\n this.fab.setAttribute(\"aria-expanded\", \"false\");\n this.fab.addEventListener(\"click\", () => this.toggle());\n\n // Radial container\n this.radialContainer = document.createElement(\"div\");\n this.radialContainer.className = `sp-radial sp-radial--${position}`;\n this.radialContainer.setAttribute(\"role\", \"menu\");\n\n for (let i = 0; i < this.items.length; i++) {\n const item = this.items[i];\n if (!item) continue;\n const btn = document.createElement(\"button\");\n btn.className = \"sp-radial-item\";\n btn.style.setProperty(\"--sp-i\", String(i));\n btn.appendChild(parseSvg(item.icon));\n btn.setAttribute(\"role\", \"menuitem\");\n btn.setAttribute(\"aria-label\", item.label);\n btn.dataset.itemId = item.id;\n\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.handleItemClick(item.id);\n });\n\n const label = document.createElement(\"span\");\n label.className = \"sp-radial-label\";\n label.textContent = item.label;\n label.style.cssText = isRight\n ? \"position:absolute; right:54px; top:50%; transform:translateY(-50%); white-space:nowrap;\"\n : \"position:absolute; left:54px; top:50%; transform:translateY(-50%); white-space:nowrap;\";\n btn.appendChild(label);\n\n this.radialContainer.appendChild(btn);\n }\n\n this.root = document.createElement(\"div\");\n this.root.appendChild(this.radialContainer);\n this.root.appendChild(this.fab);\n shadowRoot.appendChild(this.root);\n\n // Close radial menu on click outside.\n const host = shadowRoot.host;\n this.onDocumentClick = (e: MouseEvent) => {\n if (this.isOpen && !e.composedPath().includes(host)) {\n this.close();\n }\n };\n document.addEventListener(\"click\", this.onDocumentClick);\n\n // Escape on FAB or menu container closes the menu\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && this.isOpen) {\n e.stopPropagation();\n this.close();\n }\n };\n this.fab.addEventListener(\"keydown\", handleEscape);\n this.radialContainer.addEventListener(\"keydown\", handleEscape);\n\n // Arrow key navigation within the radial menu\n this.radialContainer.addEventListener(\"keydown\", (e) => {\n const items = Array.from(this.radialContainer.querySelectorAll<HTMLButtonElement>(\".sp-radial-item\"));\n if (items.length === 0 || !this.isOpen) return;\n const activeEl = (shadowRoot.activeElement ?? document.activeElement) as HTMLElement;\n const currentIndex = items.indexOf(activeEl as HTMLButtonElement);\n\n switch (e.key) {\n case \"ArrowUp\": {\n e.preventDefault();\n const nextIndex = currentIndex <= 0 ? items.length - 1 : currentIndex - 1;\n items[nextIndex]?.focus();\n break;\n }\n case \"ArrowDown\": {\n e.preventDefault();\n const nextIndex = currentIndex >= items.length - 1 ? 0 : currentIndex + 1;\n items[nextIndex]?.focus();\n break;\n }\n case \"Home\": {\n e.preventDefault();\n items[0]?.focus();\n break;\n }\n case \"End\": {\n e.preventDefault();\n items[items.length - 1]?.focus();\n break;\n }\n }\n });\n }\n\n private onDocumentClick: (e: MouseEvent) => void;\n\n /** Update the badge count. Pass 0 to hide. */\n updateBadge(count: number): void {\n if (count <= 0) {\n this.badgeEl?.remove();\n this.badgeEl = null;\n return;\n }\n\n if (!this.badgeEl) {\n this.badgeEl = document.createElement(\"span\");\n this.badgeEl.className = \"sp-fab-badge\";\n this.badgeEl.setAttribute(\"role\", \"status\");\n this.badgeEl.setAttribute(\"aria-live\", \"polite\");\n this.fab.appendChild(this.badgeEl);\n }\n\n const displayText = count > 99 ? \"99+\" : String(count);\n setText(this.badgeEl, displayText);\n this.badgeEl.setAttribute(\"aria-label\", this.t(\"fab.badge\").replace(\"{count}\", String(count)));\n }\n\n private toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n private open(): void {\n this.isOpen = true;\n this.setFabIcon(ICON_CLOSE);\n this.fab.setAttribute(\"aria-expanded\", \"true\");\n\n const buttons = this.radialContainer.querySelectorAll<HTMLButtonElement>(\".sp-radial-item\");\n buttons.forEach((btn, i) => {\n // Stack vertically above the FAB with initial offset + gap\n const y = -(16 + ITEM_GAP * (i + 1));\n btn.style.transform = `translate(0px, ${y}px) scale(1)`;\n btn.classList.add(\"sp-radial-item--open\");\n });\n\n // Focus the first menu item after animation\n requestAnimationFrame(() => {\n const firstItem = this.radialContainer.querySelector<HTMLButtonElement>(\".sp-radial-item\");\n firstItem?.focus();\n });\n }\n\n private close(): void {\n this.isOpen = false;\n this.setFabIcon(ICON_SITEPING);\n this.fab.setAttribute(\"aria-expanded\", \"false\");\n\n const buttons = this.radialContainer.querySelectorAll<HTMLButtonElement>(\".sp-radial-item\");\n buttons.forEach((btn) => {\n btn.style.transform = \"translate(0, 0) scale(0.8)\";\n btn.classList.remove(\"sp-radial-item--open\");\n });\n\n // Return focus to FAB\n this.fab.focus();\n }\n\n private setFabIcon(svgStr: string): void {\n const badge = this.badgeEl;\n this.fab.replaceChildren(parseSvg(svgStr));\n // Re-append badge after icon swap\n if (badge) this.fab.appendChild(badge);\n }\n\n private handleItemClick(id: string): void {\n this.close();\n\n switch (id) {\n case \"chat\":\n this.bus.emit(\"panel:toggle\", true);\n break;\n case \"annotate\":\n this.bus.emit(\"annotation:start\");\n break;\n case \"toggle-annotations\": {\n this.annotationsVisible = !this.annotationsVisible;\n this.bus.emit(\"annotations:toggle\", this.annotationsVisible);\n const btn = this.radialContainer.querySelector('[data-item-id=\"toggle-annotations\"]');\n if (btn) {\n btn.replaceChildren(parseSvg(this.annotationsVisible ? ICON_EYE : ICON_EYE_OFF));\n }\n break;\n }\n }\n }\n\n destroy(): void {\n document.removeEventListener(\"click\", this.onDocumentClick);\n this.root.remove();\n }\n}\n","import type { Translations } from \"./types.js\";\n\nexport const en: Translations = {\n // Panel\n \"panel.title\": \"Feedbacks\",\n \"panel.ariaLabel\": \"Siteping feedback panel\",\n \"panel.feedbackList\": \"Feedback list\",\n \"panel.loading\": \"Loading feedbacks\",\n \"panel.close\": \"Close panel\",\n \"panel.deleteAll\": \"Delete all\",\n \"panel.deleteAllConfirmTitle\": \"Delete all\",\n \"panel.deleteAllConfirmMessage\": \"Delete all feedbacks for this project? This action cannot be undone.\",\n \"panel.search\": \"Search...\",\n \"panel.searchAria\": \"Search feedbacks\",\n \"panel.filterAll\": \"All\",\n \"panel.loadError\": \"Failed to load\",\n \"panel.retry\": \"Retry\",\n \"panel.empty\": \"No feedback yet\",\n \"panel.showMore\": \"Show more\",\n \"panel.showLess\": \"Show less\",\n \"panel.resolve\": \"Resolve\",\n \"panel.reopen\": \"Reopen\",\n \"panel.delete\": \"Delete\",\n \"panel.cancel\": \"Cancel\",\n \"panel.confirmDelete\": \"Delete\",\n\n // Feedback type labels\n \"type.question\": \"Question\",\n \"type.change\": \"Change\",\n \"type.bug\": \"Bug\",\n \"type.other\": \"Other\",\n\n // FAB menu\n \"fab.aria\": \"Siteping \\u2014 Feedback menu\",\n \"fab.messages\": \"Messages\",\n \"fab.annotate\": \"Annotate\",\n \"fab.annotations\": \"Annotations\",\n\n // Annotator\n \"annotator.instruction\": \"Draw a rectangle on the area to comment\",\n \"annotator.cancel\": \"Cancel\",\n\n // Popup\n \"popup.ariaLabel\": \"Feedback form\",\n \"popup.placeholder\": \"Describe your feedback...\",\n \"popup.textareaAria\": \"Feedback message\",\n \"popup.submitHintMac\": \"\\u2318+Enter to send\",\n \"popup.submitHintOther\": \"Ctrl+Enter to send\",\n \"popup.cancel\": \"Cancel\",\n \"popup.submit\": \"Send\",\n\n // Identity modal\n \"identity.title\": \"Identify yourself\",\n \"identity.nameLabel\": \"Name\",\n \"identity.namePlaceholder\": \"Your name\",\n \"identity.emailLabel\": \"Email\",\n \"identity.emailPlaceholder\": \"your@email.com\",\n \"identity.cancel\": \"Cancel\",\n \"identity.submit\": \"Continue\",\n\n // Markers\n \"marker.approximate\": \"Approximate position (confidence: {confidence}%)\",\n \"marker.aria\": \"Feedback #{number}: {type} — {message}\",\n\n // FAB badge\n \"fab.badge\": \"{count} unresolved feedbacks\",\n\n // Accessibility — screen reader announcements\n \"feedback.sent.confirmation\": \"Feedback sent successfully\",\n \"feedback.error.message\": \"Failed to send feedback\",\n \"feedback.deleted.confirmation\": \"Feedback deleted\",\n\n // Badge\n \"badge.count\": \"{count} unresolved feedbacks\",\n};\n","import type { Translations } from \"./types.js\";\n\nexport const fr: Translations = {\n // Panel\n \"panel.title\": \"Feedbacks\",\n \"panel.ariaLabel\": \"Panneau de feedback Siteping\",\n \"panel.feedbackList\": \"Liste des feedbacks\",\n \"panel.loading\": \"Chargement des feedbacks\",\n \"panel.close\": \"Fermer le panneau\",\n \"panel.deleteAll\": \"Tout supprimer\",\n \"panel.deleteAllConfirmTitle\": \"Tout supprimer\",\n \"panel.deleteAllConfirmMessage\": \"Supprimer tous les feedbacks de ce projet ? Cette action est irr\\u00e9versible.\",\n \"panel.search\": \"Rechercher...\",\n \"panel.searchAria\": \"Rechercher dans les feedbacks\",\n \"panel.filterAll\": \"Tous\",\n \"panel.loadError\": \"Erreur de chargement\",\n \"panel.retry\": \"R\\u00e9essayer\",\n \"panel.empty\": \"Aucun feedback pour le moment\",\n \"panel.showMore\": \"Voir plus\",\n \"panel.showLess\": \"Voir moins\",\n \"panel.resolve\": \"R\\u00e9soudre\",\n \"panel.reopen\": \"Rouvrir\",\n \"panel.delete\": \"Supprimer\",\n \"panel.cancel\": \"Annuler\",\n \"panel.confirmDelete\": \"Supprimer\",\n\n // Feedback type labels\n \"type.question\": \"Question\",\n \"type.change\": \"Changement\",\n \"type.bug\": \"Bug\",\n \"type.other\": \"Autre\",\n\n // FAB menu\n \"fab.aria\": \"Siteping \\u2014 Menu feedback\",\n \"fab.messages\": \"Messages\",\n \"fab.annotate\": \"Annoter\",\n \"fab.annotations\": \"Annotations\",\n\n // Annotator\n \"annotator.instruction\": \"Tracez un rectangle sur la zone \\u00e0 commenter\",\n \"annotator.cancel\": \"Annuler\",\n\n // Popup\n \"popup.ariaLabel\": \"Formulaire de feedback\",\n \"popup.placeholder\": \"D\\u00e9crivez votre retour...\",\n \"popup.textareaAria\": \"Message de feedback\",\n \"popup.submitHintMac\": \"\\u2318+Entr\\u00e9e pour envoyer\",\n \"popup.submitHintOther\": \"Ctrl+Entr\\u00e9e pour envoyer\",\n \"popup.cancel\": \"Annuler\",\n \"popup.submit\": \"Envoyer\",\n\n // Identity modal\n \"identity.title\": \"Identifiez-vous\",\n \"identity.nameLabel\": \"Nom\",\n \"identity.namePlaceholder\": \"Votre nom\",\n \"identity.emailLabel\": \"Email\",\n \"identity.emailPlaceholder\": \"votre@email.com\",\n \"identity.cancel\": \"Annuler\",\n \"identity.submit\": \"Continuer\",\n\n // Markers\n \"marker.approximate\": \"Position approximative (confiance : {confidence}%)\",\n \"marker.aria\": \"Feedback n°{number} : {type} — {message}\",\n\n // FAB badge\n \"fab.badge\": \"{count} feedbacks non résolus\",\n\n // Accessibility — screen reader announcements\n \"feedback.sent.confirmation\": \"Feedback envoyé avec succès\",\n \"feedback.error.message\": \"Échec de l'envoi du feedback\",\n \"feedback.deleted.confirmation\": \"Feedback supprimé\",\n\n // Badge\n \"badge.count\": \"{count} feedbacks non résolus\",\n};\n","import type { TFunction, Translations } from \"./types.js\";\n\nexport type { TFunction, Translations } from \"./types.js\";\n\n// Static imports — bundler (tsup) will include both.\n// For tree-shaking in consumer apps, use dynamic import() with a bundler plugin.\nimport { en } from \"./en.js\";\nimport { fr } from \"./fr.js\";\n\nconst LOCALES: Record<string, Translations> = { fr, en };\n\n/** Register a custom locale at runtime. */\nexport function registerLocale(code: string, translations: Translations): void {\n LOCALES[code] = translations;\n}\n\n/**\n * Create a translation function for the given locale.\n *\n * Locale resolution: exact match > language prefix > English fallback.\n */\nexport function createT(locale: string): TFunction {\n const lang = (locale.split(\"-\")[0] ?? locale).toLowerCase();\n if (!LOCALES[lang]) {\n console.warn(`[siteping] Unknown locale \"${locale}\", falling back to \"en\"`);\n }\n const dict = LOCALES[lang] ?? LOCALES.en ?? ({} as Translations);\n return (key) => dict[key] ?? key;\n}\n\n/**\n * Returns the type label for a FeedbackType value.\n * Maps API enum values (english) to localized display labels.\n */\nexport function getTypeLabel(type: string, t: TFunction): string {\n switch (type) {\n case \"question\":\n return t(\"type.question\");\n case \"change\":\n return t(\"type.change\");\n case \"bug\":\n return t(\"type.bug\");\n case \"other\":\n return t(\"type.other\");\n default:\n return type;\n }\n}\n","const STORAGE_KEY = \"siteping_identity\";\n\nexport interface Identity {\n name: string;\n email: string;\n}\n\nexport function getIdentity(): Identity | null {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n \"name\" in parsed &&\n typeof (parsed as Record<string, unknown>).name === \"string\" &&\n \"email\" in parsed &&\n typeof (parsed as Record<string, unknown>).email === \"string\"\n ) {\n const identity = parsed as Identity;\n if (identity.name && identity.email) return identity;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function saveIdentity(identity: Identity): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(identity));\n } catch {\n // Quota exceeded or localStorage disabled — identity works for this session only\n }\n}\n","/**\n * Lightweight fuzzy text matching for DOM re-anchoring.\n * Zero dependencies — bundled into the widget.\n * Uses Levenshtein distance, optimized for short strings (~50 chars).\n */\n\n/**\n * Levenshtein edit distance.\n * O(n*m) time, O(min(n,m)) space.\n */\nexport function editDistance(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Ensure `a` is the shorter string for space optimization\n if (a.length > b.length) {\n const t = a;\n a = b;\n b = t;\n }\n\n const aLen = a.length;\n const bLen = b.length;\n let prev = new Array<number>(aLen + 1);\n for (let k = 0; k <= aLen; k++) prev[k] = k;\n let curr = new Array<number>(aLen + 1);\n\n for (let j = 1; j <= bLen; j++) {\n curr[0] = j;\n for (let i = 1; i <= aLen; i++) {\n // Indices are valid: i-1 in [0, aLen-1], j-1 in [0, bLen-1], loop bounds guarantee access\n const prevDiag = prev[i - 1] ?? 0;\n curr[i] = a[i - 1] === b[j - 1] ? prevDiag : 1 + Math.min(prevDiag, prev[i] ?? 0, curr[i - 1] ?? 0);\n }\n const tmp = prev;\n prev = curr;\n curr = tmp;\n }\n\n return prev[aLen] ?? 0; // aLen is within bounds — prev has aLen+1 entries\n}\n\n/**\n * Normalized similarity score (0–1, where 1 = identical).\n */\nexport function similarity(a: string, b: string): number {\n if (a === b) return 1;\n const maxLen = Math.max(a.length, b.length);\n if (maxLen === 0) return 1;\n return 1 - editDistance(a, b) / maxLen;\n}\n\n/**\n * Fuzzy substring search — checks if `needle` approximately exists in `haystack`.\n * Slides a window of `needle.length` over the haystack and returns the best\n * similarity score found. Returns 0 if below `minScore`.\n */\nexport function fuzzyIncludes(haystack: string, needle: string, minScore = 0.6): number {\n if (!needle || !haystack) return 0;\n if (haystack.includes(needle)) return 1;\n\n const nLen = needle.length;\n\n // If needle is longer than haystack, compare directly\n if (nLen > haystack.length) {\n const score = similarity(haystack, needle);\n return score >= minScore ? score : 0;\n }\n\n let best = 0;\n\n // Cap haystack to avoid O(n²) on huge text nodes\n const capped = haystack.length > 500 ? haystack.slice(0, 500) : haystack;\n const limit = capped.length - nLen;\n\n for (let i = 0; i <= limit; i++) {\n const window = capped.slice(i, i + nLen);\n const score = similarity(window, needle);\n if (score > best) best = score;\n if (best >= 0.95) break;\n }\n\n return best >= minScore ? best : 0;\n}\n","import type { AnchorData, RectData } from \"@siteping/core\";\nimport { scoreFingerprint } from \"./fingerprint.js\";\nimport { fuzzyIncludes, similarity } from \"./fuzzy.js\";\nimport { adjacentText, neighborText } from \"./text-context.js\";\n\nexport type ResolutionStrategy = \"id\" | \"css\" | \"xpath\" | \"scan\";\n\nexport interface AnchorResolution {\n element: Element;\n confidence: number;\n strategy: ResolutionStrategy;\n}\n\nexport interface ResolvedAnnotation {\n element: Element;\n rect: DOMRect;\n confidence: number;\n strategy: ResolutionStrategy;\n}\n\n/** Max elements to scan during smart fallback. */\nconst MAX_SCAN_CANDIDATES = 300;\n\n/** Minimum fuzzy text match score for CSS/XPath verification. */\nconst TEXT_MATCH_THRESHOLD = 0.3;\n\n/**\n * Verify that a resolved element's text content matches the stored snippet.\n * If no snippet is stored, returns true (no verification possible).\n * Uses fuzzy matching to tolerate minor text changes.\n */\nfunction textMatches(el: Element, anchor: AnchorData): boolean {\n if (!anchor.textSnippet) return true;\n const text = (el.textContent?.trim() ?? \"\").slice(0, 500);\n return fuzzyIncludes(text, anchor.textSnippet, 0.5) > TEXT_MATCH_THRESHOLD;\n}\n\n/**\n * Re-anchor an annotation using a multi-level fallback strategy\n * with text verification and confidence scoring.\n *\n * Resolution order:\n * 1. getElementById + text verification — confidence 1.0\n * 2. CSS selector + text verification — confidence 0.95\n * 3. XPath + text verification — confidence 0.9\n * 4. Smart scan (fingerprint + text + prefix/suffix + neighbor) — up to 0.85\n *\n * Text verification prevents false matches when DOM elements are reordered.\n * Returns null if all strategies fail (annotation is orphaned).\n */\nexport function resolveAnchor(anchor: AnchorData): AnchorResolution | null {\n // Level 1: Element ID (most stable, still verify text)\n if (anchor.elementId) {\n const el = document.getElementById(anchor.elementId);\n if (el && el.tagName === anchor.elementTag && textMatches(el, anchor)) {\n return { element: el, confidence: 1.0, strategy: \"id\" };\n }\n }\n\n // Level 2: CSS Selector (with text verification)\n try {\n const el = document.querySelector(anchor.cssSelector);\n if (el && el.tagName === anchor.elementTag && textMatches(el, anchor)) {\n return { element: el, confidence: 0.95, strategy: \"css\" };\n }\n } catch {\n // Invalid selector — skip\n }\n\n // Level 3: XPath (with text verification)\n try {\n const result = document.evaluate(anchor.xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);\n const el = result.singleNodeValue;\n if (el instanceof Element && el.tagName === anchor.elementTag && textMatches(el, anchor)) {\n return { element: el, confidence: 0.9, strategy: \"xpath\" };\n }\n } catch {\n // Invalid XPath — skip\n }\n\n // Level 4: Smart scan — combine all available signals\n return smartScan(anchor);\n}\n\n/**\n * Scan DOM elements by tag and score each candidate using multiple signals:\n * fingerprint, text similarity, prefix/suffix context, neighbor text.\n *\n * Returns the best candidate above a 0.4 threshold, capped at 0.85 confidence\n * (smart scan is never 100% certain).\n */\nfunction smartScan(anchor: AnchorData): AnchorResolution | null {\n const tag = anchor.elementTag.toLowerCase();\n const candidates = document.querySelectorAll(tag);\n if (candidates.length === 0) return null;\n\n let bestElement: Element | null = null;\n let bestScore = 0;\n\n const limit = Math.min(candidates.length, MAX_SCAN_CANDIDATES);\n\n for (let i = 0; i < limit; i++) {\n const el = candidates[i];\n if (!el) continue;\n const score = scoreCandidate(el, anchor);\n if (score > bestScore) {\n bestScore = score;\n bestElement = el;\n if (bestScore >= 0.85) break;\n }\n }\n\n if (!bestElement || bestScore < 0.4) return null;\n\n return {\n element: bestElement,\n confidence: Math.min(bestScore, 0.85),\n strategy: \"scan\",\n };\n}\n\n/**\n * Score a candidate element against all stored anchor signals.\n *\n * Dynamic weighting — only active signals contribute, then normalized:\n * - Text snippet (fuzzy substring match): weight 40 (most reliable for reordering)\n * - Fingerprint (structural match): weight 20\n * - Prefix/suffix context: weight 20\n * - Neighbor text: weight 20\n */\nfunction scoreCandidate(candidate: Element, anchor: AnchorData): number {\n let score = 0;\n let totalWeight = 0;\n\n // Truncate to avoid O(n*m) explosion on huge text nodes\n const candidateText = (candidate.textContent?.trim() ?? \"\").slice(0, 500);\n\n // --- Text snippet (weight 40 — most important for reordered elements) ---\n if (anchor.textSnippet) {\n totalWeight += 40;\n score += fuzzyIncludes(candidateText, anchor.textSnippet, 0.5) * 40;\n }\n\n // --- Fingerprint (weight 20) ---\n if (anchor.fingerprint) {\n totalWeight += 20;\n score += scoreFingerprint(candidate, anchor.fingerprint) * 20;\n }\n\n // --- Prefix/suffix context (weight 20) ---\n if (anchor.textPrefix || anchor.textSuffix) {\n totalWeight += 20;\n let contextScore = 0;\n let contextParts = 0;\n\n if (anchor.textPrefix) {\n const prevText = adjacentText(candidate, \"before\");\n contextScore += prevText ? similarity(prevText, anchor.textPrefix) : 0;\n contextParts++;\n }\n\n if (anchor.textSuffix) {\n const nextText = adjacentText(candidate, \"after\");\n contextScore += nextText ? similarity(nextText, anchor.textSuffix) : 0;\n contextParts++;\n }\n\n if (contextParts > 0) {\n score += (contextScore / contextParts) * 20;\n }\n }\n\n // --- Neighbor text (weight 20) ---\n if (anchor.neighborText) {\n totalWeight += 20;\n const candidateNeighbor = neighborText(candidate);\n score += candidateNeighbor ? similarity(candidateNeighbor, anchor.neighborText) * 20 : 0;\n }\n\n return totalWeight > 0 ? score / totalWeight : 0;\n}\n\n/**\n * Resolve an annotation's position on the page.\n * Converts stored percentage-based rect back to absolute coordinates\n * using the current bounding box of the resolved anchor element.\n */\nexport function resolveAnnotation(anchor: AnchorData, rect: RectData): ResolvedAnnotation | null {\n const resolution = resolveAnchor(anchor);\n\n if (!resolution) return null;\n\n const bounds = resolution.element.getBoundingClientRect();\n const absoluteRect = new DOMRect(\n bounds.x + rect.xPct * bounds.width,\n bounds.y + rect.yPct * bounds.height,\n rect.wPct * bounds.width,\n rect.hPct * bounds.height,\n );\n\n return {\n element: resolution.element,\n rect: absoluteRect,\n confidence: resolution.confidence,\n strategy: resolution.strategy,\n };\n}\n","import type { AnchorData, FeedbackResponse, RectData } from \"@siteping/core\";\nimport { resolveAnnotation } from \"./dom/resolver.js\";\nimport { el, setText } from \"./dom-utils.js\";\nimport type { EventBus, WidgetEvents } from \"./events.js\";\nimport { getTypeLabel, type TFunction } from \"./i18n/index.js\";\nimport { getTypeColor, type ThemeColors } from \"./styles/theme.js\";\nimport type { Tooltip } from \"./tooltip.js\";\n\ntype Annotation = FeedbackResponse[\"annotations\"][number];\n\nfunction toAnchorData(a: Annotation): AnchorData {\n return {\n cssSelector: a.cssSelector,\n xpath: a.xpath,\n textSnippet: a.textSnippet,\n elementTag: a.elementTag,\n elementId: a.elementId ?? undefined,\n textPrefix: a.textPrefix,\n textSuffix: a.textSuffix,\n fingerprint: a.fingerprint,\n neighborText: a.neighborText,\n };\n}\n\nfunction toRectData(a: Annotation): RectData {\n return { xPct: a.xPct, yPct: a.yPct, wPct: a.wPct, hPct: a.hPct };\n}\n\n/** Half of the 26px marker diameter — used for centering on anchor corner. */\nconst MARKER_OFFSET = 13;\n\n/** Convert a resolved rect to document-absolute marker position. */\nfunction markerPosition(rect: DOMRect): { top: number; left: number } {\n return {\n top: rect.top + window.scrollY - MARKER_OFFSET,\n left: rect.right + window.scrollX - MARKER_OFFSET,\n };\n}\n\ninterface MarkerEntry {\n feedback: FeedbackResponse;\n elements: HTMLElement[];\n baseTop: number;\n baseLeft: number;\n}\n\ninterface Cluster {\n entries: MarkerEntry[];\n elementIndices: number[];\n expanded: boolean;\n}\n\n/** Get the i-th marker element from a cluster. */\nfunction clusterMarker(cluster: Cluster, i: number): HTMLElement | undefined {\n const entry = cluster.entries[i];\n const elIdx = cluster.elementIndices[i];\n if (!entry || elIdx === undefined) return undefined;\n return entry.elements[elIdx];\n}\n\nconst HIGHLIGHT_FADE = 300;\nconst REPOSITION_DEBOUNCE = 200;\nconst LOW_CONFIDENCE_THRESHOLD = 0.7;\nconst CLUSTER_DISTANCE = 28;\nconst FAN_SPACING = 32;\n\n/**\n * Numbered markers on the page for each feedback annotation.\n *\n * Cluster system: click-to-expand (same pattern as Google Maps / Spiderfier).\n * Hover is only used for tooltip/scale on individual markers — never for expansion.\n */\nexport class MarkerManager {\n private container: HTMLElement;\n private entries: MarkerEntry[] = [];\n private highlightElements: HTMLElement[] = [];\n private pinnedFeedback: FeedbackResponse | null = null;\n private onDocumentClick: ((e: MouseEvent) => void) | null = null;\n private repositionTimer: number | null = null;\n private mutationObserver: MutationObserver | null = null;\n private scrollHandler: (() => void) | null = null;\n private resizeHandler: (() => void) | null = null;\n private anchorCache = new Map<string, WeakRef<Element>>();\n private clusters: Cluster[] = [];\n private onDocumentClickForClusters: ((e: MouseEvent) => void) | null = null;\n\n get count(): number {\n return this.entries.length;\n }\n\n constructor(\n private readonly colors: ThemeColors,\n private readonly tooltip: Tooltip,\n private readonly bus: EventBus<WidgetEvents>,\n private readonly t: TFunction,\n ) {\n this.container = el(\"div\", {\n style: \"position:absolute;top:0;left:0;pointer-events:none;z-index:2147483646;\",\n });\n this.container.id = \"siteping-markers\";\n document.body.appendChild(this.container);\n\n this.bus.on(\"annotations:toggle\", (visible) => {\n this.container.style.display = visible ? \"block\" : \"none\";\n });\n\n this.resizeHandler = () => this.scheduleReposition();\n window.addEventListener(\"resize\", this.resizeHandler, { passive: true });\n\n this.scrollHandler = () => this.scheduleReposition();\n window.addEventListener(\"scroll\", this.scrollHandler, { passive: true, capture: true });\n\n // Re-resolve after DOM changes (SPA, lazy-load).\n // Filter out widget-owned mutations and skip batches with only irrelevant changes.\n // Fast-path: large batches (>20 mutations) skip per-record filtering.\n this.mutationObserver = new MutationObserver((mutations) => {\n if (mutations.length > 20) {\n this.scheduleReposition();\n return;\n }\n let hasRelevantMutation = false;\n for (const m of mutations) {\n if (this.container.contains(m.target) || this.tooltip.contains(m.target)) continue;\n hasRelevantMutation = true;\n break;\n }\n if (hasRelevantMutation) this.scheduleReposition();\n });\n this.mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: false,\n characterData: false,\n });\n\n this.onDocumentClickForClusters = (e: MouseEvent) => {\n if (this.container.contains(e.target as Node)) return;\n this.collapseAllClusters();\n };\n document.addEventListener(\"click\", this.onDocumentClickForClusters);\n }\n\n private scheduleReposition(): void {\n if (this.repositionTimer) return;\n if (\"requestIdleCallback\" in window) {\n this.repositionTimer = window.requestIdleCallback(\n () => {\n this.repositionTimer = null;\n this.repositionAll();\n },\n { timeout: REPOSITION_DEBOUNCE + 100 },\n );\n } else {\n this.repositionTimer = +setTimeout(() => {\n this.repositionTimer = null;\n this.repositionAll();\n }, REPOSITION_DEBOUNCE);\n }\n }\n\n private repositionAll(): void {\n for (const entry of this.entries) {\n for (let i = 0; i < entry.feedback.annotations.length; i++) {\n const markerEl = entry.elements[i];\n if (!markerEl) continue;\n\n const annotation = entry.feedback.annotations[i];\n if (!annotation) continue;\n const cacheKey = `${entry.feedback.id}:${i}`;\n\n // Try cached element first to avoid full resolution chain.\n const cachedRef = this.anchorCache.get(cacheKey);\n const cachedEl = cachedRef?.deref();\n let resolved: ReturnType<typeof resolveAnnotation>;\n\n if (cachedEl?.isConnected) {\n const anchorRect = cachedEl.getBoundingClientRect();\n const r = toRectData(annotation);\n resolved = {\n element: cachedEl,\n rect: new DOMRect(\n anchorRect.left + r.xPct * anchorRect.width,\n anchorRect.top + r.yPct * anchorRect.height,\n r.wPct * anchorRect.width,\n r.hPct * anchorRect.height,\n ),\n confidence: 1,\n strategy: \"css\",\n };\n } else {\n resolved = resolveAnnotation(toAnchorData(annotation), toRectData(annotation));\n if (resolved?.element) {\n this.anchorCache.set(cacheKey, new WeakRef(resolved.element));\n }\n }\n\n if (!resolved) {\n markerEl.style.display = \"none\";\n continue;\n }\n\n const pos = markerPosition(resolved.rect);\n entry.baseTop = pos.top;\n entry.baseLeft = pos.left;\n markerEl.style.display = \"flex\";\n this.applyConfidenceStyle(markerEl, resolved.confidence, entry.feedback);\n }\n }\n this.applyClusterPositions();\n }\n\n private applyClusterPositions(): void {\n for (const cluster of this.clusters) {\n if (cluster.expanded) {\n this.applyFanPositions(cluster);\n } else {\n this.applyStackPositions(cluster);\n }\n }\n }\n\n render(feedbacks: FeedbackResponse[]): void {\n this.clear();\n feedbacks.forEach((feedback, i) => {\n const entry = this.buildEntry(feedback, i + 1);\n this.entries.push(entry);\n });\n this.buildClusters();\n }\n\n addFeedback(feedback: FeedbackResponse, index: number): void {\n const entry = this.buildEntry(feedback, index);\n for (const m of entry.elements) {\n m.style.animation = \"sp-marker-in 0.35s cubic-bezier(0.34,1.56,0.64,1) both\";\n }\n this.entries.push(entry);\n this.buildClusters();\n }\n\n private buildEntry(feedback: FeedbackResponse, index: number): MarkerEntry {\n const entry: MarkerEntry = { feedback, elements: [], baseTop: 0, baseLeft: 0 };\n for (const annotation of feedback.annotations) {\n const resolved = resolveAnnotation(toAnchorData(annotation), toRectData(annotation));\n if (!resolved) continue;\n const pos = markerPosition(resolved.rect);\n entry.baseTop = pos.top;\n entry.baseLeft = pos.left;\n const marker = this.createMarker(index, feedback, pos);\n this.applyConfidenceStyle(marker, resolved.confidence, feedback);\n this.container.appendChild(marker);\n entry.elements.push(marker);\n }\n return entry;\n }\n\n private buildClusters(): void {\n for (const badge of this.container.querySelectorAll<HTMLElement>(\".sp-cluster-badge\")) {\n badge.remove();\n }\n\n const allItems: { entry: MarkerEntry; elIdx: number }[] = [];\n for (const entry of this.entries) {\n for (let i = 0; i < entry.elements.length; i++) {\n allItems.push({ entry, elIdx: i });\n }\n }\n\n const used = new Set<number>();\n this.clusters = [];\n\n for (let i = 0; i < allItems.length; i++) {\n if (used.has(i)) continue;\n const itemI = allItems[i];\n if (!itemI) continue;\n const cluster: Cluster = {\n entries: [itemI.entry],\n elementIndices: [itemI.elIdx],\n expanded: false,\n };\n used.add(i);\n\n for (let j = i + 1; j < allItems.length; j++) {\n if (used.has(j)) continue;\n const a = itemI.entry;\n const itemJ = allItems[j];\n if (!itemJ) continue;\n const b = itemJ.entry;\n const dist = Math.sqrt((a.baseLeft - b.baseLeft) ** 2 + (a.baseTop - b.baseTop) ** 2);\n if (dist < CLUSTER_DISTANCE) {\n cluster.entries.push(b);\n cluster.elementIndices.push(itemJ.elIdx);\n used.add(j);\n }\n }\n\n this.clusters.push(cluster);\n }\n\n for (const cluster of this.clusters) {\n if (cluster.entries.length <= 1) continue;\n this.applyStackPositions(cluster);\n this.addClusterBadge(cluster);\n }\n }\n\n private applyStackPositions(cluster: Cluster): void {\n const first = cluster.entries[0];\n if (!first) return;\n const { baseTop, baseLeft } = first;\n const isSolo = cluster.entries.length <= 1;\n for (let i = 0; i < cluster.entries.length; i++) {\n const m = clusterMarker(cluster, i);\n if (!m) continue;\n m.style.top = `${baseTop + (isSolo ? 0 : i * 3)}px`;\n m.style.left = `${baseLeft + (isSolo ? 0 : i * 3)}px`;\n m.style.zIndex = String(i + 1);\n }\n }\n\n private applyFanPositions(cluster: Cluster): void {\n const first = cluster.entries[0];\n if (!first) return;\n const { baseTop, baseLeft } = first;\n const count = cluster.entries.length;\n const totalWidth = (count - 1) * FAN_SPACING;\n const startLeft = baseLeft - totalWidth / 2;\n\n for (let i = 0; i < count; i++) {\n const m = clusterMarker(cluster, i);\n if (!m) continue;\n m.style.top = `${baseTop}px`;\n m.style.left = `${startLeft + i * FAN_SPACING}px`;\n m.style.zIndex = String(10 + i);\n }\n }\n\n private addClusterBadge(cluster: Cluster): void {\n const topMarker = clusterMarker(cluster, cluster.entries.length - 1);\n if (!topMarker) return;\n const badge = el(\"div\", {\n class: \"sp-cluster-badge\",\n style: `\n position:absolute;top:-6px;right:-6px;\n min-width:16px;height:16px;padding:0 4px;\n border-radius:9999px;\n background:${this.colors.accent};color:#fff;\n font-size:10px;font-weight:700;\n display:flex;align-items:center;justify-content:center;\n border:1.5px solid #fff;\n pointer-events:none;\n font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n line-height:1;\n `,\n });\n setText(badge, String(cluster.entries.length));\n topMarker.appendChild(badge);\n }\n\n private setBadgesVisible(cluster: Cluster, visible: boolean): void {\n for (let i = 0; i < cluster.entries.length; i++) {\n const badge = clusterMarker(cluster, i)?.querySelector(\".sp-cluster-badge\") as HTMLElement | null;\n if (badge) badge.style.display = visible ? \"flex\" : \"none\";\n }\n }\n\n private findCluster(marker: HTMLElement): Cluster | null {\n for (const cluster of this.clusters) {\n if (cluster.entries.length <= 1) continue;\n for (let i = 0; i < cluster.entries.length; i++) {\n if (clusterMarker(cluster, i) === marker) return cluster;\n }\n }\n return null;\n }\n\n private handleClusterClick(marker: HTMLElement, e: MouseEvent): boolean {\n const cluster = this.findCluster(marker);\n if (!cluster) return false;\n if (!cluster.expanded) {\n e.stopPropagation();\n this.collapseAllClusters();\n cluster.expanded = true;\n this.applyFanPositions(cluster);\n this.setBadgesVisible(cluster, false);\n return true;\n }\n return false;\n }\n\n private collapseCluster(cluster: Cluster): void {\n if (!cluster.expanded) return;\n cluster.expanded = false;\n this.applyStackPositions(cluster);\n this.setBadgesVisible(cluster, true);\n }\n\n private collapseAllClusters(): void {\n for (const cluster of this.clusters) {\n this.collapseCluster(cluster);\n }\n }\n\n private applyConfidenceStyle(marker: HTMLElement, confidence: number, feedback: FeedbackResponse): void {\n const isResolved = feedback.status === \"resolved\";\n if (confidence < LOW_CONFIDENCE_THRESHOLD && !isResolved) {\n marker.style.borderStyle = \"dashed\";\n marker.style.opacity = \"0.7\";\n marker.title = this.t(\"marker.approximate\").replace(\"{confidence}\", String(Math.round(confidence * 100)));\n } else {\n marker.style.borderStyle = \"solid\";\n marker.style.opacity = \"1\";\n marker.title = \"\";\n }\n }\n\n private createMarker(number: number, feedback: FeedbackResponse, pos: { top: number; left: number }): HTMLElement {\n const typeColor = getTypeColor(feedback.type, this.colors);\n const isResolved = feedback.status === \"resolved\";\n\n const marker = el(\"div\", {\n style: `\n position:absolute;\n top:${pos.top}px;\n left:${pos.left}px;\n width:26px;height:26px;\n border-radius:50%;\n background:${isResolved ? \"rgba(241,245,249,0.9)\" : \"rgba(255,255,255,0.92)\"};\n border:2px solid ${isResolved ? \"#cbd5e1\" : typeColor};\n display:flex;align-items:center;justify-content:center;\n font-family:\"Inter\",system-ui,-apple-system,sans-serif;\n font-size:11px;font-weight:700;\n color:${isResolved ? \"#94a3b8\" : typeColor};\n cursor:pointer;pointer-events:auto;\n box-shadow:${isResolved ? \"0 2px 8px rgba(0,0,0,0.06)\" : `0 2px 12px ${typeColor}25, 0 2px 6px rgba(0,0,0,0.06)`};\n transition:top 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), left 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.15s ease, box-shadow 0.15s ease;\n user-select:none;\n -webkit-font-smoothing:antialiased;\n `,\n });\n marker.dataset.feedbackId = feedback.id;\n marker.setAttribute(\"tabindex\", \"0\");\n marker.setAttribute(\"role\", \"button\");\n const truncatedMessage = feedback.message.length > 60 ? `${feedback.message.slice(0, 60)}...` : feedback.message;\n const ariaLabel = this.t(\"marker.aria\")\n .replace(\"{number}\", String(number))\n .replace(\"{type}\", getTypeLabel(feedback.type, this.t))\n .replace(\"{message}\", truncatedMessage);\n marker.setAttribute(\"aria-label\", ariaLabel);\n marker.setAttribute(\"aria-describedby\", this.tooltip.tooltipId);\n setText(marker, isResolved ? \"\\u2713\" : String(number));\n\n marker.addEventListener(\"mouseenter\", () => {\n marker.style.transform = \"scale(1.2)\";\n marker.style.boxShadow = isResolved\n ? \"0 4px 16px rgba(0,0,0,0.1)\"\n : `0 4px 20px ${typeColor}35, 0 4px 12px rgba(0,0,0,0.08)`;\n this.tooltip.show(feedback, marker.getBoundingClientRect());\n if (!this.pinnedFeedback) this.showHighlight(feedback);\n });\n\n marker.addEventListener(\"mouseleave\", () => {\n marker.style.transform = \"scale(1)\";\n marker.style.boxShadow = isResolved\n ? \"0 2px 8px rgba(0,0,0,0.06)\"\n : `0 2px 12px ${typeColor}25, 0 2px 6px rgba(0,0,0,0.06)`;\n this.tooltip.scheduleHide();\n if (!this.pinnedFeedback) this.clearHighlight();\n });\n\n const activateMarker = (e: MouseEvent | KeyboardEvent) => {\n if (e instanceof MouseEvent && this.handleClusterClick(marker, e)) return;\n this.pinHighlight(feedback);\n this.bus.emit(\"panel:toggle\", true);\n marker.dispatchEvent(\n new CustomEvent(\"sp-marker-click\", {\n detail: { feedbackId: feedback.id },\n bubbles: true,\n }),\n );\n };\n\n marker.addEventListener(\"click\", (e) => activateMarker(e));\n marker.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n activateMarker(e);\n }\n });\n\n return marker;\n }\n\n highlight(feedbackId: string): void {\n for (const entry of this.entries) {\n if (entry.feedback.id === feedbackId) {\n for (const markerEl of entry.elements) {\n markerEl.style.animation = \"sp-pulse-ring 0.7s ease-out\";\n markerEl.addEventListener(\n \"animationend\",\n () => {\n markerEl.style.animation = \"\";\n },\n { once: true },\n );\n }\n }\n }\n }\n\n showHighlight(feedback: FeedbackResponse): void {\n this.removeHighlightElements();\n for (const annotation of feedback.annotations) {\n const resolved = resolveAnnotation(toAnchorData(annotation), toRectData(annotation));\n if (!resolved) continue;\n\n const typeColor = getTypeColor(feedback.type, this.colors);\n const rect = resolved.rect;\n const highlight = el(\"div\", {\n style: `\n position:absolute;\n top:${rect.top + window.scrollY}px;\n left:${rect.left + window.scrollX}px;\n width:${rect.width}px;height:${rect.height}px;\n border:2px solid ${typeColor};\n background:${typeColor}0c;\n border-radius:8px;\n pointer-events:none;z-index:-1;\n opacity:0;\n box-shadow:0 0 16px ${typeColor}20;\n transition:opacity ${HIGHLIGHT_FADE}ms ease;\n `,\n });\n this.container.appendChild(highlight);\n this.highlightElements.push(highlight);\n void highlight.offsetHeight; // Force reflow for CSS transition\n highlight.style.opacity = \"1\";\n }\n }\n\n pinHighlight(feedback: FeedbackResponse): void {\n this.unpinHighlight();\n this.showHighlight(feedback);\n this.pinnedFeedback = feedback;\n this.onDocumentClick = (e: MouseEvent) => {\n if (this.container.contains(e.target as Node)) return;\n this.unpinHighlight();\n };\n document.addEventListener(\"click\", this.onDocumentClick, { capture: true });\n }\n\n private unpinHighlight(): void {\n if (this.onDocumentClick) {\n document.removeEventListener(\"click\", this.onDocumentClick, { capture: true });\n this.onDocumentClick = null;\n }\n this.pinnedFeedback = null;\n this.clearHighlight();\n }\n\n private clearHighlight(): void {\n for (const h of this.highlightElements) {\n h.style.opacity = \"0\";\n setTimeout(() => h.remove(), HIGHLIGHT_FADE);\n }\n this.highlightElements = [];\n }\n\n private removeHighlightElements(): void {\n for (const h of this.highlightElements) h.remove();\n this.highlightElements = [];\n }\n\n clear(): void {\n this.unpinHighlight();\n this.container.replaceChildren();\n this.entries = [];\n this.clusters = [];\n this.anchorCache.clear();\n }\n\n destroy(): void {\n this.unpinHighlight();\n if (this.repositionTimer) {\n if (\"cancelIdleCallback\" in window) {\n window.cancelIdleCallback(this.repositionTimer);\n }\n clearTimeout(this.repositionTimer);\n }\n if (this.resizeHandler) window.removeEventListener(\"resize\", this.resizeHandler);\n if (this.scrollHandler) window.removeEventListener(\"scroll\", this.scrollHandler, { capture: true });\n if (this.onDocumentClickForClusters) document.removeEventListener(\"click\", this.onDocumentClickForClusters);\n this.mutationObserver?.disconnect();\n this.container.remove();\n }\n}\n","import type { FeedbackResponse, FeedbackType } from \"@siteping/core\";\nimport type { WidgetClient } from \"./api-client.js\";\nimport { el, formatRelativeDate, parseSvg, setText } from \"./dom-utils.js\";\nimport type { EventBus, WidgetEvents } from \"./events.js\";\nimport { getTypeLabel, type TFunction } from \"./i18n/index.js\";\nimport { ICON_CHECK, ICON_CLOSE, ICON_SEARCH, ICON_TRASH, ICON_UNDO } from \"./icons.js\";\nimport type { MarkerManager } from \"./markers.js\";\nimport { getTypeBgColor, getTypeColor, type ThemeColors } from \"./styles/theme.js\";\n\n/**\n * Side panel (400px) with feedback history, filters, and search.\n *\n * Lives inside the Shadow DOM.\n * Glassmorphism: glass background, staggered card animations,\n * loading states, resolve feedback with disabled state.\n */\nexport class Panel {\n private root: HTMLElement;\n private listContainer: HTMLElement;\n private searchInput: HTMLInputElement;\n private closeBtn: HTMLButtonElement;\n private deleteAllBtn: HTMLButtonElement;\n private activeFilters = new Set<string>([\"all\"]);\n private feedbacks: FeedbackResponse[] = [];\n private isOpen = false;\n private searchTimeout: ReturnType<typeof setTimeout> | null = null;\n private loadController: AbortController | null = null;\n /** Tracks feedback IDs with in-flight mutations to prevent spam-click race conditions */\n private pendingMutations = new Set<string>();\n\n constructor(\n shadowRoot: ShadowRoot,\n private readonly colors: ThemeColors,\n private readonly bus: EventBus<WidgetEvents>,\n private readonly client: WidgetClient,\n private readonly projectName: string,\n private readonly markers: MarkerManager,\n private readonly t: TFunction,\n private readonly locale: string,\n ) {\n this.root = el(\"div\", { class: \"sp-panel\" });\n this.root.setAttribute(\"role\", \"complementary\");\n this.root.setAttribute(\"aria-label\", this.t(\"panel.ariaLabel\"));\n this.root.setAttribute(\"aria-hidden\", \"true\");\n\n // Header\n const header = el(\"div\", { class: \"sp-panel-header\" });\n const title = el(\"span\", { class: \"sp-panel-title\" });\n setText(title, this.t(\"panel.title\"));\n\n this.closeBtn = document.createElement(\"button\");\n this.closeBtn.className = \"sp-panel-close\";\n this.closeBtn.setAttribute(\"aria-label\", this.t(\"panel.close\"));\n this.closeBtn.appendChild(parseSvg(ICON_CLOSE));\n this.closeBtn.addEventListener(\"click\", () => this.close());\n\n this.deleteAllBtn = document.createElement(\"button\");\n this.deleteAllBtn.className = \"sp-btn-delete-all\";\n this.deleteAllBtn.setAttribute(\"aria-label\", this.t(\"panel.deleteAll\"));\n this.deleteAllBtn.appendChild(parseSvg(ICON_TRASH));\n const deleteAllLabel = document.createElement(\"span\");\n setText(deleteAllLabel, ` ${this.t(\"panel.deleteAll\")}`);\n this.deleteAllBtn.appendChild(deleteAllLabel);\n this.deleteAllBtn.addEventListener(\"click\", () => this.confirmDeleteAll());\n\n const headerRight = el(\"div\", { class: \"sp-panel-header-right\" });\n headerRight.appendChild(this.deleteAllBtn);\n headerRight.appendChild(this.closeBtn);\n\n header.appendChild(title);\n header.appendChild(headerRight);\n\n // Filters\n const filters = el(\"div\", { class: \"sp-filters\" });\n\n // Search\n const searchWrap = el(\"div\", { class: \"sp-search-wrap\" });\n const searchIcon = parseSvg(ICON_SEARCH);\n searchIcon.setAttribute(\"class\", \"sp-search-icon\");\n this.searchInput = document.createElement(\"input\");\n this.searchInput.type = \"text\";\n this.searchInput.className = \"sp-search\";\n this.searchInput.placeholder = this.t(\"panel.search\");\n this.searchInput.setAttribute(\"aria-label\", this.t(\"panel.searchAria\"));\n this.searchInput.addEventListener(\"input\", () => {\n if (this.searchTimeout) clearTimeout(this.searchTimeout);\n this.searchTimeout = setTimeout(() => this.loadFeedbacks().catch(() => {}), 200);\n });\n searchWrap.appendChild(searchIcon);\n searchWrap.appendChild(this.searchInput);\n\n // Chips\n const chips = el(\"div\", { class: \"sp-chips\" });\n const chipOptions = [\n { value: \"all\", label: this.t(\"panel.filterAll\") },\n { value: \"question\", label: this.t(\"type.question\") },\n { value: \"change\", label: this.t(\"type.change\") },\n { value: \"bug\", label: this.t(\"type.bug\") },\n { value: \"other\", label: this.t(\"type.other\") },\n ];\n\n for (const option of chipOptions) {\n const chip = document.createElement(\"button\");\n chip.className = `sp-chip ${option.value === \"all\" ? \"sp-chip--active\" : \"\"}`;\n if (option.value !== \"all\") {\n chip.style.borderColor = getTypeColor(option.value, this.colors);\n }\n setText(chip, option.label);\n chip.dataset.filter = option.value;\n chip.setAttribute(\"aria-pressed\", option.value === \"all\" ? \"true\" : \"false\");\n chip.addEventListener(\"click\", () => this.toggleFilter(option.value, chips));\n chips.appendChild(chip);\n }\n\n filters.appendChild(searchWrap);\n filters.appendChild(chips);\n\n // List\n this.listContainer = el(\"div\", { class: \"sp-list\" });\n this.listContainer.setAttribute(\"role\", \"list\");\n this.listContainer.setAttribute(\"aria-label\", this.t(\"panel.feedbackList\"));\n\n this.root.appendChild(header);\n this.root.appendChild(filters);\n this.root.appendChild(this.listContainer);\n shadowRoot.appendChild(this.root);\n\n // --- Event delegation on listContainer ---\n\n this.onListClick = (e: Event) => {\n const target = e.target as Element;\n\n // Action buttons (expand, resolve, delete)\n const actionEl = target.closest<HTMLElement>(\"[data-action]\");\n if (actionEl) {\n e.stopPropagation();\n const card = actionEl.closest<HTMLElement>(\".sp-card\");\n if (!card) return;\n const feedbackId = card.dataset.feedbackId;\n const feedback = this.feedbacks.find((f) => f.id === feedbackId);\n if (!feedback) return;\n\n const action = actionEl.dataset.action;\n if (action === \"expand\") {\n const message = card.querySelector<HTMLElement>(\".sp-card-message\");\n if (!message) return;\n const isExpanded = message.classList.toggle(\"sp-card-message--expanded\");\n setText(actionEl, isExpanded ? this.t(\"panel.showLess\") : this.t(\"panel.showMore\"));\n actionEl.setAttribute(\"aria-expanded\", String(isExpanded));\n } else if (action === \"resolve\") {\n if (this.pendingMutations.has(feedback.id)) return;\n const btn = actionEl as HTMLButtonElement;\n this.toggleResolve(feedback, btn).catch(() => {});\n } else if (action === \"delete\") {\n if (this.pendingMutations.has(feedback.id)) return;\n const btn = actionEl as HTMLButtonElement;\n this.deleteFeedback(feedback, btn).catch(() => {});\n }\n return;\n }\n\n // Card click (scroll to annotation)\n const card = target.closest<HTMLElement>(\".sp-card\");\n if (card) {\n const feedbackId = card.dataset.feedbackId;\n const feedback = this.feedbacks.find((f) => f.id === feedbackId);\n if (feedback && feedback.annotations.length > 0) {\n const ann = feedback.annotations[0];\n if (!ann) return;\n window.scrollTo({ left: ann.scrollX, top: ann.scrollY, behavior: \"smooth\" });\n this.markers.pinHighlight(feedback);\n }\n }\n };\n this.listContainer.addEventListener(\"click\", this.onListClick);\n\n this.onListKeydown = (e: Event) => {\n const ke = e as KeyboardEvent;\n if (ke.key !== \"Enter\" && ke.key !== \" \") return;\n const target = ke.target as Element;\n const card = target.closest<HTMLElement>(\".sp-card\");\n // Only activate if the card itself is focused, not a button inside it\n if (!card || target !== card) return;\n ke.preventDefault();\n const feedbackId = card.dataset.feedbackId;\n const feedback = this.feedbacks.find((f) => f.id === feedbackId);\n if (feedback && feedback.annotations.length > 0) {\n const ann = feedback.annotations[0];\n if (!ann) return;\n window.scrollTo({ left: ann.scrollX, top: ann.scrollY, behavior: \"smooth\" });\n this.markers.pinHighlight(feedback);\n }\n };\n this.listContainer.addEventListener(\"keydown\", this.onListKeydown);\n\n // mouseover/mouseout bubble (unlike mouseenter/mouseleave), enabling delegation\n this.onListMouseover = (e: Event) => {\n const target = (e as MouseEvent).target as Element;\n const card = target.closest<HTMLElement>(\".sp-card\");\n if (!card) return;\n const feedbackId = card.dataset.feedbackId;\n if (feedbackId) this.markers.highlight(feedbackId);\n };\n this.listContainer.addEventListener(\"mouseover\", this.onListMouseover);\n\n this.onListMouseout = (e: Event) => {\n const target = (e as MouseEvent).relatedTarget as Element | null;\n // Only clear highlight when leaving all cards (relatedTarget is outside listContainer)\n if (target && this.listContainer.contains(target)) return;\n this.markers.highlight(\"\");\n };\n this.listContainer.addEventListener(\"mouseout\", this.onListMouseout);\n\n // Events\n this.bus.on(\"panel:toggle\", (open) => {\n open ? this.open() : this.close();\n });\n\n // Keyboard handling: Escape to close + focus trap\n shadowRoot.addEventListener(\"keydown\", (e) => {\n const ke = e as KeyboardEvent;\n if (ke.key === \"Escape\" && this.isOpen) {\n this.close();\n return;\n }\n if (ke.key === \"Tab\" && this.isOpen) {\n const focusable = this.root.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusable.length === 0) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n const active = (shadowRoot as ShadowRoot).activeElement;\n if (ke.shiftKey && active === first) {\n ke.preventDefault();\n last.focus();\n } else if (!ke.shiftKey && active === last) {\n ke.preventDefault();\n first.focus();\n }\n }\n });\n\n // Listen for marker clicks\n this.onMarkerClick = ((e: CustomEvent) => {\n this.scrollToFeedback(e.detail.feedbackId);\n }) as EventListener;\n document.addEventListener(\"sp-marker-click\", this.onMarkerClick);\n }\n\n private onMarkerClick: EventListener;\n private onListClick: (e: Event) => void;\n private onListKeydown: (e: Event) => void;\n private onListMouseover: (e: Event) => void;\n private onListMouseout: (e: Event) => void;\n\n async open(): Promise<void> {\n if (this.isOpen) return;\n this.isOpen = true;\n this.root.classList.add(\"sp-panel--open\");\n this.root.setAttribute(\"aria-hidden\", \"false\");\n this.bus.emit(\"open\");\n await this.loadFeedbacks();\n // Move focus into the panel (search input or close button)\n requestAnimationFrame(() => {\n if (this.searchInput) {\n this.searchInput.focus();\n } else {\n this.closeBtn.focus();\n }\n });\n }\n\n close(): void {\n if (!this.isOpen) return;\n this.isOpen = false;\n this.root.classList.remove(\"sp-panel--open\");\n this.root.setAttribute(\"aria-hidden\", \"true\");\n this.bus.emit(\"close\");\n // Restore focus to the FAB\n const fab = (this.root.getRootNode() as ShadowRoot).querySelector<HTMLButtonElement>(\".sp-fab\");\n fab?.focus();\n }\n\n private showLoading(): void {\n this.listContainer.replaceChildren();\n const loading = el(\"div\", { class: \"sp-loading\" });\n loading.setAttribute(\"role\", \"status\");\n loading.setAttribute(\"aria-live\", \"polite\");\n loading.setAttribute(\"aria-label\", this.t(\"panel.loading\"));\n const spinner = el(\"div\", { class: \"sp-spinner\" });\n loading.appendChild(spinner);\n this.listContainer.appendChild(loading);\n }\n\n private showError(): void {\n this.listContainer.replaceChildren();\n const empty = el(\"div\", { class: \"sp-empty\" });\n empty.setAttribute(\"role\", \"status\");\n empty.setAttribute(\"aria-live\", \"polite\");\n const text = el(\"div\", { class: \"sp-empty-text\" });\n setText(text, this.t(\"panel.loadError\"));\n const retryBtn = document.createElement(\"button\");\n retryBtn.className = \"sp-btn-ghost\";\n retryBtn.style.marginTop = \"8px\";\n setText(retryBtn, this.t(\"panel.retry\"));\n retryBtn.addEventListener(\"click\", () => this.loadFeedbacks().catch(() => {}));\n empty.appendChild(text);\n empty.appendChild(retryBtn);\n this.listContainer.appendChild(empty);\n }\n\n private async loadFeedbacks(): Promise<void> {\n // Cancel any in-flight request to prevent stale responses from overwriting newer results\n this.loadController?.abort();\n this.loadController = new AbortController();\n const { signal } = this.loadController;\n\n const search = this.searchInput.value.trim() || undefined;\n const typeFilter = this.activeFilters.has(\"all\") ? undefined : (Array.from(this.activeFilters)[0] as FeedbackType);\n\n const options: { limit: number; type?: FeedbackType; search?: string } = { limit: 50 };\n if (typeFilter) options.type = typeFilter;\n if (search) options.search = search;\n\n // Only show spinner on first load (empty list) — otherwise keep current content visible\n const hasContent = this.feedbacks.length > 0;\n if (!hasContent) this.showLoading();\n\n try {\n const { feedbacks } = await this.client.getFeedbacks(this.projectName, options);\n if (signal.aborted) return; // Stale response — a newer request superseded this one\n this.feedbacks = feedbacks;\n this.renderList();\n this.markers.render(feedbacks);\n } catch (error) {\n if (signal.aborted) return; // Expected abort, not a real error\n if (!hasContent) this.showError();\n this.bus.emit(\"feedback:error\", error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n private renderList(): void {\n this.listContainer.replaceChildren();\n\n if (this.feedbacks.length === 0) {\n const empty = el(\"div\", { class: \"sp-empty\" });\n empty.setAttribute(\"role\", \"status\");\n empty.setAttribute(\"aria-live\", \"polite\");\n const emptyText = el(\"div\", { class: \"sp-empty-text\" });\n setText(emptyText, this.t(\"panel.empty\"));\n empty.appendChild(emptyText);\n this.listContainer.appendChild(empty);\n return;\n }\n\n this.feedbacks.forEach((feedback, index) => {\n const card = this.createCard(feedback, index + 1);\n // Stagger animation via CSS custom property\n card.style.setProperty(\"--sp-card-i\", String(index));\n this.listContainer.appendChild(card);\n });\n }\n\n private createCard(feedback: FeedbackResponse, number: number): HTMLElement {\n const isResolved = feedback.status === \"resolved\";\n const typeColor = getTypeColor(feedback.type, this.colors);\n\n const card = el(\"div\", {\n class: `sp-card ${isResolved ? \"sp-card--resolved\" : \"\"}`,\n });\n card.setAttribute(\"role\", \"listitem\");\n card.setAttribute(\"tabindex\", \"0\");\n card.setAttribute(\n \"aria-label\",\n `Feedback #${number}: ${getTypeLabel(feedback.type, this.t)} — ${feedback.message.slice(0, 80)}`,\n );\n card.dataset.feedbackId = feedback.id;\n\n // Color bar\n const bar = el(\"div\", { class: \"sp-card-bar\" });\n bar.style.background = isResolved ? \"#9ca3af\" : typeColor;\n\n // Body\n const body = el(\"div\", { class: \"sp-card-body\" });\n\n // Header: #number + badge + date\n const header = el(\"div\", { class: \"sp-card-header\" });\n\n const num = el(\"span\", { class: \"sp-card-number\" });\n setText(num, `#${number}`);\n\n const badge = el(\"span\", { class: \"sp-badge\" });\n const typeBg = getTypeBgColor(feedback.type, this.colors);\n badge.style.background = typeBg;\n badge.style.color = typeColor;\n setText(badge, getTypeLabel(feedback.type, this.t));\n\n const date = el(\"span\", { class: \"sp-card-date\" });\n setText(date, formatRelativeDate(feedback.createdAt, this.locale));\n\n header.appendChild(num);\n header.appendChild(badge);\n header.appendChild(date);\n\n // Message\n const message = el(\"div\", { class: \"sp-card-message\" });\n setText(message, feedback.message);\n\n // Expand button\n const expandBtn = document.createElement(\"button\");\n expandBtn.className = \"sp-card-expand\";\n expandBtn.dataset.action = \"expand\";\n setText(expandBtn, this.t(\"panel.showMore\"));\n expandBtn.style.display = \"none\";\n expandBtn.setAttribute(\"aria-expanded\", \"false\");\n\n // Check if text is clamped (after render)\n requestAnimationFrame(() => {\n if (message.scrollHeight > message.clientHeight) {\n expandBtn.style.display = \"block\";\n }\n });\n\n // Footer: resolve button\n const footer = el(\"div\", { class: \"sp-card-footer\" });\n\n const resolveBtn = document.createElement(\"button\");\n resolveBtn.className = \"sp-btn-resolve\";\n resolveBtn.dataset.action = \"resolve\";\n if (isResolved) {\n resolveBtn.appendChild(parseSvg(ICON_UNDO));\n const span = document.createElement(\"span\");\n setText(span, ` ${this.t(\"panel.reopen\")}`);\n resolveBtn.appendChild(span);\n } else {\n resolveBtn.appendChild(parseSvg(ICON_CHECK));\n const span = document.createElement(\"span\");\n setText(span, ` ${this.t(\"panel.resolve\")}`);\n resolveBtn.appendChild(span);\n }\n\n const deleteBtn = document.createElement(\"button\");\n deleteBtn.className = \"sp-btn-delete\";\n deleteBtn.dataset.action = \"delete\";\n deleteBtn.appendChild(parseSvg(ICON_TRASH));\n const deleteLabel = document.createElement(\"span\");\n setText(deleteLabel, ` ${this.t(\"panel.delete\")}`);\n deleteBtn.appendChild(deleteLabel);\n\n footer.appendChild(resolveBtn);\n footer.appendChild(deleteBtn);\n\n body.appendChild(header);\n body.appendChild(message);\n body.appendChild(expandBtn);\n body.appendChild(footer);\n\n card.appendChild(bar);\n card.appendChild(body);\n\n return card;\n }\n\n private async deleteFeedback(feedback: FeedbackResponse, btn: HTMLButtonElement): Promise<void> {\n this.pendingMutations.add(feedback.id);\n btn.disabled = true;\n try {\n await this.client.deleteFeedback(feedback.id);\n this.bus.emit(\"feedback:deleted\", feedback.id);\n await this.loadFeedbacks();\n } catch (error) {\n this.pendingMutations.delete(feedback.id);\n btn.disabled = false;\n this.bus.emit(\"feedback:error\", error instanceof Error ? error : new Error(String(error)));\n } finally {\n if (this.pendingMutations.has(feedback.id)) {\n setTimeout(() => this.pendingMutations.delete(feedback.id), 300);\n }\n }\n }\n\n private async confirmDeleteAll(): Promise<void> {\n const confirmed = await this.showConfirmDialog(\n this.t(\"panel.deleteAllConfirmTitle\"),\n this.t(\"panel.deleteAllConfirmMessage\"),\n );\n if (!confirmed) return;\n\n this.deleteAllBtn.disabled = true;\n try {\n await this.client.deleteAllFeedbacks(this.projectName);\n this.bus.emit(\"feedback:all-deleted\");\n await this.loadFeedbacks();\n } catch (error) {\n this.bus.emit(\"feedback:error\", error instanceof Error ? error : new Error(String(error)));\n } finally {\n this.deleteAllBtn.disabled = false;\n }\n }\n\n private showConfirmDialog(title: string, message: string): Promise<boolean> {\n return new Promise((resolve) => {\n const backdrop = el(\"div\", { class: \"sp-confirm-backdrop\" });\n\n const titleId = `sp-confirm-title-${Date.now()}`;\n const messageId = `sp-confirm-msg-${Date.now()}`;\n\n const dialog = el(\"div\", { class: \"sp-confirm-dialog\" });\n dialog.setAttribute(\"role\", \"alertdialog\");\n dialog.setAttribute(\"aria-modal\", \"true\");\n dialog.setAttribute(\"aria-labelledby\", titleId);\n dialog.setAttribute(\"aria-describedby\", messageId);\n\n const titleEl = el(\"div\", { class: \"sp-confirm-title\" });\n titleEl.id = titleId;\n setText(titleEl, title);\n\n const messageEl = el(\"div\", { class: \"sp-confirm-message\" });\n messageEl.id = messageId;\n setText(messageEl, message);\n\n const btnRow = el(\"div\", { class: \"sp-confirm-actions\" });\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = \"sp-btn-ghost\";\n setText(cancelBtn, this.t(\"panel.cancel\"));\n\n const confirmBtn = document.createElement(\"button\");\n confirmBtn.type = \"button\";\n confirmBtn.className = \"sp-btn-danger\";\n setText(confirmBtn, this.t(\"panel.confirmDelete\"));\n\n let closed = false;\n const close = (result: boolean) => {\n if (closed) return;\n closed = true;\n backdrop.removeEventListener(\"keydown\", onKeydown);\n backdrop.style.opacity = \"0\";\n dialog.style.transform = \"translateY(8px) scale(0.97)\";\n setTimeout(() => {\n backdrop.remove();\n resolve(result);\n }, 200);\n };\n\n // Focus trap: Tab cycles between cancel and confirm\n const onKeydown = (e: Event) => {\n const ke = e as KeyboardEvent;\n if (ke.key === \"Escape\") {\n close(false);\n return;\n }\n if (ke.key === \"Tab\") {\n ke.preventDefault();\n const active = (backdrop.getRootNode() as ShadowRoot).activeElement;\n if (active === cancelBtn) {\n confirmBtn.focus();\n } else {\n cancelBtn.focus();\n }\n }\n };\n backdrop.addEventListener(\"keydown\", onKeydown);\n\n cancelBtn.addEventListener(\"click\", () => close(false));\n confirmBtn.addEventListener(\"click\", () => close(true));\n backdrop.addEventListener(\"click\", (e) => {\n if (e.target === backdrop) close(false);\n });\n\n btnRow.appendChild(cancelBtn);\n btnRow.appendChild(confirmBtn);\n dialog.appendChild(titleEl);\n dialog.appendChild(messageEl);\n dialog.appendChild(btnRow);\n backdrop.appendChild(dialog);\n\n this.root.getRootNode() instanceof ShadowRoot\n ? (this.root.getRootNode() as ShadowRoot).appendChild(backdrop)\n : this.root.appendChild(backdrop);\n\n requestAnimationFrame(() => {\n backdrop.style.opacity = \"1\";\n dialog.style.transform = \"translateY(0) scale(1)\";\n cancelBtn.focus();\n });\n });\n }\n\n private async toggleResolve(feedback: FeedbackResponse, btn: HTMLButtonElement): Promise<void> {\n this.pendingMutations.add(feedback.id);\n btn.disabled = true;\n try {\n const newResolved = feedback.status !== \"resolved\";\n await this.client.resolveFeedback(feedback.id, newResolved);\n await this.loadFeedbacks();\n } catch (error) {\n this.pendingMutations.delete(feedback.id);\n btn.disabled = false;\n this.bus.emit(\"feedback:error\", error instanceof Error ? error : new Error(String(error)));\n } finally {\n // Brief cooldown prevents spam-click from immediately toggling back\n if (this.pendingMutations.has(feedback.id)) {\n setTimeout(() => this.pendingMutations.delete(feedback.id), 300);\n }\n }\n }\n\n private toggleFilter(value: string, container: HTMLElement): void {\n // Single-select: only one filter active at a time\n this.activeFilters.clear();\n this.activeFilters.add(value);\n\n // Update chip styles\n const chips = container.querySelectorAll<HTMLButtonElement>(\".sp-chip\");\n for (const chip of chips) {\n const isActive = this.activeFilters.has(chip.dataset.filter ?? \"\");\n chip.classList.toggle(\"sp-chip--active\", isActive);\n chip.setAttribute(\"aria-pressed\", String(isActive));\n }\n\n this.loadFeedbacks().catch(() => {});\n }\n\n scrollToFeedback(feedbackId: string): void {\n const escapedId = CSS.escape(feedbackId);\n const card = this.listContainer.querySelector<HTMLElement>(`[data-feedback-id=\"${escapedId}\"]`);\n if (card) {\n card.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n card.classList.add(\"sp-anim-flash\");\n card.addEventListener(\n \"animationend\",\n () => {\n card.classList.remove(\"sp-anim-flash\");\n },\n { once: true },\n );\n }\n }\n\n /** Refresh the panel after a new feedback is submitted */\n async refresh(): Promise<void> {\n if (this.isOpen) {\n await this.loadFeedbacks();\n }\n }\n\n destroy(): void {\n this.loadController?.abort();\n if (this.searchTimeout) clearTimeout(this.searchTimeout);\n this.listContainer.removeEventListener(\"click\", this.onListClick);\n this.listContainer.removeEventListener(\"keydown\", this.onListKeydown);\n this.listContainer.removeEventListener(\"mouseover\", this.onListMouseover);\n this.listContainer.removeEventListener(\"mouseout\", this.onListMouseout);\n document.removeEventListener(\"sp-marker-click\", this.onMarkerClick);\n this.root.remove();\n }\n}\n","// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\n/** Configuration options for the Siteping widget. */\nexport interface SitepingConfig {\n /** HTTP endpoint that receives feedbacks (e.g. '/api/siteping'). Required unless `store` is provided. */\n endpoint?: string | undefined;\n /** Required — project identifier used to scope feedbacks */\n projectName: string;\n /** Direct store for client-side mode. When set, bypasses HTTP and uses the store directly in the browser. */\n store?: SitepingStore | undefined;\n /** FAB position — defaults to 'bottom-right' */\n position?: \"bottom-right\" | \"bottom-left\";\n /** Accent color for the widget UI — defaults to '#0066ff' */\n accentColor?: string;\n /** Show the widget even in production — defaults to false */\n forceShow?: boolean;\n /** Enable debug logging of lifecycle events — defaults to false */\n debug?: boolean;\n /** Color theme — defaults to 'light' */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** UI locale — defaults to 'en' */\n locale?: \"fr\" | \"en\" | (string & {}) | undefined;\n /** Called when the widget is skipped (production mode, mobile viewport) */\n onSkip?: (reason: \"production\" | \"mobile\") => void;\n\n // Events\n /** Called when the feedback panel is opened. */\n onOpen?: () => void;\n /** Called when the feedback panel is closed. */\n onClose?: () => void;\n onFeedbackSent?: (feedback: FeedbackResponse) => void;\n onError?: (error: Error) => void;\n /** Called when the user starts drawing an annotation. */\n onAnnotationStart?: () => void;\n /** Called when the user finishes drawing an annotation. */\n onAnnotationEnd?: () => void;\n}\n\n/** Instance returned by initSiteping() with lifecycle methods. */\nexport interface SitepingInstance {\n /** Remove the widget from the DOM and clean up all listeners. */\n destroy: () => void;\n /** Open the panel programmatically */\n open: () => void;\n /** Close the panel */\n close: () => void;\n /** Reload feedbacks from server */\n refresh: () => void;\n /** Subscribe to a public widget event */\n on: <K extends keyof SitepingPublicEvents>(\n event: K,\n listener: (...args: SitepingPublicEvents[K]) => void,\n ) => () => void;\n /** Unsubscribe from a public widget event */\n off: <K extends keyof SitepingPublicEvents>(event: K, listener: (...args: SitepingPublicEvents[K]) => void) => void;\n}\n\n/** Events exposed to consumers via SitepingInstance.on / .off */\nexport interface SitepingPublicEvents {\n \"feedback:sent\": [FeedbackResponse];\n \"feedback:deleted\": [string];\n \"panel:open\": [];\n \"panel:close\": [];\n}\n\n// ---------------------------------------------------------------------------\n// Feedback\n// ---------------------------------------------------------------------------\n\n/** Single source of truth for feedback types — used by both TS types and Zod schemas. */\nexport const FEEDBACK_TYPES = [\"question\", \"change\", \"bug\", \"other\"] as const;\nexport type FeedbackType = (typeof FEEDBACK_TYPES)[number];\n\n/** Single source of truth for feedback statuses. */\nexport const FEEDBACK_STATUSES = [\"open\", \"resolved\"] as const;\nexport type FeedbackStatus = (typeof FEEDBACK_STATUSES)[number];\n\n// ---------------------------------------------------------------------------\n// Abstract Store — adapter pattern\n// ---------------------------------------------------------------------------\n\n/** Input for creating a feedback record in the store. */\nexport interface FeedbackCreateInput {\n projectName: string;\n type: FeedbackType;\n message: string;\n status: FeedbackStatus;\n url: string;\n viewport: string;\n userAgent: string;\n authorName: string;\n authorEmail: string;\n clientId: string;\n annotations: AnnotationCreateInput[];\n}\n\n/** Input for a single annotation when creating a feedback. */\nexport interface AnnotationCreateInput {\n cssSelector: string;\n xpath: string;\n textSnippet: string;\n elementTag: string;\n elementId?: string | undefined;\n textPrefix: string;\n textSuffix: string;\n fingerprint: string;\n neighborText: string;\n xPct: number;\n yPct: number;\n wPct: number;\n hPct: number;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n}\n\n/** Query parameters for fetching feedbacks. */\nexport interface FeedbackQuery {\n projectName: string;\n type?: FeedbackType | undefined;\n status?: FeedbackStatus | undefined;\n search?: string | undefined;\n page?: number | undefined;\n limit?: number | undefined;\n}\n\n/** Update payload for patching a feedback. */\nexport interface FeedbackUpdateInput {\n status: FeedbackStatus;\n resolvedAt: Date | null;\n}\n\n/** A persisted feedback record returned by the store. */\nexport interface FeedbackRecord {\n id: string;\n type: FeedbackType;\n message: string;\n status: FeedbackStatus;\n projectName: string;\n url: string;\n authorName: string;\n authorEmail: string;\n viewport: string;\n userAgent: string;\n clientId: string;\n resolvedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n annotations: AnnotationRecord[];\n}\n\n/** A persisted annotation record returned by the store. */\nexport interface AnnotationRecord {\n id: string;\n feedbackId: string;\n cssSelector: string;\n xpath: string;\n textSnippet: string;\n elementTag: string;\n elementId: string | null;\n textPrefix: string;\n textSuffix: string;\n fingerprint: string;\n neighborText: string;\n xPct: number;\n yPct: number;\n wPct: number;\n hPct: number;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n createdAt: Date;\n}\n\n// ---------------------------------------------------------------------------\n// Store errors — throw these from adapter implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a record is not found during update or delete.\n *\n * Handlers translate this to HTTP 404. Adapters MUST throw this (not\n * ORM-specific errors) so the handler layer remains ORM-agnostic.\n */\nexport class StoreNotFoundError extends Error {\n readonly code = \"STORE_NOT_FOUND\" as const;\n constructor(message = \"Record not found\") {\n super(message);\n this.name = \"StoreNotFoundError\";\n }\n}\n\n/**\n * Thrown when a unique constraint is violated (e.g. duplicate `clientId`).\n *\n * Handlers use this to return the existing record instead of failing.\n */\nexport class StoreDuplicateError extends Error {\n readonly code = \"STORE_DUPLICATE\" as const;\n constructor(message = \"Duplicate record\") {\n super(message);\n this.name = \"StoreDuplicateError\";\n }\n}\n\n/** Type guard — works for `StoreNotFoundError` and ORM-specific equivalents (e.g. Prisma P2025). */\nexport function isStoreNotFound(error: unknown): boolean {\n if (error instanceof StoreNotFoundError) return true;\n // Backwards compat: Prisma's P2025\n return typeof error === \"object\" && error !== null && \"code\" in error && (error as { code: string }).code === \"P2025\";\n}\n\n/** Type guard — works for `StoreDuplicateError` and ORM-specific equivalents (e.g. Prisma P2002). */\nexport function isStoreDuplicate(error: unknown): boolean {\n if (error instanceof StoreDuplicateError) return true;\n // Backwards compat: Prisma's P2002\n return typeof error === \"object\" && error !== null && \"code\" in error && (error as { code: string }).code === \"P2002\";\n}\n\n// ---------------------------------------------------------------------------\n// Store helpers — shared conversion logic for adapters\n// ---------------------------------------------------------------------------\n\n/** Flatten a widget `AnnotationPayload` (nested anchor + rect) into a flat `AnnotationCreateInput`. */\nexport function flattenAnnotation(ann: AnnotationPayload): AnnotationCreateInput {\n return {\n cssSelector: ann.anchor.cssSelector,\n xpath: ann.anchor.xpath,\n textSnippet: ann.anchor.textSnippet,\n elementTag: ann.anchor.elementTag,\n elementId: ann.anchor.elementId,\n textPrefix: ann.anchor.textPrefix,\n textSuffix: ann.anchor.textSuffix,\n fingerprint: ann.anchor.fingerprint,\n neighborText: ann.anchor.neighborText,\n xPct: ann.rect.xPct,\n yPct: ann.rect.yPct,\n wPct: ann.rect.wPct,\n hPct: ann.rect.hPct,\n scrollX: ann.scrollX,\n scrollY: ann.scrollY,\n viewportW: ann.viewportW,\n viewportH: ann.viewportH,\n devicePixelRatio: ann.devicePixelRatio,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Abstract Store — adapter pattern\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract storage interface for Siteping.\n *\n * Any adapter (Prisma, Drizzle, raw SQL, localStorage, etc.) implements this\n * interface. The HTTP handler and widget `StoreClient` operate against\n * `SitepingStore`, decoupled from the storage backend.\n *\n * ## Error contract\n *\n * - **`updateFeedback` / `deleteFeedback`**: throw `StoreNotFoundError` when\n * the record does not exist.\n * - **`createFeedback`**: either return the existing record on duplicate\n * `clientId` (idempotent) or throw `StoreDuplicateError`. The handler\n * handles both patterns.\n * - Other methods should not throw on empty results — return empty arrays or `null`.\n */\nexport interface SitepingStore {\n /** Create a feedback with its annotations. Idempotent on `clientId` — return existing record on duplicate, or throw `StoreDuplicateError`. */\n createFeedback(data: FeedbackCreateInput): Promise<FeedbackRecord>;\n /** Paginated query with optional filters. Returns empty array (not error) when no results. */\n getFeedbacks(query: FeedbackQuery): Promise<{ feedbacks: FeedbackRecord[]; total: number }>;\n /** Lookup by client-generated UUID. Returns `null` (not error) when not found. */\n findByClientId(clientId: string): Promise<FeedbackRecord | null>;\n /** Update status/resolvedAt. Throws `StoreNotFoundError` if `id` does not exist. */\n updateFeedback(id: string, data: FeedbackUpdateInput): Promise<FeedbackRecord>;\n /** Delete a single record. Throws `StoreNotFoundError` if `id` does not exist. */\n deleteFeedback(id: string): Promise<void>;\n /** Bulk delete all feedbacks for a project. No-op (not error) if none exist. */\n deleteAllFeedbacks(projectName: string): Promise<void>;\n}\n\n/** Payload sent from the widget to the server when submitting feedback. */\nexport interface FeedbackPayload {\n projectName: string;\n type: FeedbackType;\n message: string;\n url: string;\n viewport: string;\n userAgent: string;\n authorName: string;\n authorEmail: string;\n annotations: AnnotationPayload[];\n /** Client-generated UUID for deduplication */\n clientId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Annotation — multi-selector anchoring (Hypothesis / W3C Web Annotation)\n// ---------------------------------------------------------------------------\n\n/** DOM anchoring data for re-attaching annotations to page elements. */\nexport interface AnchorData {\n /** CSS selector generated by @medv/finder — primary anchor */\n cssSelector: string;\n /** XPath — fallback 1 */\n xpath: string;\n /** First ~120 chars of element innerText — empty string if none */\n textSnippet: string;\n /** Tag name for validation (e.g. \"DIV\", \"SECTION\") */\n elementTag: string;\n /** Element id attribute if available — most stable */\n elementId?: string | undefined;\n /** ~32 chars of text before this element in document flow (disambiguation) */\n textPrefix: string;\n /** ~32 chars of text after this element in document flow (disambiguation) */\n textSuffix: string;\n /** Structural fingerprint: \"childCount:siblingIdx:attrHash\" */\n fingerprint: string;\n /** Text content of adjacent sibling elements (context) */\n neighborText: string;\n}\n\n/** Drawn rectangle coordinates as percentages relative to the anchor element. */\nexport interface RectData {\n /** X offset as fraction of anchor element width — must be in range [0, 1] */\n xPct: number;\n /** Y offset as fraction of anchor element height — must be in range [0, 1] */\n yPct: number;\n /** Width as fraction of anchor element width — must be in range [0, 1] */\n wPct: number;\n /** Height as fraction of anchor element height — must be in range [0, 1] */\n hPct: number;\n}\n\n/** Annotation data sent as part of a feedback submission. */\nexport interface AnnotationPayload {\n anchor: AnchorData;\n rect: RectData;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n}\n\n// ---------------------------------------------------------------------------\n// API responses\n// ---------------------------------------------------------------------------\n\n/** Feedback record as returned by the API (dates serialized as strings). */\nexport interface FeedbackResponse {\n id: string;\n projectName: string;\n type: FeedbackType;\n message: string;\n status: FeedbackStatus;\n url: string;\n viewport: string;\n userAgent: string;\n authorName: string;\n authorEmail: string;\n resolvedAt: string | null;\n createdAt: string;\n updatedAt: string;\n annotations: AnnotationResponse[];\n}\n\n/** Annotation record as returned by the API. */\nexport interface AnnotationResponse {\n id: string;\n feedbackId: string;\n cssSelector: string;\n xpath: string;\n textSnippet: string;\n elementTag: string;\n elementId: string | null;\n textPrefix: string;\n textSuffix: string;\n fingerprint: string;\n neighborText: string;\n xPct: number;\n yPct: number;\n wPct: number;\n hPct: number;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n createdAt: string;\n}\n","import {\n type AnnotationRecord,\n type AnnotationResponse,\n type FeedbackPayload,\n type FeedbackRecord,\n type FeedbackResponse,\n type FeedbackStatus,\n type FeedbackType,\n flattenAnnotation,\n type SitepingStore,\n} from \"@siteping/core\";\nimport type { WidgetClient } from \"./api-client.js\";\n\n/**\n * `WidgetClient` implementation that delegates directly to a `SitepingStore`.\n *\n * Used in client-side mode — the widget calls the store in-process instead of\n * making HTTP requests. Handles the same conversions the HTTP handler normally\n * performs: flattening annotations and serializing dates.\n */\nexport class StoreClient implements WidgetClient {\n constructor(\n private readonly store: SitepingStore,\n private readonly projectName: string,\n ) {}\n\n async sendFeedback(payload: FeedbackPayload): Promise<FeedbackResponse> {\n const record = await this.store.createFeedback({\n projectName: payload.projectName,\n type: payload.type,\n message: payload.message,\n status: \"open\",\n url: payload.url,\n viewport: payload.viewport,\n userAgent: payload.userAgent,\n authorName: payload.authorName,\n authorEmail: payload.authorEmail,\n clientId: payload.clientId,\n annotations: payload.annotations.map(flattenAnnotation),\n });\n\n return toResponse(record);\n }\n\n async getFeedbacks(\n projectName: string,\n options?: { page?: number; limit?: number; type?: FeedbackType; status?: FeedbackStatus; search?: string },\n ): Promise<{ feedbacks: FeedbackResponse[]; total: number }> {\n const { feedbacks, total } = await this.store.getFeedbacks({\n projectName,\n page: options?.page,\n limit: options?.limit,\n type: options?.type,\n status: options?.status,\n search: options?.search,\n });\n\n return { feedbacks: feedbacks.map(toResponse), total };\n }\n\n async resolveFeedback(id: string, resolved: boolean): Promise<FeedbackResponse> {\n const record = await this.store.updateFeedback(id, {\n status: resolved ? \"resolved\" : \"open\",\n resolvedAt: resolved ? new Date() : null,\n });\n return toResponse(record);\n }\n\n async deleteFeedback(id: string): Promise<void> {\n await this.store.deleteFeedback(id);\n }\n\n async deleteAllFeedbacks(projectName: string): Promise<void> {\n await this.store.deleteAllFeedbacks(projectName);\n }\n}\n\n// ---------------------------------------------------------------------------\n// FeedbackRecord (Date) → FeedbackResponse (string) serialization\n// ---------------------------------------------------------------------------\n\nfunction toResponse(record: FeedbackRecord): FeedbackResponse {\n return {\n id: record.id,\n projectName: record.projectName,\n type: record.type,\n message: record.message,\n status: record.status,\n url: record.url,\n viewport: record.viewport,\n userAgent: record.userAgent,\n authorName: record.authorName,\n authorEmail: record.authorEmail,\n resolvedAt: record.resolvedAt?.toISOString() ?? null,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n annotations: record.annotations.map(toAnnotationResponse),\n };\n}\n\nfunction toAnnotationResponse(ann: AnnotationRecord): AnnotationResponse {\n return {\n id: ann.id,\n feedbackId: ann.feedbackId,\n cssSelector: ann.cssSelector,\n xpath: ann.xpath,\n textSnippet: ann.textSnippet,\n elementTag: ann.elementTag,\n elementId: ann.elementId,\n textPrefix: ann.textPrefix,\n textSuffix: ann.textSuffix,\n fingerprint: ann.fingerprint,\n neighborText: ann.neighborText,\n xPct: ann.xPct,\n yPct: ann.yPct,\n wPct: ann.wPct,\n hPct: ann.hPct,\n scrollX: ann.scrollX,\n scrollY: ann.scrollY,\n viewportW: ann.viewportW,\n viewportH: ann.viewportH,\n devicePixelRatio: ann.devicePixelRatio,\n createdAt: ann.createdAt.toISOString(),\n };\n}\n","/**\n * CSS keyframes and animation utilities — Glassmorphism edition.\n *\n * Uses CSS-only spring animations via linear() timing function\n * and refined easing curves for premium motion design.\n */\n\n// Spring easing — computed from a spring simulation (damping: 15, stiffness: 100)\nconst SPRING_LINEAR = `linear(0, 0.006, 0.025, 0.06, 0.11, 0.17, 0.25, 0.34, 0.45, 0.56, 0.67, 0.78, 0.88, 0.95, 1.01, 1.04, 1.05, 1.04, 1.02, 1, 0.99, 1)`;\n\n// Ease-out-expo — fast start, smooth deceleration\nconst EASE_OUT_EXPO = `cubic-bezier(0.16, 1, 0.3, 1)`;\n\n// Spring overshoot — bouncy entrance\nconst SPRING_OVERSHOOT = `cubic-bezier(0.34, 1.56, 0.64, 1)`;\n\n// Smooth decel — for glass transitions\nconst EASE_OUT_QUART = `cubic-bezier(0.25, 1, 0.5, 1)`;\n\nexport const ANIMATION_CSS = `\n /* ---- Keyframes ---- */\n\n @keyframes sp-fab-in {\n from {\n transform: scale(0) rotate(-180deg);\n opacity: 0;\n }\n to {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n @keyframes sp-fab-glow {\n 0%, 100% { box-shadow: 0 4px 20px var(--sp-accent-glow), 0 2px 8px rgba(0, 0, 0, 0.08); }\n 50% { box-shadow: 0 4px 28px var(--sp-accent-glow), 0 2px 12px rgba(0, 0, 0, 0.1); }\n }\n\n @keyframes sp-marker-in {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n 60% {\n transform: scale(1.2);\n opacity: 1;\n }\n 100% {\n transform: scale(1);\n }\n }\n\n @keyframes sp-pulse-ring {\n 0% {\n box-shadow: 0 0 0 0 var(--sp-accent-glow);\n }\n 70% {\n box-shadow: 0 0 0 8px transparent;\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n }\n }\n\n @keyframes sp-flash-bg {\n 0% { background-color: var(--sp-accent-light); }\n 100% { background-color: transparent; }\n }\n\n @keyframes sp-slide-up {\n from {\n transform: translateY(8px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n }\n\n @keyframes sp-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes sp-shimmer {\n 0% { background-position: -200% 0; }\n 100% { background-position: 200% 0; }\n }\n\n /* ---- Animation classes ---- */\n\n .sp-anim-fab-in {\n animation: sp-fab-in 0.5s ${SPRING_LINEAR} both;\n }\n\n .sp-anim-marker-in {\n animation: sp-marker-in 0.35s ${SPRING_OVERSHOOT} both;\n }\n\n .sp-anim-pulse {\n animation: sp-pulse-ring 0.7s ease-out;\n }\n\n .sp-anim-flash {\n animation: sp-flash-bg 0.5s ${EASE_OUT_QUART};\n }\n\n .sp-anim-slide-up {\n animation: sp-slide-up 0.3s ${EASE_OUT_EXPO} both;\n }\n\n .sp-anim-fade-in {\n animation: sp-fade-in 0.2s ease-out both;\n }\n\n /* ---- Transition utilities ---- */\n\n .sp-panel {\n transform: translateX(110%);\n transition: transform 0.4s ${EASE_OUT_EXPO};\n }\n\n .sp-panel.sp-panel--open {\n transform: translateX(0);\n }\n\n .sp-radial-item {\n opacity: 0;\n pointer-events: none;\n transform: translate(0, 0) scale(0.8);\n transition:\n transform 0.35s ${SPRING_OVERSHOOT},\n opacity 0.2s ease,\n background 0.2s ease,\n border-color 0.2s ease,\n box-shadow 0.2s ease;\n }\n\n .sp-radial-item.sp-radial-item--open {\n opacity: 1;\n pointer-events: auto;\n }\n\n /* Stagger delay via CSS custom property --sp-i */\n .sp-radial-item {\n transition-delay: calc(var(--sp-i, 0) * 50ms);\n }\n\n /* ---- Card stagger animation ---- */\n\n @keyframes sp-card-in {\n from {\n transform: translateY(12px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n }\n\n .sp-card {\n animation: sp-card-in 0.35s ${EASE_OUT_EXPO} both;\n animation-delay: calc(var(--sp-card-i, 0) * 40ms);\n }\n\n /* ---- Loading spinner ---- */\n\n @keyframes sp-spin {\n to { transform: rotate(360deg); }\n }\n\n .sp-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid var(--sp-border);\n border-top-color: var(--sp-accent);\n border-radius: 50%;\n animation: sp-spin 0.6s linear infinite;\n }\n\n /* ---- Badge bounce ---- */\n\n @keyframes sp-badge-in {\n 0% { transform: scale(0); }\n 60% { transform: scale(1.3); }\n 100% { transform: scale(1); }\n }\n\n .sp-fab-badge {\n animation: sp-badge-in 0.4s ${SPRING_OVERSHOOT} both;\n }\n\n /* ---- Reduced motion ---- */\n\n @media (prefers-reduced-motion: reduce) {\n *, *::before, *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n }\n\n`;\n","import { ANIMATION_CSS } from \"./animations.js\";\nimport { cssVariables, type ThemeColors } from \"./theme.js\";\n\n/**\n * Build the complete CSS stylesheet for the Shadow DOM.\n *\n * Design: Glassmorphism — frosted glass surfaces, soft depth,\n * accent gradients, premium micro-interactions.\n *\n * Principles:\n * - :host uses `all: initial` to block inherited styles\n * - All classes prefixed with sp- (defense in depth)\n * - CSS custom properties for theming\n * - No external fonts — system-ui stack (Inter if available)\n * - :focus-visible on all interactive elements\n * - prefers-reduced-motion support\n */\nexport function buildStyles(colors: ThemeColors): string {\n return `\n :host {\n all: initial;\n position: fixed;\n z-index: 2147483647;\n font-family: var(--sp-font);\n font-size: 14px;\n line-height: 1.5;\n color: var(--sp-text);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n ${cssVariables(colors)}\n\n /* Identity modal — theme-aware backdrop + panel */\n --sp-identity-bg: ${colors.glassBgHeavy};\n --sp-identity-overlay: ${colors.bg === \"#ffffff\" ? \"rgba(15, 23, 42, 0.2)\" : \"rgba(0, 0, 0, 0.4)\"};\n }\n\n *, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n /* ============================\n Focus visible (accessibility)\n ============================ */\n\n :focus-visible {\n outline: 2px solid var(--sp-accent);\n outline-offset: 2px;\n }\n\n /* ============================\n FAB (Floating Action Button)\n ============================ */\n\n .sp-fab {\n position: fixed;\n width: 52px;\n height: 52px;\n border-radius: var(--sp-radius-full);\n background: var(--sp-accent-gradient);\n color: #fff;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow:\n 0 4px 20px var(--sp-accent-glow),\n 0 2px 8px rgba(0, 0, 0, 0.08);\n transition:\n transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),\n box-shadow 0.3s ease;\n outline: none;\n }\n\n .sp-fab:focus-visible {\n outline: 2px solid #fff;\n outline-offset: 3px;\n }\n\n .sp-fab:hover {\n transform: translateY(-2px) scale(1.05);\n box-shadow:\n 0 8px 28px var(--sp-accent-glow),\n 0 4px 12px rgba(0, 0, 0, 0.1);\n }\n\n .sp-fab:active {\n transform: translateY(0) scale(0.95);\n transition-duration: 0.1s;\n }\n\n .sp-fab--bottom-right {\n bottom: 24px;\n right: 24px;\n }\n\n .sp-fab--bottom-left {\n bottom: 24px;\n left: 24px;\n }\n\n .sp-fab svg {\n width: 22px;\n height: 22px;\n fill: currentColor;\n transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n /* ---- FAB Badge ---- */\n\n .sp-fab-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n border-radius: var(--sp-radius-full);\n background: #ef4444;\n color: #fff;\n font-size: 11px;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid #fff;\n pointer-events: none;\n font-family: var(--sp-font);\n line-height: 1;\n }\n\n /* ============================\n Radial Menu\n ============================ */\n\n .sp-radial {\n position: fixed;\n pointer-events: none;\n width: 52px;\n height: 52px;\n }\n\n .sp-radial--bottom-right {\n bottom: 24px;\n right: 24px;\n }\n\n .sp-radial--bottom-left {\n bottom: 24px;\n left: 24px;\n }\n\n .sp-radial-item {\n position: absolute;\n left: 4px;\n bottom: 4px;\n width: 44px;\n height: 44px;\n border-radius: var(--sp-radius-full);\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur));\n -webkit-backdrop-filter: blur(var(--sp-blur));\n color: var(--sp-text);\n border: 1px solid var(--sp-glass-border);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: var(--sp-shadow-md);\n font-size: 12px;\n font-weight: 600;\n }\n\n .sp-radial-item:hover,\n .sp-radial-item:focus-visible {\n background: rgba(255, 255, 255, 0.95);\n border-color: var(--sp-accent);\n color: var(--sp-accent);\n box-shadow:\n var(--sp-shadow-md),\n 0 0 0 3px var(--sp-accent-light);\n outline: none;\n }\n\n .sp-radial-item svg {\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n stroke: currentColor;\n fill: none;\n }\n\n .sp-radial-label {\n white-space: nowrap;\n font-size: 12px;\n font-weight: 500;\n color: var(--sp-text);\n pointer-events: none;\n opacity: 0;\n padding: 4px 12px;\n border-radius: var(--sp-radius);\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-sm);\n transform: translateX(4px);\n transition: opacity 0.2s ease, transform 0.2s ease;\n }\n\n .sp-radial-item:hover .sp-radial-label,\n .sp-radial-item:focus-visible .sp-radial-label {\n opacity: 1;\n transform: translateX(0);\n }\n\n /* ============================\n Panel (Side drawer)\n ============================ */\n\n .sp-panel {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n max-width: 100vw;\n height: 100vh;\n height: 100dvh;\n background: var(--sp-glass-bg);\n backdrop-filter: blur(var(--sp-blur-heavy));\n -webkit-backdrop-filter: blur(var(--sp-blur-heavy));\n border-left: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-xl);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n @media (max-width: 480px) {\n .sp-panel {\n width: 100vw;\n border-left: none;\n }\n }\n\n .sp-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur));\n -webkit-backdrop-filter: blur(var(--sp-blur));\n }\n\n .sp-panel-title {\n font-size: 17px;\n font-weight: 700;\n color: var(--sp-text);\n letter-spacing: -0.02em;\n }\n\n .sp-panel-close {\n width: 44px;\n height: 44px;\n border-radius: var(--sp-radius);\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--sp-text-tertiary);\n transition: all 0.2s ease;\n }\n\n .sp-panel-close:hover {\n background: var(--sp-bg-hover);\n color: var(--sp-text);\n }\n\n .sp-panel-close svg {\n width: 16px;\n height: 16px;\n }\n\n /* ============================\n Filters & Search\n ============================ */\n\n .sp-filters {\n padding: 16px 24px;\n border-bottom: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur));\n -webkit-backdrop-filter: blur(var(--sp-blur));\n position: sticky;\n top: 0;\n z-index: 1;\n }\n\n .sp-search-wrap {\n position: relative;\n margin-bottom: 12px;\n }\n\n .sp-search {\n width: 100%;\n height: 40px;\n padding: 0 12px 0 38px;\n border-radius: var(--sp-radius);\n border: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n color: var(--sp-text);\n font-family: var(--sp-font);\n font-size: 13px;\n outline: none;\n transition: all 0.2s ease;\n }\n\n .sp-search::placeholder {\n color: var(--sp-text-tertiary);\n }\n\n .sp-search:focus {\n border-color: var(--sp-accent);\n box-shadow: 0 0 0 3px var(--sp-accent-light);\n background: #fff;\n }\n\n .sp-search-icon {\n position: absolute;\n left: 12px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--sp-text-tertiary);\n width: 16px;\n height: 16px;\n transition: color 0.2s ease;\n }\n\n .sp-search:focus ~ .sp-search-icon,\n .sp-search-wrap:focus-within .sp-search-icon {\n color: var(--sp-accent);\n }\n\n .sp-chips {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n }\n\n .sp-chip {\n padding: 5px 14px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n color: var(--sp-text-secondary);\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n white-space: nowrap;\n letter-spacing: 0.01em;\n }\n\n .sp-chip:hover {\n border-color: var(--sp-accent);\n color: var(--sp-accent);\n background: var(--sp-accent-light);\n }\n\n .sp-chip--active {\n background: var(--sp-accent-gradient);\n border-color: transparent;\n color: #fff;\n box-shadow: 0 2px 8px var(--sp-accent-glow);\n }\n\n .sp-chip--active:hover {\n background: var(--sp-accent-gradient);\n border-color: transparent;\n color: #fff;\n }\n\n /* ============================\n Feedback Cards\n ============================ */\n\n .sp-list {\n flex: 1;\n overflow-y: auto;\n padding: 8px 12px;\n }\n\n .sp-list::-webkit-scrollbar {\n width: 6px;\n }\n\n .sp-list::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .sp-list::-webkit-scrollbar-thumb {\n background: var(--sp-border);\n border-radius: var(--sp-radius-full);\n }\n\n .sp-list::-webkit-scrollbar-thumb:hover {\n background: var(--sp-text-tertiary);\n }\n\n .sp-card {\n display: flex;\n padding: 14px 16px;\n margin-bottom: 6px;\n cursor: pointer;\n border-radius: var(--sp-radius);\n background: var(--sp-glass-bg-heavy);\n border: 1px solid var(--sp-glass-border);\n transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n .sp-card:hover {\n background: #fff;\n border-color: var(--sp-border);\n box-shadow: var(--sp-shadow-md);\n transform: translateY(-2px);\n }\n\n .sp-card:active {\n transform: translateY(0) scale(0.99);\n transition-duration: 0.1s;\n }\n\n .sp-card-bar {\n width: 3px;\n border-radius: var(--sp-radius-full);\n margin-right: 14px;\n flex-shrink: 0;\n }\n\n .sp-card-body {\n flex: 1;\n min-width: 0;\n }\n\n .sp-card-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n }\n\n .sp-card-number {\n font-size: 12px;\n font-weight: 700;\n color: var(--sp-text-tertiary);\n font-variant-numeric: tabular-nums;\n }\n\n .sp-badge {\n padding: 2px 10px;\n border-radius: var(--sp-radius-full);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.02em;\n }\n\n .sp-card-date {\n font-size: 11px;\n color: var(--sp-text-tertiary);\n margin-left: auto;\n }\n\n .sp-card-message {\n font-size: 13px;\n line-height: 1.5;\n color: var(--sp-text);\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .sp-card-message--expanded {\n -webkit-line-clamp: unset;\n }\n\n .sp-card-expand {\n font-size: 12px;\n font-weight: 500;\n color: var(--sp-accent);\n cursor: pointer;\n background: none;\n border: none;\n padding: 4px 0;\n font-family: var(--sp-font);\n transition: opacity 0.15s ease;\n }\n\n .sp-card-expand:hover {\n opacity: 0.8;\n }\n\n .sp-card-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n margin-top: 10px;\n }\n\n .sp-btn-resolve,\n .sp-btn-delete {\n padding: 8px 14px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: transparent;\n color: var(--sp-text-secondary);\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 4px;\n transition: all 0.2s ease;\n }\n\n .sp-btn-resolve svg,\n .sp-btn-delete svg {\n width: 14px;\n height: 14px;\n }\n\n .sp-btn-resolve:hover {\n border-color: #22c55e;\n color: #22c55e;\n background: rgba(34, 197, 94, 0.06);\n }\n\n .sp-btn-delete:hover {\n border-color: #ef4444;\n color: #ef4444;\n background: rgba(239, 68, 68, 0.06);\n }\n\n .sp-btn-resolve:disabled,\n .sp-btn-delete:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* ---- Delete All (header) ---- */\n\n .sp-panel-header-right {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .sp-btn-delete-all {\n padding: 5px 12px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: transparent;\n color: var(--sp-text-tertiary);\n font-family: var(--sp-font);\n font-size: 11px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 4px;\n transition: all 0.2s ease;\n }\n\n .sp-btn-delete-all svg {\n width: 13px;\n height: 13px;\n }\n\n .sp-btn-delete-all:hover {\n border-color: #ef4444;\n color: #ef4444;\n background: rgba(239, 68, 68, 0.06);\n }\n\n .sp-btn-delete-all:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* ---- Confirm Dialog ---- */\n\n .sp-confirm-backdrop {\n position: fixed;\n inset: 0;\n background: var(--sp-backdrop, rgba(15, 23, 42, 0.2));\n backdrop-filter: blur(var(--sp-blur));\n -webkit-backdrop-filter: blur(var(--sp-blur));\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 2147483647;\n opacity: 0;\n transition: opacity 0.2s ease;\n }\n\n .sp-confirm-dialog {\n width: 340px;\n padding: 28px;\n border-radius: 20px;\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur-heavy));\n -webkit-backdrop-filter: blur(var(--sp-blur-heavy));\n border: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-xl);\n font-family: var(--sp-font);\n transform: translateY(8px) scale(0.97);\n transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n .sp-confirm-title {\n font-size: 17px;\n font-weight: 700;\n color: var(--sp-text);\n letter-spacing: -0.02em;\n margin-bottom: 8px;\n }\n\n .sp-confirm-message {\n font-size: 14px;\n color: var(--sp-text-secondary);\n line-height: 1.5;\n margin-bottom: 20px;\n }\n\n .sp-confirm-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n }\n\n .sp-btn-danger {\n height: 40px;\n padding: 0 22px;\n border-radius: var(--sp-radius);\n border: none;\n background: #ef4444;\n color: #fff;\n font-family: var(--sp-font);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px rgba(239, 68, 68, 0.25);\n }\n\n .sp-btn-danger:hover {\n background: #dc2626;\n box-shadow: 0 4px 16px rgba(239, 68, 68, 0.3);\n transform: translateY(-1px);\n }\n\n .sp-btn-danger:active {\n transform: translateY(0) scale(0.98);\n transition-duration: 0.1s;\n }\n\n .sp-card--resolved {\n opacity: 0.5;\n }\n\n .sp-card--resolved .sp-card-message {\n text-decoration: line-through;\n text-decoration-color: var(--sp-text-tertiary);\n }\n\n /* ============================\n Loading State\n ============================ */\n\n .sp-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n }\n\n /* ============================\n Identity Form\n ============================ */\n\n .sp-identity-title {\n font-size: 17px;\n font-weight: 700;\n color: var(--sp-text);\n letter-spacing: -0.02em;\n }\n\n .sp-input {\n width: 100%;\n height: 42px;\n padding: 0 14px;\n border-radius: var(--sp-radius);\n border: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n color: var(--sp-text);\n font-family: var(--sp-font);\n font-size: 14px;\n outline: none;\n transition: all 0.2s ease;\n }\n\n .sp-input::placeholder {\n color: var(--sp-text-tertiary);\n }\n\n .sp-input:focus {\n border-color: var(--sp-accent);\n box-shadow: 0 0 0 3px var(--sp-accent-light);\n background: #fff;\n }\n\n .sp-input-label {\n font-size: 13px;\n font-weight: 500;\n color: var(--sp-text-secondary);\n margin-bottom: 6px;\n display: block;\n }\n\n /* ============================\n Buttons\n ============================ */\n\n .sp-btn-primary {\n height: 40px;\n padding: 0 22px;\n border-radius: var(--sp-radius);\n border: none;\n background: var(--sp-accent-gradient);\n color: #fff;\n font-family: var(--sp-font);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px var(--sp-accent-glow);\n }\n\n .sp-btn-primary:hover {\n box-shadow: 0 4px 16px var(--sp-accent-glow);\n transform: translateY(-1px);\n }\n\n .sp-btn-primary:active {\n transform: translateY(0) scale(0.98);\n transition-duration: 0.1s;\n }\n\n .sp-btn-primary:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n }\n\n .sp-btn-ghost {\n height: 40px;\n padding: 0 22px;\n border-radius: var(--sp-radius);\n border: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n color: var(--sp-text-secondary);\n font-family: var(--sp-font);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .sp-btn-ghost:hover {\n border-color: var(--sp-accent);\n color: var(--sp-accent);\n background: var(--sp-accent-light);\n }\n\n /* ============================\n Empty State\n ============================ */\n\n .sp-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 56px 24px;\n color: var(--sp-text-tertiary);\n text-align: center;\n gap: 8px;\n animation: sp-fade-in 0.3s ease-out both;\n }\n\n .sp-empty-text {\n font-size: 14px;\n font-weight: 500;\n }\n\n /* ============================\n Forced Colors / High Contrast\n ============================ */\n\n @media (forced-colors: active) {\n .sp-fab,\n .sp-radial-item,\n .sp-chip,\n .sp-card,\n .sp-panel-close,\n .sp-search,\n .sp-btn-resolve,\n .sp-btn-delete,\n .sp-btn-delete-all,\n .sp-btn-primary,\n .sp-btn-ghost,\n .sp-btn-danger,\n .sp-card-expand,\n .sp-input,\n .sp-confirm-dialog {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n color: ButtonText !important;\n }\n\n .sp-fab:focus-visible,\n .sp-radial-item:focus-visible,\n .sp-chip:focus-visible,\n .sp-panel-close:focus-visible,\n .sp-btn-resolve:focus-visible,\n .sp-btn-delete:focus-visible,\n .sp-btn-delete-all:focus-visible,\n .sp-btn-primary:focus-visible,\n .sp-btn-ghost:focus-visible,\n .sp-btn-danger:focus-visible,\n .sp-card-expand:focus-visible,\n .sp-input:focus-visible,\n .sp-search:focus-visible {\n outline: 3px solid Highlight !important;\n }\n\n .sp-panel {\n border: 2px solid ButtonText !important;\n }\n\n .sp-fab-badge {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n color: ButtonText !important;\n }\n\n .sp-card-bar {\n background: ButtonText !important;\n }\n }\n\n ${ANIMATION_CSS}\n `;\n}\n","import type { FeedbackResponse } from \"@siteping/core\";\nimport { el, formatRelativeDate, setText } from \"./dom-utils.js\";\nimport { createT, getTypeLabel } from \"./i18n/index.js\";\nimport { getTypeBgColor, getTypeColor, type ThemeColors } from \"./styles/theme.js\";\n\nconst SHOW_DELAY = 120;\nconst HIDE_DELAY = 80;\n\n/**\n * Tooltip shown on annotation marker hover.\n *\n * Glassmorphism design: frosted glass with pastel badge,\n * smooth entrance animation, directional arrow.\n * Lives outside Shadow DOM.\n */\nexport class Tooltip {\n private root: HTMLElement;\n private arrow: HTMLElement;\n private showTimer: ReturnType<typeof setTimeout> | null = null;\n private hideTimer: ReturnType<typeof setTimeout> | null = null;\n private currentFeedbackId: string | null = null;\n\n readonly tooltipId = \"sp-tooltip\";\n\n constructor(\n private readonly colors: ThemeColors,\n private readonly locale: string = \"fr\",\n ) {\n this.root = el(\"div\", {\n style: `\n position: fixed;\n z-index: 2147483647;\n max-width: 280px;\n padding: 12px 14px;\n border-radius: 14px;\n background: ${this.colors.glassBgHeavy};\n backdrop-filter: blur(24px);\n -webkit-backdrop-filter: blur(24px);\n border: 1px solid ${this.colors.glassBorder};\n box-shadow: 0 8px 32px ${this.colors.shadow}, 0 2px 8px ${this.colors.shadow};\n font-family: \"Inter\", system-ui, -apple-system, sans-serif;\n pointer-events: auto;\n opacity: 0;\n transform: translateY(6px) scale(0.97);\n transition: opacity 0.2s cubic-bezier(0.16, 1, 0.3, 1), transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n visibility: hidden;\n -webkit-font-smoothing: antialiased;\n `,\n });\n\n this.root.setAttribute(\"role\", \"tooltip\");\n this.root.id = this.tooltipId;\n\n // Arrow element\n this.arrow = el(\"div\", {\n style: `\n position: absolute;\n width: 12px;\n height: 12px;\n background: ${this.colors.glassBgHeavy};\n border: 1px solid ${this.colors.glassBorder};\n transform: rotate(45deg);\n pointer-events: none;\n `,\n });\n this.root.appendChild(this.arrow);\n\n this.root.addEventListener(\"mouseenter\", () => this.cancelHide());\n this.root.addEventListener(\"mouseleave\", () => this.scheduleHide());\n document.body.appendChild(this.root);\n }\n\n show(feedback: FeedbackResponse, anchorRect: DOMRect): void {\n if (this.currentFeedbackId === feedback.id) return;\n this.cancelHide();\n this.cancelShow();\n\n this.showTimer = setTimeout(() => {\n this.currentFeedbackId = feedback.id;\n this.render(feedback);\n this.position(anchorRect);\n\n // Check prefers-reduced-motion live (not cached at construction time)\n const reduceMotion =\n typeof window !== \"undefined\" && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n this.root.style.transition = reduceMotion ? \"none\" : \"\";\n\n this.root.style.visibility = \"visible\";\n this.root.style.opacity = \"1\";\n this.root.style.transform = \"translateY(0) scale(1)\";\n }, SHOW_DELAY);\n }\n\n scheduleHide(): void {\n this.cancelHide();\n this.hideTimer = setTimeout(() => this.hide(), HIDE_DELAY);\n }\n\n hide(): void {\n this.cancelShow();\n this.currentFeedbackId = null;\n this.root.style.opacity = \"0\";\n this.root.style.transform = \"translateY(6px) scale(0.97)\";\n setTimeout(() => {\n if (!this.currentFeedbackId) {\n this.root.style.visibility = \"hidden\";\n }\n }, 200);\n }\n\n private cancelShow(): void {\n if (this.showTimer) {\n clearTimeout(this.showTimer);\n this.showTimer = null;\n }\n }\n\n private cancelHide(): void {\n if (this.hideTimer) {\n clearTimeout(this.hideTimer);\n this.hideTimer = null;\n }\n }\n\n private render(feedback: FeedbackResponse): void {\n // Clear previous content safely (except arrow)\n const children = Array.from(this.root.children);\n for (const child of children) {\n if (child !== this.arrow) child.remove();\n }\n\n const typeColor = getTypeColor(feedback.type, this.colors);\n const typeBg = getTypeBgColor(feedback.type, this.colors);\n const t = createT(this.locale);\n const typeLabel = getTypeLabel(feedback.type, t);\n\n // Header row: badge + date\n const header = el(\"div\", { style: \"display:flex;align-items:center;gap:8px;margin-bottom:8px;\" });\n\n const badge = el(\"span\", {\n style: `\n padding:3px 10px;border-radius:9999px;\n font-size:11px;font-weight:600;\n color:${typeColor};background:${typeBg};\n letter-spacing:0.02em;\n `,\n });\n setText(badge, typeLabel);\n\n const date = el(\"span\", { style: `font-size:11px;color:${this.colors.textSecondary};margin-left:auto;` });\n setText(date, formatRelativeDate(feedback.createdAt, this.locale));\n\n header.appendChild(badge);\n header.appendChild(date);\n\n // Message body (safe — textContent only)\n const body = el(\"div\", {\n style: `font-size:13px;line-height:1.55;color:${this.colors.text};display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;`,\n });\n setText(body, feedback.message);\n\n // Insert content before arrow\n this.root.insertBefore(header, this.arrow);\n this.root.insertBefore(body, this.arrow);\n }\n\n private position(anchorRect: DOMRect): void {\n const tooltipRect = this.root.getBoundingClientRect();\n const gap = 10;\n\n let top = anchorRect.top - tooltipRect.height - gap;\n let left = anchorRect.left + anchorRect.width / 2 - tooltipRect.width / 2;\n let isAbove = true;\n\n // Flip below if not enough space above\n if (top < 8) {\n top = anchorRect.bottom + gap;\n isAbove = false;\n }\n\n left = Math.max(8, Math.min(left, window.innerWidth - tooltipRect.width - 8));\n\n this.root.style.top = `${top}px`;\n this.root.style.left = `${left}px`;\n\n // Position arrow\n const arrowLeft = Math.max(16, Math.min(anchorRect.left + anchorRect.width / 2 - left - 6, tooltipRect.width - 22));\n\n if (isAbove) {\n // Arrow at bottom, pointing down\n this.arrow.style.cssText = `\n position:absolute;\n width:12px;height:12px;\n background:${this.colors.glassBgHeavy};\n border-right:1px solid ${this.colors.glassBorder};\n border-bottom:1px solid ${this.colors.glassBorder};\n transform:rotate(45deg);\n pointer-events:none;\n bottom:-6px;\n left:${arrowLeft}px;\n `;\n } else {\n // Arrow at top, pointing up\n this.arrow.style.cssText = `\n position:absolute;\n width:12px;height:12px;\n background:${this.colors.glassBgHeavy};\n border-left:1px solid ${this.colors.glassBorder};\n border-top:1px solid ${this.colors.glassBorder};\n transform:rotate(45deg);\n pointer-events:none;\n top:-6px;\n left:${arrowLeft}px;\n `;\n }\n }\n\n /** Check if a DOM node belongs to this tooltip (for MutationObserver filtering). */\n contains(node: Node): boolean {\n return this.root.contains(node);\n }\n\n destroy(): void {\n this.cancelShow();\n this.cancelHide();\n this.root.remove();\n }\n}\n","import type { FeedbackPayload, SitepingConfig, SitepingInstance, SitepingPublicEvents } from \"@siteping/core\";\nimport { Annotator } from \"./annotator.js\";\nimport { ApiClient, flushRetryQueue, type WidgetClient } from \"./api-client.js\";\nimport { EventBus, type PublicWidgetEvents, type WidgetEvents } from \"./events.js\";\nimport { Fab } from \"./fab.js\";\nimport { createT, type TFunction } from \"./i18n/index.js\";\nimport { getIdentity, type Identity, saveIdentity } from \"./identity.js\";\nimport { MarkerManager } from \"./markers.js\";\nimport { Panel } from \"./panel.js\";\nimport { StoreClient } from \"./store-client.js\";\nimport { buildStyles } from \"./styles/base.js\";\nimport { buildThemeColors } from \"./styles/theme.js\";\nimport { Tooltip } from \"./tooltip.js\";\n\n/** Singleton guard — prevents duplicate widgets from overlapping */\nlet instance: SitepingInstance | null = null;\n\n/** Build a no-op SitepingInstance for when the widget is skipped */\nfunction skippedInstance(): SitepingInstance {\n const noop = () => {};\n return {\n destroy: noop,\n open: noop,\n close: noop,\n refresh: noop,\n on: () => noop,\n off: noop,\n };\n}\n\n/**\n * Main widget launcher — orchestrates all UI components.\n *\n * Architecture:\n * - Creates a <siteping-widget> custom element in the document\n * - Attaches a closed Shadow DOM for CSS isolation\n * - FAB + Panel live inside the Shadow DOM\n * - Overlay, markers, tooltips live outside (appended to document.body)\n */\nexport function launch(config: SitepingConfig): SitepingInstance {\n // Debug helper — only logs when config.debug is true\n const log: (...args: unknown[]) => void = config.debug\n ? (...args: unknown[]) => console.debug(\"[siteping]\", ...args)\n : () => {};\n\n // Guard: prevent duplicate initSiteping() calls\n if (instance) {\n log(\"initSiteping() called more than once — returning existing instance\");\n return instance;\n }\n\n // Guard: only show in development (forceShow bypasses)\n if (!config.forceShow) {\n try {\n // Check for Node/bundler production environment — avoid import.meta\n // which causes \"Critical dependency\" warnings in Next.js webpack builds\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\") {\n const reason = \"production\";\n console.info(\"[siteping] Widget not loaded: production mode detected. Use forceShow: true to override.\");\n config.onSkip?.(reason);\n return skippedInstance();\n }\n } catch {\n // Silently ignore — browser or restricted environment\n }\n }\n\n // Guard: desktop only (< 768px = hidden)\n if (window.innerWidth < 768) {\n const reason = \"mobile\";\n console.info(\"[siteping] Widget not loaded: viewport width < 768px (mobile not supported).\");\n config.onSkip?.(reason);\n return skippedInstance();\n }\n\n // Guard: validate required config fields\n if (!config.store && (!config.endpoint || typeof config.endpoint !== \"string\")) {\n console.error(\n \"[siteping] Missing 'endpoint' or 'store' in config. Provide an endpoint like '/api/siteping' or a SitepingStore instance.\",\n );\n return skippedInstance();\n }\n if (!config.projectName || typeof config.projectName !== \"string\") {\n console.error(\"[siteping] Missing or invalid 'projectName' in config. Expected a non-empty string.\");\n return skippedInstance();\n }\n\n const locale = config.locale ?? \"en\";\n const t = createT(locale);\n\n log(\"Initializing widget\", { projectName: config.projectName, theme: config.theme ?? \"light\", locale });\n\n const colors = buildThemeColors(config.accentColor, config.theme);\n const bus = new EventBus<WidgetEvents>();\n const publicBus = new EventBus<PublicWidgetEvents>();\n\n // Client-side mode (store) vs HTTP mode (endpoint)\n const client: WidgetClient = config.store\n ? new StoreClient(config.store, config.projectName)\n : new ApiClient(config.endpoint as string, config.projectName);\n\n // Wire config callbacks to event bus\n if (config.onOpen) bus.on(\"open\", config.onOpen);\n if (config.onClose) bus.on(\"close\", config.onClose);\n if (config.onFeedbackSent) bus.on(\"feedback:sent\", config.onFeedbackSent);\n if (config.onError) bus.on(\"feedback:error\", config.onError);\n if (config.onAnnotationStart) bus.on(\"annotation:start\", config.onAnnotationStart);\n if (config.onAnnotationEnd) bus.on(\"annotation:end\", config.onAnnotationEnd);\n\n // Bridge internal events to public bus\n bus.on(\"feedback:sent\", (fb) => publicBus.emit(\"feedback:sent\", fb));\n bus.on(\"feedback:deleted\", (id) => publicBus.emit(\"feedback:deleted\", id));\n bus.on(\"open\", () => publicBus.emit(\"panel:open\"));\n bus.on(\"close\", () => publicBus.emit(\"panel:close\"));\n\n // Debug logging for key lifecycle events\n bus.on(\"open\", () => log(\"Panel opened\"));\n bus.on(\"close\", () => log(\"Panel closed\"));\n bus.on(\"feedback:sent\", (fb) => log(\"Feedback sent\", fb.id));\n bus.on(\"feedback:error\", (err) => log(\"Feedback failed\", err.message));\n bus.on(\"annotation:start\", () => log(\"Annotation started\"));\n bus.on(\"annotation:end\", () => log(\"Annotation ended\"));\n\n // Create host element + Shadow DOM\n const host = document.createElement(\"siteping-widget\");\n host.style.cssText = \"position:fixed;z-index:2147483647;\";\n // Use open mode only for testing — closed in production for CSS isolation.\n // Shadow DOM mode is determined by environment, never by public config.\n let isTestEnv = false;\n try {\n // Dynamic key prevents bundlers (tsup/esbuild) from statically replacing\n // process.env.NODE_ENV at build time — the widget needs runtime detection\n // so E2E tests can set globalThis.process = { env: { NODE_ENV: 'test' } }\n const envKey = \"NODE_\" + \"ENV\";\n if (typeof process !== \"undefined\" && process.env?.[envKey] === \"test\") {\n isTestEnv = true;\n }\n } catch {\n // Silently ignore — browser or restricted environment\n }\n const shadowMode = isTestEnv ? (\"open\" as const) : (\"closed\" as const);\n const shadow = host.attachShadow({ mode: shadowMode });\n\n // Inject styles into Shadow DOM — adoptedStyleSheets with fallback for Safari < 16.4\n const supportsAdoptedStyleSheets = \"adoptedStyleSheets\" in ShadowRoot.prototype;\n if (supportsAdoptedStyleSheets) {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(buildStyles(colors));\n shadow.adoptedStyleSheets = [sheet];\n } else {\n const style = document.createElement(\"style\");\n style.textContent = buildStyles(colors);\n (shadow as unknown as DocumentFragment).appendChild(style);\n }\n\n document.body.appendChild(host);\n\n // Screen reader live region for feedback submission announcements\n const liveRegion = document.createElement(\"div\");\n liveRegion.setAttribute(\"role\", \"status\");\n liveRegion.setAttribute(\"aria-live\", \"polite\");\n liveRegion.setAttribute(\"aria-atomic\", \"true\");\n liveRegion.style.cssText =\n \"position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;\";\n document.body.appendChild(liveRegion);\n\n // Components outside Shadow DOM\n const tooltip = new Tooltip(colors, locale);\n const markers = new MarkerManager(colors, tooltip, bus, t);\n\n // Components inside Shadow DOM\n const fab = new Fab(shadow, config, bus, t);\n const panel = new Panel(shadow, colors, bus, client, config.projectName, markers, t, locale);\n const annotator = new Annotator(colors, bus, t);\n\n // Handle annotation completion via event bus (not DOM events)\n // Concurrency guard: prevent duplicate submissions if user draws two annotations quickly\n let submitting = false;\n const unsubAnnotation = bus.on(\"annotation:complete\", async (data) => {\n if (submitting) return;\n submitting = true;\n try {\n const { annotation, type, message } = data;\n\n // Ensure identity\n let identity = getIdentity();\n if (!identity) {\n identity = await promptIdentity(shadow, t);\n if (!identity) return; // User cancelled\n saveIdentity(identity);\n }\n\n // Sanitize URL — strip sensitive query params before sending\n const rawUrl = new URL(window.location.href);\n for (const key of [...rawUrl.searchParams.keys()]) {\n if (/token|key|secret|auth|session|password|code/i.test(key)) {\n rawUrl.searchParams.delete(key);\n }\n }\n const sanitizedUrl = rawUrl.toString();\n\n // crypto.randomUUID() throws in non-secure contexts (plain HTTP)\n const clientId = (() => {\n try {\n return crypto.randomUUID();\n } catch {\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n }\n })();\n\n const payload: FeedbackPayload = {\n projectName: config.projectName,\n type,\n message,\n url: sanitizedUrl,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n userAgent: navigator.userAgent,\n authorName: identity.name,\n authorEmail: identity.email,\n annotations: [annotation],\n clientId,\n };\n\n try {\n const response = await client.sendFeedback(payload);\n bus.emit(\"feedback:sent\", response);\n markers.addFeedback(response, markers.count + 1);\n liveRegion.textContent = t(\"feedback.sent.confirmation\");\n await panel.refresh();\n } catch (error) {\n bus.emit(\"feedback:error\", error instanceof Error ? error : new Error(String(error)));\n liveRegion.textContent = t(\"feedback.error.message\");\n }\n } finally {\n submitting = false;\n }\n });\n\n // Load markers immediately on page load\n client\n .getFeedbacks(config.projectName, { limit: 50 })\n .then(({ feedbacks }) => {\n markers.render(feedbacks);\n })\n .catch((err) => {\n log(\"Failed to load initial markers:\", err);\n });\n\n // Flush retry queue on load (HTTP mode only — store mode has no retry queue)\n if (config.endpoint) {\n flushRetryQueue(config.endpoint)\n .then(() => log(\"Retry queue flushed\"))\n .catch(() => {});\n }\n\n instance = {\n destroy: () => {\n log(\"Destroying widget\");\n unsubAnnotation();\n fab.destroy();\n panel.destroy();\n annotator.destroy();\n markers.destroy();\n tooltip.destroy();\n bus.removeAll();\n publicBus.removeAll();\n liveRegion.remove();\n host.remove();\n instance = null;\n },\n open: () => {\n panel.open();\n },\n close: () => {\n panel.close();\n },\n refresh: () => {\n panel.refresh();\n },\n on: <K extends keyof SitepingPublicEvents>(event: K, listener: (...args: SitepingPublicEvents[K]) => void) => {\n // Safe cast: SitepingPublicEvents and PublicWidgetEvents have identical keys and value types\n type TargetKey = K & keyof PublicWidgetEvents;\n return publicBus.on(event as TargetKey, listener as unknown as (...args: PublicWidgetEvents[TargetKey]) => void);\n },\n off: <K extends keyof SitepingPublicEvents>(event: K, listener: (...args: SitepingPublicEvents[K]) => void) => {\n // Safe cast: SitepingPublicEvents and PublicWidgetEvents have identical keys and value types\n type TargetKey = K & keyof PublicWidgetEvents;\n publicBus.off(event as TargetKey, listener as unknown as (...args: PublicWidgetEvents[TargetKey]) => void);\n },\n };\n\n return instance;\n}\n\n/**\n * Show a modal identity form inside the Shadow DOM.\n * Glassmorphism: frosted backdrop, glass modal, gradient CTA.\n * Returns null if the user cancels.\n */\nfunction promptIdentity(shadowRoot: ShadowRoot, t: TFunction): Promise<Identity | null> {\n return new Promise((resolve) => {\n // Save the currently focused element to restore on close\n const previouslyFocused = (shadowRoot.activeElement ?? document.activeElement) as HTMLElement | null;\n\n const backdrop = document.createElement(\"div\");\n backdrop.style.cssText = `\n position:fixed;inset:0;\n background:var(--sp-identity-overlay);\n backdrop-filter:blur(8px);\n -webkit-backdrop-filter:blur(8px);\n display:flex;align-items:center;justify-content:center;\n z-index:2147483647;\n opacity:0;transition:opacity 0.25s ease;\n `;\n\n const modal = document.createElement(\"div\");\n modal.style.cssText = `\n width:340px;padding:28px;border-radius:var(--sp-radius-xl);\n background:var(--sp-identity-bg);\n backdrop-filter:blur(var(--sp-blur-heavy));\n -webkit-backdrop-filter:blur(var(--sp-blur-heavy));\n border:1px solid var(--sp-glass-border);\n box-shadow:0 16px 48px var(--sp-shadow), 0 8px 16px var(--sp-shadow);\n font-family:var(--sp-font, \"Inter\",system-ui,-apple-system,sans-serif);\n color:var(--sp-text);\n transform:translateY(12px) scale(0.97);\n transition:transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n -webkit-font-smoothing:antialiased;\n `;\n\n const titleId = `sp-identity-title-${Date.now()}`;\n modal.setAttribute(\"role\", \"dialog\");\n modal.setAttribute(\"aria-modal\", \"true\");\n modal.setAttribute(\"aria-labelledby\", titleId);\n\n const title = document.createElement(\"div\");\n title.className = \"sp-identity-title\";\n title.id = titleId;\n title.textContent = t(\"identity.title\");\n title.style.marginBottom = \"20px\";\n\n const nameInputId = `sp-identity-name-${Date.now()}`;\n const emailInputId = `sp-identity-email-${Date.now()}`;\n\n const nameLabel = document.createElement(\"label\");\n nameLabel.className = \"sp-input-label\";\n nameLabel.textContent = t(\"identity.nameLabel\");\n nameLabel.setAttribute(\"for\", nameInputId);\n const nameInput = document.createElement(\"input\");\n nameInput.className = \"sp-input\";\n nameInput.id = nameInputId;\n nameInput.type = \"text\";\n nameInput.placeholder = t(\"identity.namePlaceholder\");\n nameInput.style.marginBottom = \"14px\";\n\n const emailLabel = document.createElement(\"label\");\n emailLabel.className = \"sp-input-label\";\n emailLabel.textContent = t(\"identity.emailLabel\");\n emailLabel.setAttribute(\"for\", emailInputId);\n const emailInput = document.createElement(\"input\");\n emailInput.className = \"sp-input\";\n emailInput.id = emailInputId;\n emailInput.type = \"email\";\n emailInput.placeholder = t(\"identity.emailPlaceholder\");\n\n const btnRow = document.createElement(\"div\");\n btnRow.style.cssText = \"display:flex;gap:8px;justify-content:flex-end;margin-top:20px;\";\n\n const closeModal = (result: Identity | null) => {\n backdrop.removeEventListener(\"keydown\", onKeydown);\n backdrop.style.opacity = \"0\";\n modal.style.transform = \"translateY(12px) scale(0.97)\";\n setTimeout(() => {\n backdrop.remove();\n previouslyFocused?.focus();\n resolve(result);\n }, 250);\n };\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.className = \"sp-btn-ghost\";\n cancelBtn.textContent = t(\"identity.cancel\");\n cancelBtn.addEventListener(\"click\", () => closeModal(null));\n\n const submitBtn = document.createElement(\"button\");\n submitBtn.className = \"sp-btn-primary\";\n submitBtn.textContent = t(\"identity.submit\");\n submitBtn.addEventListener(\"click\", () => {\n const name = nameInput.value.trim();\n const email = emailInput.value.trim();\n if (!name || !email) return;\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(email)) {\n emailInput.style.borderColor = \"var(--sp-type-bug, #ef4444)\";\n return;\n }\n closeModal({ name, email });\n });\n\n // Focus trap: cycle Tab/Shift+Tab within the modal\n const focusableSelectors = 'input, button, [tabindex]:not([tabindex=\"-1\"])';\n const onKeydown = (e: Event) => {\n const ke = e as KeyboardEvent;\n if (ke.key === \"Escape\") {\n closeModal(null);\n return;\n }\n if (ke.key === \"Tab\") {\n const focusableEls = Array.from(modal.querySelectorAll<HTMLElement>(focusableSelectors));\n if (focusableEls.length === 0) return;\n const first = focusableEls[0];\n const last = focusableEls[focusableEls.length - 1];\n if (!first || !last) return;\n const active = shadowRoot.activeElement as HTMLElement | null;\n if (ke.shiftKey) {\n if (active === first || !modal.contains(active)) {\n ke.preventDefault();\n last.focus();\n }\n } else {\n if (active === last || !modal.contains(active)) {\n ke.preventDefault();\n first.focus();\n }\n }\n }\n };\n backdrop.addEventListener(\"keydown\", onKeydown);\n\n // Close on backdrop click\n backdrop.addEventListener(\"click\", (e) => {\n if (e.target === backdrop) closeModal(null);\n });\n\n btnRow.appendChild(cancelBtn);\n btnRow.appendChild(submitBtn);\n\n modal.appendChild(title);\n modal.appendChild(nameLabel);\n modal.appendChild(nameInput);\n modal.appendChild(emailLabel);\n modal.appendChild(emailInput);\n modal.appendChild(btnRow);\n backdrop.appendChild(modal);\n\n shadowRoot.appendChild(backdrop);\n\n // Animate in\n requestAnimationFrame(() => {\n backdrop.style.opacity = \"1\";\n modal.style.transform = \"translateY(0) scale(1)\";\n nameInput.focus();\n });\n });\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["index_exports","__export","initSiteping","config","rootDocument","start","finder","input","options","defaults","name","value","findRootDocument","path","bottomUpSearch","optimized","sort","optimize","selector","rootNode","limit","fallback","stack","current","i","elapsedTime","level","maybe","id","attr","classNames","tagName","any","nth","index","dispensableNth","node","nthChild","findUniquePath","paths","combinations","candidate","unique","query","penalty","acc","css","elementId","parent","child","list","notEmpty","a","b","scope","newPath","newPathKey","same","STABLE_ATTRS","djb2","str","hash","i","generateFingerprint","element","childCount","siblingIdx","parent","child","attrs","attr","val","attrHash","scoreFingerprint","candidate","storedFingerprint","parts","storedChildren","storedSibIdx","storedAttrHash","storedChildCount","storedSibIndex","candidateFp","candChildren","candSibIdx","candAttrHash","score","childDiff","sibDiff","adjacentText","element","direction","prop","sibling","attempts","text","neighborText","prev","next","generateXPath","element","safeId","segments","current","tag","parent","position","sibling","generateAnchor","element","cssSelector","finder","name","xpath","generateXPath","textSnippet","textPrefix","adjacentText","textSuffix","fingerprint","generateFingerprint","neighbor","neighborText","findAnchorElement","rect","root","centerX","centerY","elementAtCenter","candidate","current","bounds","rectToPercentages","anchorBounds","parseSvg","svgString","svg","attr","el","tag","attrs","element","key","value","setText","text","formatRelativeDate","isoString","locale","diff","seconds","rtf","minutes","hours","days","ICON_SITEPING","ICON_CHAT","ICON_ANNOTATE","ICON_EYE","ICON_EYE_OFF","ICON_CLOSE","ICON_SEARCH","ICON_CHECK","ICON_QUESTION","ICON_CHANGE","ICON_BUG","ICON_OTHER","ICON_UNDO","ICON_TRASH","DEFAULT_ACCENT","HEX6_RE","HEX3_RE","HEX8_RE","normalizeHex","raw","short","darkenHex","hex","amount","r","g","b","prefersDark","resolveTheme","theme","buildThemeColors","accent","dark","getTypeColor","type","colors","getTypeBgColor","cssVariables","Popup","colors","t","el","typeOptions","ICON_QUESTION","ICON_CHANGE","ICON_BUG","ICON_OTHER","typeRow","option","btn","icon","parseSvg","labelSpan","setText","bgColor","getTypeBgColor","getTypeColor","hint","uaData","isMac","e","btnRow","cancelBtn","rectBounds","resolve","top","left","focusableEls","first","last","reduceMotion","type","container","buttons","isActive","color","enabled","Annotator","colors","bus","t","Popup","el","dot","style","instruction","setText","cancelBtn","target","bounds","rectBounds","result","annotation","generateAnchor","touch","clientX","clientY","source","evt","x","y","w","h","anchorElement","findAnchorElement","anchor","anchorBounds","rect","rectToPercentages","RETRY_QUEUE_KEY","resilientFetch","url","init","retries","attempt","controller","timeout","response","error","baseDelay","jitter","r","queueForRetry","endpoint","payload","raw","RETRY_QUEUE_KEY","parsed","queue","flushRetryQueue","toRetry","e","failed","entry","remaining","ApiClient","projectName","text","options","params","id","resolved","EventBus","event","listener","set","args","fn","err","ITEM_GAP","Fab","shadowRoot","config","bus","t","position","isRight","ICON_CHAT","ICON_ANNOTATE","ICON_EYE","ICON_EYE_OFF","parseSvg","ICON_SITEPING","i","item","btn","e","label","host","handleEscape","items","activeEl","currentIndex","nextIndex","count","displayText","setText","ICON_CLOSE","y","svgStr","badge","id","en","fr","LOCALES","fr","en","createT","locale","lang","LOCALES","dict","key","getTypeLabel","type","t","STORAGE_KEY","getIdentity","raw","parsed","identity","saveIdentity","editDistance","a","b","t","aLen","bLen","prev","k","curr","j","i","prevDiag","tmp","similarity","maxLen","fuzzyIncludes","haystack","needle","minScore","nLen","score","best","capped","limit","window","MAX_SCAN_CANDIDATES","TEXT_MATCH_THRESHOLD","textMatches","el","anchor","text","fuzzyIncludes","resolveAnchor","smartScan","tag","candidates","bestElement","bestScore","limit","i","score","scoreCandidate","candidate","totalWeight","candidateText","scoreFingerprint","contextScore","contextParts","prevText","adjacentText","similarity","nextText","candidateNeighbor","neighborText","resolveAnnotation","rect","resolution","bounds","absoluteRect","toAnchorData","a","toRectData","MARKER_OFFSET","markerPosition","rect","clusterMarker","cluster","i","entry","elIdx","HIGHLIGHT_FADE","REPOSITION_DEBOUNCE","LOW_CONFIDENCE_THRESHOLD","CLUSTER_DISTANCE","FAN_SPACING","MarkerManager","colors","tooltip","bus","t","el","visible","mutations","hasRelevantMutation","m","e","markerEl","annotation","cacheKey","cachedEl","resolved","anchorRect","r","resolveAnnotation","pos","feedbacks","feedback","index","marker","badge","allItems","used","itemI","j","itemJ","b","first","baseTop","baseLeft","isSolo","count","totalWidth","startLeft","topMarker","setText","confidence","isResolved","number","typeColor","getTypeColor","truncatedMessage","ariaLabel","getTypeLabel","activateMarker","feedbackId","highlight","h","Panel","shadowRoot","colors","bus","client","projectName","markers","t","locale","el","header","title","setText","parseSvg","ICON_CLOSE","ICON_TRASH","deleteAllLabel","headerRight","filters","searchWrap","searchIcon","ICON_SEARCH","chips","chipOptions","option","chip","getTypeColor","e","target","actionEl","card","feedbackId","feedback","f","action","message","isExpanded","btn","ann","ke","open","focusable","first","last","active","loading","spinner","empty","text","retryBtn","signal","search","typeFilter","options","hasContent","feedbacks","error","emptyText","index","number","isResolved","typeColor","getTypeLabel","bar","body","num","badge","typeBg","getTypeBgColor","date","formatRelativeDate","expandBtn","footer","resolveBtn","ICON_UNDO","span","ICON_CHECK","deleteBtn","deleteLabel","resolve","backdrop","titleId","messageId","dialog","titleEl","messageEl","btnRow","cancelBtn","confirmBtn","closed","close","result","onKeydown","newResolved","value","container","isActive","escapedId","flattenAnnotation","ann","StoreClient","store","projectName","payload","record","flattenAnnotation","toResponse","options","feedbacks","total","id","resolved","toAnnotationResponse","ann","SPRING_LINEAR","EASE_OUT_EXPO","SPRING_OVERSHOOT","EASE_OUT_QUART","ANIMATION_CSS","buildStyles","colors","cssVariables","ANIMATION_CSS","SHOW_DELAY","HIDE_DELAY","Tooltip","colors","locale","el","feedback","anchorRect","reduceMotion","children","child","typeColor","getTypeColor","typeBg","getTypeBgColor","t","createT","typeLabel","getTypeLabel","header","badge","setText","date","formatRelativeDate","body","tooltipRect","gap","top","left","isAbove","arrowLeft","node","instance","skippedInstance","noop","launch","config","log","args","reason","locale","t","createT","colors","buildThemeColors","bus","EventBus","publicBus","client","StoreClient","ApiClient","fb","id","err","host","isTestEnv","shadowMode","shadow","sheet","buildStyles","style","liveRegion","tooltip","Tooltip","markers","MarkerManager","fab","Fab","panel","Panel","annotator","Annotator","submitting","unsubAnnotation","data","annotation","type","message","identity","getIdentity","promptIdentity","saveIdentity","rawUrl","key","sanitizedUrl","clientId","payload","response","error","feedbacks","flushRetryQueue","event","listener","shadowRoot","resolve","previouslyFocused","backdrop","modal","titleId","title","nameInputId","emailInputId","nameLabel","nameInput","emailLabel","emailInput","btnRow","closeModal","result","onKeydown","cancelBtn","submitBtn","name","email","focusableSelectors","e","ke","focusableEls","first","last","active","initSiteping","config","launch"]}