@qaiddev/thumbs-embed 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{annotate-pSxJNPPL.js → annotate-NeITyYNe.js} +4 -9
- package/dist/chunks/{annotate-pSxJNPPL.js.map → annotate-NeITyYNe.js.map} +1 -1
- package/dist/chunks/{bootstrap-rINL2pCt.js → loader-DnJ3MHkt.js} +137 -127
- package/dist/chunks/loader-DnJ3MHkt.js.map +1 -0
- package/dist/chunks/{modal-Br-8TcEE.js → modal-C8Ca7E5j.js} +3 -3
- package/dist/chunks/{modal-Br-8TcEE.js.map → modal-C8Ca7E5j.js.map} +1 -1
- package/dist/chunks/{recording-BRvu1Vkh.js → recording-DhRZ6-tW.js} +133 -74
- package/dist/chunks/recording-DhRZ6-tW.js.map +1 -0
- package/dist/chunks/{targeting-_aii7qgw.js → targeting-BEQU2Ogo.js} +2 -2
- package/dist/chunks/{targeting-_aii7qgw.js.map → targeting-BEQU2Ogo.js.map} +1 -1
- package/dist/index.js +2569 -19
- package/dist/index.js.map +1 -1
- package/dist/loader.js +2 -4
- package/dist/loader.js.map +1 -1
- package/dist/qaid.umd.cjs +3 -3
- package/dist/qaid.umd.cjs.map +1 -1
- package/package.json +2 -2
- package/dist/chunks/bootstrap-rINL2pCt.js.map +0 -1
- package/dist/chunks/network-capture-DMbMwwwR.js +0 -63
- package/dist/chunks/network-capture-DMbMwwwR.js.map +0 -1
- package/dist/chunks/recording-BRvu1Vkh.js.map +0 -1
|
@@ -401,19 +401,14 @@ const Y = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
|
401
401
|
rectFromPoints: u
|
|
402
402
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
403
403
|
export {
|
|
404
|
-
f as A,
|
|
405
404
|
H as F,
|
|
406
405
|
$ as R,
|
|
407
406
|
j as T,
|
|
408
407
|
W as a,
|
|
409
408
|
z as b,
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
K as e,
|
|
413
|
-
Y as f,
|
|
409
|
+
K as c,
|
|
410
|
+
Y as d,
|
|
414
411
|
V as g,
|
|
415
|
-
F as i
|
|
416
|
-
U as o,
|
|
417
|
-
u as r
|
|
412
|
+
F as i
|
|
418
413
|
};
|
|
419
|
-
//# sourceMappingURL=annotate-
|
|
414
|
+
//# sourceMappingURL=annotate-NeITyYNe.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotate-pSxJNPPL.js","sources":["../../src/icons.ts","../../src/dom-utils.ts","../../src/annotate.ts"],"sourcesContent":["/**\n * SVG icon strings for thumbs up/down buttons\n */\n\nexport const THUMBS_UP_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5\"/>\n</svg>`;\n\nexport const THUMBS_DOWN_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.737 3h4.018a2 2 0 01.485.06l3.76.94m-7 10v5a2 2 0 002 2h.095c.5 0 .905-.405.905-.905 0-.714.211-1.412.608-2.006L17 13V4m-7 10h2m5-10h2a2 2 0 012 2v6a2 2 0 01-2 2h-2.5\"/>\n</svg>`;\n\nexport const RECORD_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z\"/>\n</svg>`;\n\n/** Chat-bubble icon for the single, sentiment-free \"Feedback\" button. */\nexport const FEEDBACK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8 10h8M8 14h5m-9 6l3.5-3.5H18a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v14z\"/>\n</svg>`;\n\n/* ---------------------------------------------------------------------------\n * Screenshot annotation toolbar icons. Same 24×24 stroked style as the thumbs\n * icons above so they inherit `.qaid-icon` sizing and `currentColor`.\n * ------------------------------------------------------------------------- */\n\nexport const RECTANGLE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"4\" y=\"6\" width=\"16\" height=\"12\" rx=\"1\" stroke-linejoin=\"round\"/>\n</svg>`;\n\nexport const ARROW_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 19L19 5m0 0h-8m8 0v8\"/>\n</svg>`;\n\nexport const PENCIL_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 20h9M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z\"/>\n</svg>`;\n\nexport const BLUR_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 3s6 6.4 6 10a6 6 0 01-12 0c0-3.6 6-10 6-10z\"/>\n</svg>`;\n\nexport const UNDO_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-4\"/>\n</svg>`;\n\nexport const DONE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20 6L9 17l-5-5\"/>\n</svg>`;\n\nexport const SKIP_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 6l12 12M18 6L6 18\"/>\n</svg>`;\n","/**\n * DOM utility functions\n */\n\n/**\n * Create an element with attributes and optional children\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string)[]\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"className\") {\n el.className = value;\n } else {\n el.setAttribute(key, value);\n }\n }\n }\n\n if (children) {\n for (const child of children) {\n if (typeof child === \"string\") {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n }\n\n return el;\n}\n\n/**\n * Check if viewport width indicates mobile device\n */\nexport function isMobileViewport(breakpoint = 640): boolean {\n return typeof window !== \"undefined\" && window.innerWidth < breakpoint;\n}\n\n/**\n * Get element at point, temporarily hiding shadow hosts so elementFromPoint\n * sees through to the page elements underneath.\n * Uses visibility:hidden which skips the element in elementFromPoint without\n * triggering reflow.\n */\nexport function getElementAtPointUnderOverlay(\n x: number,\n y: number,\n hosts: HTMLElement[]\n): Element | null {\n const saved = hosts.map((h) => h.style.visibility);\n hosts.forEach((h) => (h.style.visibility = \"hidden\"));\n const element = document.elementFromPoint(x, y);\n hosts.forEach((h, i) => (h.style.visibility = saved[i]));\n return element;\n}\n\n/**\n * Check if an element is part of the feedback embed\n * With shadow DOM, embed elements are inside shadow hosts marked with\n * data-qaid-embed or data-qaid-embed-overlay\n */\nexport function isEmbedElement(element: Element | null): boolean {\n if (!element) return false;\n // Check if the element itself is a shadow host\n if (element.hasAttribute(\"data-qaid-embed\") || element.hasAttribute(\"data-qaid-embed-overlay\")) return true;\n // Check ancestors for shadow host\n return !!element.closest(\"[data-qaid-embed], [data-qaid-embed-overlay]\");\n}\n\n/**\n * Get element bounds with optional padding\n */\nexport function getElementBounds(\n element: Element,\n padding = 0\n): { x: number; y: number; width: number; height: number } {\n const rect = element.getBoundingClientRect();\n return {\n x: rect.left - padding,\n y: rect.top - padding,\n width: rect.width + padding * 2,\n height: rect.height + padding * 2,\n };\n}\n\n/**\n * Remove all elements with a specific class\n */\nexport function removeAllByClass(className: string): void {\n document.querySelectorAll(`.${className}`).forEach((el) => {\n el.classList.remove(className);\n });\n}\n","/**\n * Screenshot annotation / markup editor for the thumbs embed.\n *\n * After a screenshot is captured the user can draw on it — rectangles, arrows,\n * a freehand pen, and a blur/redact box — in a full-screen, focus-trapped,\n * WCAG 2.2 AA dialog rendered inside the embed's overlay shadow root. On \"Done\"\n * the base image and the vector shapes are composited into a fresh WebP data\n * URL (so downstream code is unchanged); on \"Skip\"/Escape the original is kept.\n *\n * The geometry and compositing are kept as pure-ish exported functions\n * (`drawShape`, `rectFromPoints`, `compositeAnnotations`) so they can be tested\n * without a real rendering engine — happy-dom has no raster, so tests assert on\n * spied canvas-context calls rather than pixels. The {@link AnnotationEditor}\n * controller keeps the DOM wiring thin and its interactive methods are public\n * so they can be driven directly in tests.\n */\n\nimport { createElement } from \"./dom-utils\";\nimport {\n RECTANGLE_ICON,\n ARROW_ICON,\n PENCIL_ICON,\n BLUR_ICON,\n UNDO_ICON,\n DONE_ICON,\n SKIP_ICON,\n} from \"./icons\";\n\n/** The four drawing tools. */\nexport type ShapeType = \"rect\" | \"arrow\" | \"pen\" | \"blur\";\n\n/** A point in the screenshot's natural-pixel coordinate space. */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * A single vector annotation. `rect`, `arrow` and `blur` use `points[0]` as the\n * drag start and `points[1]` as the drag end; `pen` uses every point in order.\n */\nexport interface Shape {\n type: ShapeType;\n color: string;\n strokeWidth: number;\n points: Point[];\n}\n\n/** Tile size (natural px) for the mosaic drawn by the blur/redact tool. */\nconst BLUR_TILE = 12;\n\n/** Default stroke colour: a high-visibility red that reads on most screenshots. */\nconst DEFAULT_STROKE = \"#ef4444\";\n/** Default swatch palette when the embed doesn't supply one. */\nconst DEFAULT_PALETTE = [\"#ef4444\", \"#f59e0b\", \"#22c55e\", \"#3b82f6\", \"#111827\", \"#ffffff\"];\n/** Friendly names for common swatches (for accessible labels). */\nconst COLOR_NAMES: Record<string, string> = {\n \"#ef4444\": \"red\",\n \"#f59e0b\": \"amber\",\n \"#22c55e\": \"green\",\n \"#3b82f6\": \"blue\",\n \"#111827\": \"black\",\n \"#ffffff\": \"white\",\n \"#6366f1\": \"indigo\",\n};\n/** Accessible name for a swatch colour — a friendly name, else the hex. */\nfunction colorName(hex: string): string {\n return COLOR_NAMES[hex.toLowerCase()] ?? hex;\n}\n/** Default stroke width in natural pixels. */\nconst DEFAULT_STROKE_WIDTH = 4;\n/** Default WebP quality for the re-encoded composite. */\nconst DEFAULT_QUALITY = 0.8;\n\n/** Human-readable labels for the toolbar / announcements (all overridable). */\nexport interface AnnotationLabels {\n title: string;\n instructions: string;\n rectangle: string;\n arrow: string;\n pen: string;\n blur: string;\n undo: string;\n skip: string;\n done: string;\n colors: string;\n}\n\nconst DEFAULT_LABELS: AnnotationLabels = {\n title: \"Annotate screenshot\",\n instructions:\n \"Draw on the screenshot to highlight or hide details, then choose Done to attach it or Skip to send the original.\",\n rectangle: \"Rectangle\",\n arrow: \"Arrow\",\n pen: \"Pen\",\n blur: \"Blur or redact\",\n undo: \"Undo last\",\n skip: \"Skip annotation\",\n done: \"Done\",\n colors: \"Drawing colour\",\n};\n\n/**\n * Normalise two drag corners into a top-left origin plus positive width/height,\n * regardless of the direction the user dragged.\n */\nexport function rectFromPoints(\n a: Point,\n b: Point\n): { x: number; y: number; w: number; h: number } {\n return {\n x: Math.min(a.x, b.x),\n y: Math.min(a.y, b.y),\n w: Math.abs(a.x - b.x),\n h: Math.abs(a.y - b.y),\n };\n}\n\n/** A drag/shape too small to be a deliberate mark (a click, not a drag). */\nfunction isDegenerate(shape: Shape): boolean {\n if (shape.type === \"pen\") return shape.points.length < 2;\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n return r.w < 3 && r.h < 3;\n}\n\nfunction drawArrow(\n ctx: CanvasRenderingContext2D,\n from: Point,\n to: Point,\n width: number\n): void {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n\n const angle = Math.atan2(to.y - from.y, to.x - from.x);\n const head = Math.max(10, width * 3);\n ctx.beginPath();\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle - Math.PI / 6),\n to.y - head * Math.sin(angle - Math.PI / 6)\n );\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle + Math.PI / 6),\n to.y - head * Math.sin(angle + Math.PI / 6)\n );\n ctx.stroke();\n}\n\nfunction drawPen(ctx: CanvasRenderingContext2D, points: Point[]): void {\n if (points.length === 0) return;\n ctx.beginPath();\n ctx.moveTo(points[0].x, points[0].y);\n for (let i = 1; i < points.length; i++) {\n ctx.lineTo(points[i].x, points[i].y);\n }\n ctx.stroke();\n}\n\n/**\n * Redact a region so the underlying content is GONE from the composite — not\n * merely dimmed. Both fills are fully opaque (no alpha), so nothing bleeds\n * through; the two-tone mosaic just gives it a recognisable \"redacted\" texture.\n * There is no real Gaussian blur (it can't be verified without a raster), and a\n * translucent wash would leave sensitive text faintly legible — so this paints\n * solid.\n */\nfunction drawBlur(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number\n): void {\n ctx.fillStyle = \"rgb(15, 23, 42)\";\n ctx.fillRect(x, y, w, h);\n ctx.fillStyle = \"rgb(37, 47, 63)\";\n for (let ty = y; ty < y + h; ty += BLUR_TILE) {\n for (let tx = x; tx < x + w; tx += BLUR_TILE) {\n const checker =\n (Math.floor((tx - x) / BLUR_TILE) + Math.floor((ty - y) / BLUR_TILE)) %\n 2 ===\n 0;\n if (checker) {\n ctx.fillRect(\n tx,\n ty,\n Math.min(BLUR_TILE, x + w - tx),\n Math.min(BLUR_TILE, y + h - ty)\n );\n }\n }\n }\n}\n\n/**\n * Draw a single shape onto a 2D context in the shape's own (natural-pixel)\n * coordinates. Used both for the live editing preview and for the final\n * composite, so what the user sees is exactly what is submitted.\n */\nexport function drawShape(ctx: CanvasRenderingContext2D, shape: Shape): void {\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = \"round\";\n ctx.lineCap = \"round\";\n\n switch (shape.type) {\n case \"rect\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n ctx.strokeRect(r.x, r.y, r.w, r.h);\n break;\n }\n case \"arrow\":\n drawArrow(ctx, shape.points[0], shape.points[1], shape.strokeWidth);\n break;\n case \"pen\":\n drawPen(ctx, shape.points);\n break;\n case \"blur\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n drawBlur(ctx, r.x, r.y, r.w, r.h);\n break;\n }\n }\n\n ctx.restore();\n}\n\n/** Load an image from a data URL. Resolves with the element once decoded. */\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = (): void => resolve(img);\n img.onerror = (): void => reject(new Error(\"Failed to load screenshot\"));\n img.src = src;\n });\n}\n\n/**\n * Composite the base screenshot and the drawn shapes into a new WebP data URL\n * at the image's natural size. Falls back to the original data URL if the image\n * can't be loaded or a 2D context isn't available (so a submission is never\n * lost). This is intentionally standalone so it can be unit-tested in isolation.\n */\nexport async function compositeAnnotations(\n dataUrl: string,\n shapes: Shape[],\n opts: { quality?: number } = {}\n): Promise<string> {\n const quality = opts.quality ?? DEFAULT_QUALITY;\n\n let img: HTMLImageElement;\n try {\n img = await loadImage(dataUrl);\n } catch {\n return dataUrl;\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.naturalWidth || img.width || 1;\n canvas.height = img.naturalHeight || img.height || 1;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return dataUrl;\n\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n for (const shape of shapes) {\n drawShape(ctx, shape);\n }\n\n return canvas.toDataURL(\"image/webp\", quality);\n}\n\n/** Options for {@link AnnotationEditor} / {@link openAnnotationEditor}. */\nexport interface AnnotationEditorOptions {\n /** The captured screenshot as a WebP data URL. */\n dataUrl: string;\n /** Shadow root (or element) the editor is mounted into. */\n root: ShadowRoot | HTMLElement;\n /** WebP quality for the re-encoded composite. */\n quality?: number;\n /** Default stroke colour for rectangle/arrow/pen. */\n color?: string;\n /** Colour swatches offered in the toolbar. The active `color` is ensured present. */\n palette?: string[];\n /** Default stroke width. */\n strokeWidth?: number;\n /** Label overrides. */\n labels?: Partial<AnnotationLabels>;\n /** Apply the embed's per-instance `--qaid-*` CSS variables to the container. */\n applyVars?: (el: HTMLElement) => void;\n /** Announce a message via the embed's live regions. */\n announce?: (message: string, assertive?: boolean) => void;\n /** Turn the container into an accessible modal dialog (focus trap + inert). */\n openDialog?: (\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string }\n ) => void;\n /** Tear down the dialog semantics and restore focus. */\n closeDialog?: () => void;\n}\n\nlet editorSeq = 0;\n\n/**\n * Full-screen screenshot annotation editor. Construct it, call {@link open}\n * (which returns a promise resolving to the composited WebP, or `null` when the\n * user skips), and the controller manages its own DOM and teardown.\n */\nexport class AnnotationEditor {\n readonly shapes: Shape[] = [];\n tool: ShapeType = \"rect\";\n\n private readonly opts: AnnotationEditorOptions;\n private readonly labels: AnnotationLabels;\n private color: string;\n private readonly palette: string[];\n private readonly strokeWidth: number;\n private readonly uid = `qaid-annotate-${++editorSeq}`;\n\n private container!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D | null = null;\n private toolbar!: HTMLDivElement;\n private undoBtn!: HTMLButtonElement;\n private img: HTMLImageElement | null = null;\n\n private drawing = false;\n private current: Shape | null = null;\n private settled = false;\n // Assigned in open() before any settle() is reachable (done/skip only fire\n // after the editor is mounted).\n private resolveResult!: (value: string | null) => void;\n\n constructor(opts: AnnotationEditorOptions) {\n this.opts = opts;\n this.labels = { ...DEFAULT_LABELS, ...(opts.labels ?? {}) };\n this.color = opts.color ?? DEFAULT_STROKE;\n this.palette = (opts.palette && opts.palette.length ? opts.palette : DEFAULT_PALETTE).slice();\n // Ensure the active colour is offered as a swatch so it stays selectable.\n if (!this.palette.some((c) => c.toLowerCase() === this.color.toLowerCase())) {\n this.palette.unshift(this.color);\n }\n this.strokeWidth = opts.strokeWidth ?? DEFAULT_STROKE_WIDTH;\n }\n\n /** Mount the editor and resolve when the user commits (Done) or skips. */\n open(): Promise<string | null> {\n const promise = new Promise<string | null>((resolve) => {\n this.resolveResult = resolve;\n });\n this.mount();\n return promise;\n }\n\n private toolButton(tool: ShapeType, icon: string, label: string): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-tool\",\n \"data-qaid-tool\": tool,\n \"aria-pressed\": tool === this.tool ? \"true\" : \"false\",\n \"aria-label\": `${label} tool`,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n /** A round colour swatch button for the toolbar's colour group. */\n private swatchButton(color: string): HTMLButtonElement {\n const name = colorName(color);\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-swatch\",\n \"data-qaid-color\": color,\n \"aria-pressed\": color.toLowerCase() === this.color.toLowerCase() ? \"true\" : \"false\",\n \"aria-label\": `Draw in ${name}`,\n title: name,\n });\n btn.style.setProperty(\"--qaid-swatch\", color);\n return btn;\n }\n\n private actionButton(\n action: string,\n icon: string,\n label: string,\n extraClass: string\n ): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: `qaid-annotate-action ${extraClass}`,\n \"data-qaid-action\": action,\n \"aria-label\": label,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n private mount(): void {\n const titleId = `${this.uid}-title`;\n const descId = `${this.uid}-desc`;\n\n this.container = createElement(\"div\", { class: \"qaid-annotate\" });\n this.opts.applyVars?.(this.container);\n\n const title = createElement(\"h2\", {\n id: titleId,\n class: \"qaid-annotate-title\",\n });\n title.textContent = this.labels.title;\n\n const desc = createElement(\"p\", {\n id: descId,\n class: \"qaid-annotate-desc\",\n });\n desc.textContent = this.labels.instructions;\n\n const stage = createElement(\"div\", { class: \"qaid-annotate-stage\" });\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"qaid-annotate-canvas\";\n this.canvas.width = 1;\n this.canvas.height = 1;\n this.canvas.setAttribute(\"role\", \"img\");\n this.canvas.setAttribute(\"aria-label\", this.labels.title);\n this.ctx = this.canvas.getContext(\"2d\");\n stage.appendChild(this.canvas);\n\n this.toolbar = createElement(\"div\", {\n class: \"qaid-annotate-toolbar\",\n role: \"toolbar\",\n \"aria-label\": this.labels.title,\n });\n\n const tools = createElement(\"div\", { class: \"qaid-annotate-tools\" });\n tools.appendChild(this.toolButton(\"rect\", RECTANGLE_ICON, this.labels.rectangle));\n tools.appendChild(this.toolButton(\"arrow\", ARROW_ICON, this.labels.arrow));\n tools.appendChild(this.toolButton(\"pen\", PENCIL_ICON, this.labels.pen));\n tools.appendChild(this.toolButton(\"blur\", BLUR_ICON, this.labels.blur));\n\n const colors = createElement(\"div\", {\n class: \"qaid-annotate-colors\",\n role: \"group\",\n \"aria-label\": this.labels.colors,\n });\n for (const c of this.palette) colors.appendChild(this.swatchButton(c));\n\n this.undoBtn = this.actionButton(\"undo\", UNDO_ICON, this.labels.undo, \"qaid-annotate-undo\");\n this.undoBtn.disabled = true;\n\n const skipBtn = this.actionButton(\"skip\", SKIP_ICON, this.labels.skip, \"qaid-annotate-skip\");\n const doneBtn = this.actionButton(\"done\", DONE_ICON, this.labels.done, \"qaid-annotate-done\");\n\n const actions = createElement(\"div\", { class: \"qaid-annotate-actions\" });\n actions.appendChild(this.undoBtn);\n actions.appendChild(skipBtn);\n actions.appendChild(doneBtn);\n\n this.toolbar.appendChild(tools);\n this.toolbar.appendChild(colors);\n this.toolbar.appendChild(actions);\n\n this.container.appendChild(title);\n this.container.appendChild(desc);\n this.container.appendChild(stage);\n this.container.appendChild(this.toolbar);\n this.opts.root.appendChild(this.container);\n\n this.toolbar.addEventListener(\"click\", this.onToolbarClick);\n this.canvas.addEventListener(\"pointerdown\", this.onPointerDown);\n this.container.addEventListener(\"pointermove\", this.onPointerMove);\n this.container.addEventListener(\"pointerup\", this.onPointerUp);\n this.container.addEventListener(\"keydown\", this.onKeyDown);\n\n this.opts.openDialog?.(this.container, {\n labelledbyId: titleId,\n describedbyId: descId,\n });\n this.opts.announce?.(this.labels.instructions);\n\n loadImage(this.opts.dataUrl)\n .then((img) => {\n this.img = img;\n this.canvas.width = img.naturalWidth || img.width || 1;\n this.canvas.height = img.naturalHeight || img.height || 1;\n this.redraw();\n })\n .catch(() => {\n this.img = null;\n this.redraw();\n });\n }\n\n /** Convert a pointer event's client coordinates into natural image pixels. */\n private toImageCoords(e: { clientX: number; clientY: number }): Point {\n const rect = this.canvas.getBoundingClientRect();\n const scaleX = this.canvas.width / (rect.width || this.canvas.width);\n const scaleY = this.canvas.height / (rect.height || this.canvas.height);\n return {\n x: (e.clientX - rect.left) * scaleX,\n y: (e.clientY - rect.top) * scaleY,\n };\n }\n\n /** Repaint the display canvas: base image (if loaded) then every shape. */\n redraw(): void {\n const ctx = this.ctx;\n if (!ctx) return;\n ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n if (this.img) {\n ctx.drawImage(this.img, 0, 0, this.canvas.width, this.canvas.height);\n }\n for (const shape of this.shapes) {\n drawShape(ctx, shape);\n }\n if (this.current) {\n drawShape(ctx, this.current);\n }\n }\n\n /** Select the active drawing tool and reflect it on the toolbar. */\n selectTool(tool: ShapeType): void {\n this.tool = tool;\n const buttons = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-tool]\");\n buttons.forEach((btn) => {\n btn.setAttribute(\n \"aria-pressed\",\n btn.getAttribute(\"data-qaid-tool\") === tool ? \"true\" : \"false\"\n );\n });\n const label = this.labels[LABEL_KEY[tool]];\n this.opts.announce?.(`${label} tool selected`);\n }\n\n /** Set the active drawing colour; subsequent shapes use it. */\n selectColor(color: string): void {\n this.color = color;\n const swatches = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-color]\");\n swatches.forEach((s) => {\n s.setAttribute(\n \"aria-pressed\",\n (s.getAttribute(\"data-qaid-color\") ?? \"\").toLowerCase() === color.toLowerCase()\n ? \"true\"\n : \"false\"\n );\n });\n this.opts.announce?.(`${colorName(color)} colour selected`);\n }\n\n private onToolbarClick = (e: Event): void => {\n const target = e.target as HTMLElement | null;\n const btn = target?.closest<HTMLElement>(\n \"[data-qaid-tool],[data-qaid-action],[data-qaid-color]\"\n );\n if (!btn) return;\n const color = btn.getAttribute(\"data-qaid-color\");\n if (color) {\n this.selectColor(color);\n return;\n }\n const tool = btn.getAttribute(\"data-qaid-tool\");\n if (tool) {\n this.selectTool(tool as ShapeType);\n return;\n }\n const action = btn.getAttribute(\"data-qaid-action\");\n if (action === \"undo\") this.undo();\n else if (action === \"skip\") this.skip();\n else if (action === \"done\") void this.done();\n };\n\n onPointerDown = (e: PointerEvent): void => {\n e.preventDefault?.();\n this.drawing = true;\n const p = this.toImageCoords(e);\n this.current = {\n type: this.tool,\n color: this.color,\n strokeWidth: this.strokeWidth,\n points: this.tool === \"pen\" ? [p] : [p, { ...p }],\n };\n this.redraw();\n };\n\n onPointerMove = (e: PointerEvent): void => {\n if (!this.drawing || !this.current) return;\n const p = this.toImageCoords(e);\n if (this.current.type === \"pen\") {\n this.current.points.push(p);\n } else {\n this.current.points[1] = p;\n }\n this.redraw();\n };\n\n onPointerUp = (): void => {\n if (!this.drawing || !this.current) return;\n this.drawing = false;\n const shape = this.current;\n this.current = null;\n if (isDegenerate(shape)) {\n this.redraw();\n return;\n }\n this.shapes.push(shape);\n this.undoBtn.disabled = false;\n this.opts.announce?.(\"Annotation added\");\n this.redraw();\n };\n\n onKeyDown = (e: KeyboardEvent): void => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n this.skip();\n return;\n }\n if (e.key === \"Enter\") {\n const target = e.target as HTMLElement | null;\n // Let Enter activate a focused tool/action button natively.\n if (target?.getAttribute(\"data-qaid-tool\") || target?.getAttribute(\"data-qaid-action\")) {\n return;\n }\n e.preventDefault();\n void this.done();\n }\n };\n\n /** Remove the most recently added shape. */\n undo(): void {\n if (this.shapes.length === 0) {\n this.opts.announce?.(\"Nothing to undo\");\n return;\n }\n this.shapes.pop();\n this.undoBtn.disabled = this.shapes.length === 0;\n this.opts.announce?.(\"Removed last annotation\");\n this.redraw();\n }\n\n /** Composite the annotations and resolve with the new WebP data URL. */\n async done(): Promise<void> {\n if (this.settled) return;\n const out = await compositeAnnotations(this.opts.dataUrl, this.shapes, {\n quality: this.opts.quality,\n });\n this.opts.announce?.(\"Annotated screenshot attached\");\n this.settle(out);\n }\n\n /** Discard annotations and resolve with `null` (caller keeps the original). */\n skip(): void {\n if (this.settled) return;\n this.opts.announce?.(\"Annotation skipped\");\n this.settle(null);\n }\n\n private settle(value: string | null): void {\n this.settled = true;\n this.teardown();\n this.resolveResult(value);\n }\n\n private teardown(): void {\n this.toolbar.removeEventListener(\"click\", this.onToolbarClick);\n this.canvas.removeEventListener(\"pointerdown\", this.onPointerDown);\n this.container.removeEventListener(\"pointermove\", this.onPointerMove);\n this.container.removeEventListener(\"pointerup\", this.onPointerUp);\n this.container.removeEventListener(\"keydown\", this.onKeyDown);\n this.opts.closeDialog?.();\n this.container.remove();\n }\n}\n\n/** Maps a tool id to its label key, for announcements. */\nconst LABEL_KEY: Record<ShapeType, keyof AnnotationLabels> = {\n rect: \"rectangle\",\n arrow: \"arrow\",\n pen: \"pen\",\n blur: \"blur\",\n};\n\n/**\n * Open the annotation editor and resolve with the composited WebP data URL, or\n * `null` if the user skipped. Thin entry point used by the embed.\n */\nexport function openAnnotationEditor(\n opts: AnnotationEditorOptions\n): Promise<string | null> {\n return new AnnotationEditor(opts).open();\n}\n"],"names":["THUMBS_UP_ICON","THUMBS_DOWN_ICON","RECORD_ICON","FEEDBACK_ICON","RECTANGLE_ICON","ARROW_ICON","PENCIL_ICON","BLUR_ICON","UNDO_ICON","DONE_ICON","SKIP_ICON","createElement","tag","attrs","children","el","key","value","isMobileViewport","breakpoint","getElementAtPointUnderOverlay","x","y","hosts","saved","h","element","i","isEmbedElement","getElementBounds","padding","rect","BLUR_TILE","DEFAULT_STROKE","DEFAULT_PALETTE","COLOR_NAMES","colorName","hex","DEFAULT_STROKE_WIDTH","DEFAULT_QUALITY","DEFAULT_LABELS","rectFromPoints","a","b","isDegenerate","shape","r","drawArrow","ctx","from","to","width","angle","head","drawPen","points","drawBlur","w","ty","tx","drawShape","loadImage","src","resolve","reject","img","compositeAnnotations","dataUrl","shapes","opts","quality","canvas","editorSeq","AnnotationEditor","c","promise","tool","icon","label","btn","color","name","action","extraClass","titleId","descId","title","desc","stage","tools","colors","skipBtn","doneBtn","actions","e","scaleX","scaleY","LABEL_KEY","s","p","target","out","openAnnotationEditor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"annotate-NeITyYNe.js","sources":["../../src/icons.ts","../../src/dom-utils.ts","../../src/annotate.ts"],"sourcesContent":["/**\n * SVG icon strings for thumbs up/down buttons\n */\n\nexport const THUMBS_UP_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5\"/>\n</svg>`;\n\nexport const THUMBS_DOWN_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.737 3h4.018a2 2 0 01.485.06l3.76.94m-7 10v5a2 2 0 002 2h.095c.5 0 .905-.405.905-.905 0-.714.211-1.412.608-2.006L17 13V4m-7 10h2m5-10h2a2 2 0 012 2v6a2 2 0 01-2 2h-2.5\"/>\n</svg>`;\n\nexport const RECORD_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z\"/>\n</svg>`;\n\n/** Chat-bubble icon for the single, sentiment-free \"Feedback\" button. */\nexport const FEEDBACK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8 10h8M8 14h5m-9 6l3.5-3.5H18a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v14z\"/>\n</svg>`;\n\n/* ---------------------------------------------------------------------------\n * Screenshot annotation toolbar icons. Same 24×24 stroked style as the thumbs\n * icons above so they inherit `.qaid-icon` sizing and `currentColor`.\n * ------------------------------------------------------------------------- */\n\nexport const RECTANGLE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"4\" y=\"6\" width=\"16\" height=\"12\" rx=\"1\" stroke-linejoin=\"round\"/>\n</svg>`;\n\nexport const ARROW_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 19L19 5m0 0h-8m8 0v8\"/>\n</svg>`;\n\nexport const PENCIL_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 20h9M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z\"/>\n</svg>`;\n\nexport const BLUR_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 3s6 6.4 6 10a6 6 0 01-12 0c0-3.6 6-10 6-10z\"/>\n</svg>`;\n\nexport const UNDO_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-4\"/>\n</svg>`;\n\nexport const DONE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20 6L9 17l-5-5\"/>\n</svg>`;\n\nexport const SKIP_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 6l12 12M18 6L6 18\"/>\n</svg>`;\n","/**\n * DOM utility functions\n */\n\n/**\n * Create an element with attributes and optional children\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string)[]\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"className\") {\n el.className = value;\n } else {\n el.setAttribute(key, value);\n }\n }\n }\n\n if (children) {\n for (const child of children) {\n if (typeof child === \"string\") {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n }\n\n return el;\n}\n\n/**\n * Check if viewport width indicates mobile device\n */\nexport function isMobileViewport(breakpoint = 640): boolean {\n return typeof window !== \"undefined\" && window.innerWidth < breakpoint;\n}\n\n/**\n * Get element at point, temporarily hiding shadow hosts so elementFromPoint\n * sees through to the page elements underneath.\n * Uses visibility:hidden which skips the element in elementFromPoint without\n * triggering reflow.\n */\nexport function getElementAtPointUnderOverlay(\n x: number,\n y: number,\n hosts: HTMLElement[]\n): Element | null {\n const saved = hosts.map((h) => h.style.visibility);\n hosts.forEach((h) => (h.style.visibility = \"hidden\"));\n const element = document.elementFromPoint(x, y);\n hosts.forEach((h, i) => (h.style.visibility = saved[i]));\n return element;\n}\n\n/**\n * Check if an element is part of the feedback embed\n * With shadow DOM, embed elements are inside shadow hosts marked with\n * data-qaid-embed or data-qaid-embed-overlay\n */\nexport function isEmbedElement(element: Element | null): boolean {\n if (!element) return false;\n // Check if the element itself is a shadow host\n if (element.hasAttribute(\"data-qaid-embed\") || element.hasAttribute(\"data-qaid-embed-overlay\")) return true;\n // Check ancestors for shadow host\n return !!element.closest(\"[data-qaid-embed], [data-qaid-embed-overlay]\");\n}\n\n/**\n * Get element bounds with optional padding\n */\nexport function getElementBounds(\n element: Element,\n padding = 0\n): { x: number; y: number; width: number; height: number } {\n const rect = element.getBoundingClientRect();\n return {\n x: rect.left - padding,\n y: rect.top - padding,\n width: rect.width + padding * 2,\n height: rect.height + padding * 2,\n };\n}\n\n/**\n * Remove all elements with a specific class\n */\nexport function removeAllByClass(className: string): void {\n document.querySelectorAll(`.${className}`).forEach((el) => {\n el.classList.remove(className);\n });\n}\n","/**\n * Screenshot annotation / markup editor for the thumbs embed.\n *\n * After a screenshot is captured the user can draw on it — rectangles, arrows,\n * a freehand pen, and a blur/redact box — in a full-screen, focus-trapped,\n * WCAG 2.2 AA dialog rendered inside the embed's overlay shadow root. On \"Done\"\n * the base image and the vector shapes are composited into a fresh WebP data\n * URL (so downstream code is unchanged); on \"Skip\"/Escape the original is kept.\n *\n * The geometry and compositing are kept as pure-ish exported functions\n * (`drawShape`, `rectFromPoints`, `compositeAnnotations`) so they can be tested\n * without a real rendering engine — happy-dom has no raster, so tests assert on\n * spied canvas-context calls rather than pixels. The {@link AnnotationEditor}\n * controller keeps the DOM wiring thin and its interactive methods are public\n * so they can be driven directly in tests.\n */\n\nimport { createElement } from \"./dom-utils\";\nimport {\n RECTANGLE_ICON,\n ARROW_ICON,\n PENCIL_ICON,\n BLUR_ICON,\n UNDO_ICON,\n DONE_ICON,\n SKIP_ICON,\n} from \"./icons\";\n\n/** The four drawing tools. */\nexport type ShapeType = \"rect\" | \"arrow\" | \"pen\" | \"blur\";\n\n/** A point in the screenshot's natural-pixel coordinate space. */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * A single vector annotation. `rect`, `arrow` and `blur` use `points[0]` as the\n * drag start and `points[1]` as the drag end; `pen` uses every point in order.\n */\nexport interface Shape {\n type: ShapeType;\n color: string;\n strokeWidth: number;\n points: Point[];\n}\n\n/** Tile size (natural px) for the mosaic drawn by the blur/redact tool. */\nconst BLUR_TILE = 12;\n\n/** Default stroke colour: a high-visibility red that reads on most screenshots. */\nconst DEFAULT_STROKE = \"#ef4444\";\n/** Default swatch palette when the embed doesn't supply one. */\nconst DEFAULT_PALETTE = [\"#ef4444\", \"#f59e0b\", \"#22c55e\", \"#3b82f6\", \"#111827\", \"#ffffff\"];\n/** Friendly names for common swatches (for accessible labels). */\nconst COLOR_NAMES: Record<string, string> = {\n \"#ef4444\": \"red\",\n \"#f59e0b\": \"amber\",\n \"#22c55e\": \"green\",\n \"#3b82f6\": \"blue\",\n \"#111827\": \"black\",\n \"#ffffff\": \"white\",\n \"#6366f1\": \"indigo\",\n};\n/** Accessible name for a swatch colour — a friendly name, else the hex. */\nfunction colorName(hex: string): string {\n return COLOR_NAMES[hex.toLowerCase()] ?? hex;\n}\n/** Default stroke width in natural pixels. */\nconst DEFAULT_STROKE_WIDTH = 4;\n/** Default WebP quality for the re-encoded composite. */\nconst DEFAULT_QUALITY = 0.8;\n\n/** Human-readable labels for the toolbar / announcements (all overridable). */\nexport interface AnnotationLabels {\n title: string;\n instructions: string;\n rectangle: string;\n arrow: string;\n pen: string;\n blur: string;\n undo: string;\n skip: string;\n done: string;\n colors: string;\n}\n\nconst DEFAULT_LABELS: AnnotationLabels = {\n title: \"Annotate screenshot\",\n instructions:\n \"Draw on the screenshot to highlight or hide details, then choose Done to attach it or Skip to send the original.\",\n rectangle: \"Rectangle\",\n arrow: \"Arrow\",\n pen: \"Pen\",\n blur: \"Blur or redact\",\n undo: \"Undo last\",\n skip: \"Skip annotation\",\n done: \"Done\",\n colors: \"Drawing colour\",\n};\n\n/**\n * Normalise two drag corners into a top-left origin plus positive width/height,\n * regardless of the direction the user dragged.\n */\nexport function rectFromPoints(\n a: Point,\n b: Point\n): { x: number; y: number; w: number; h: number } {\n return {\n x: Math.min(a.x, b.x),\n y: Math.min(a.y, b.y),\n w: Math.abs(a.x - b.x),\n h: Math.abs(a.y - b.y),\n };\n}\n\n/** A drag/shape too small to be a deliberate mark (a click, not a drag). */\nfunction isDegenerate(shape: Shape): boolean {\n if (shape.type === \"pen\") return shape.points.length < 2;\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n return r.w < 3 && r.h < 3;\n}\n\nfunction drawArrow(\n ctx: CanvasRenderingContext2D,\n from: Point,\n to: Point,\n width: number\n): void {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n\n const angle = Math.atan2(to.y - from.y, to.x - from.x);\n const head = Math.max(10, width * 3);\n ctx.beginPath();\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle - Math.PI / 6),\n to.y - head * Math.sin(angle - Math.PI / 6)\n );\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle + Math.PI / 6),\n to.y - head * Math.sin(angle + Math.PI / 6)\n );\n ctx.stroke();\n}\n\nfunction drawPen(ctx: CanvasRenderingContext2D, points: Point[]): void {\n if (points.length === 0) return;\n ctx.beginPath();\n ctx.moveTo(points[0].x, points[0].y);\n for (let i = 1; i < points.length; i++) {\n ctx.lineTo(points[i].x, points[i].y);\n }\n ctx.stroke();\n}\n\n/**\n * Redact a region so the underlying content is GONE from the composite — not\n * merely dimmed. Both fills are fully opaque (no alpha), so nothing bleeds\n * through; the two-tone mosaic just gives it a recognisable \"redacted\" texture.\n * There is no real Gaussian blur (it can't be verified without a raster), and a\n * translucent wash would leave sensitive text faintly legible — so this paints\n * solid.\n */\nfunction drawBlur(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number\n): void {\n ctx.fillStyle = \"rgb(15, 23, 42)\";\n ctx.fillRect(x, y, w, h);\n ctx.fillStyle = \"rgb(37, 47, 63)\";\n for (let ty = y; ty < y + h; ty += BLUR_TILE) {\n for (let tx = x; tx < x + w; tx += BLUR_TILE) {\n const checker =\n (Math.floor((tx - x) / BLUR_TILE) + Math.floor((ty - y) / BLUR_TILE)) %\n 2 ===\n 0;\n if (checker) {\n ctx.fillRect(\n tx,\n ty,\n Math.min(BLUR_TILE, x + w - tx),\n Math.min(BLUR_TILE, y + h - ty)\n );\n }\n }\n }\n}\n\n/**\n * Draw a single shape onto a 2D context in the shape's own (natural-pixel)\n * coordinates. Used both for the live editing preview and for the final\n * composite, so what the user sees is exactly what is submitted.\n */\nexport function drawShape(ctx: CanvasRenderingContext2D, shape: Shape): void {\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = \"round\";\n ctx.lineCap = \"round\";\n\n switch (shape.type) {\n case \"rect\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n ctx.strokeRect(r.x, r.y, r.w, r.h);\n break;\n }\n case \"arrow\":\n drawArrow(ctx, shape.points[0], shape.points[1], shape.strokeWidth);\n break;\n case \"pen\":\n drawPen(ctx, shape.points);\n break;\n case \"blur\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n drawBlur(ctx, r.x, r.y, r.w, r.h);\n break;\n }\n }\n\n ctx.restore();\n}\n\n/** Load an image from a data URL. Resolves with the element once decoded. */\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = (): void => resolve(img);\n img.onerror = (): void => reject(new Error(\"Failed to load screenshot\"));\n img.src = src;\n });\n}\n\n/**\n * Composite the base screenshot and the drawn shapes into a new WebP data URL\n * at the image's natural size. Falls back to the original data URL if the image\n * can't be loaded or a 2D context isn't available (so a submission is never\n * lost). This is intentionally standalone so it can be unit-tested in isolation.\n */\nexport async function compositeAnnotations(\n dataUrl: string,\n shapes: Shape[],\n opts: { quality?: number } = {}\n): Promise<string> {\n const quality = opts.quality ?? DEFAULT_QUALITY;\n\n let img: HTMLImageElement;\n try {\n img = await loadImage(dataUrl);\n } catch {\n return dataUrl;\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.naturalWidth || img.width || 1;\n canvas.height = img.naturalHeight || img.height || 1;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return dataUrl;\n\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n for (const shape of shapes) {\n drawShape(ctx, shape);\n }\n\n return canvas.toDataURL(\"image/webp\", quality);\n}\n\n/** Options for {@link AnnotationEditor} / {@link openAnnotationEditor}. */\nexport interface AnnotationEditorOptions {\n /** The captured screenshot as a WebP data URL. */\n dataUrl: string;\n /** Shadow root (or element) the editor is mounted into. */\n root: ShadowRoot | HTMLElement;\n /** WebP quality for the re-encoded composite. */\n quality?: number;\n /** Default stroke colour for rectangle/arrow/pen. */\n color?: string;\n /** Colour swatches offered in the toolbar. The active `color` is ensured present. */\n palette?: string[];\n /** Default stroke width. */\n strokeWidth?: number;\n /** Label overrides. */\n labels?: Partial<AnnotationLabels>;\n /** Apply the embed's per-instance `--qaid-*` CSS variables to the container. */\n applyVars?: (el: HTMLElement) => void;\n /** Announce a message via the embed's live regions. */\n announce?: (message: string, assertive?: boolean) => void;\n /** Turn the container into an accessible modal dialog (focus trap + inert). */\n openDialog?: (\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string }\n ) => void;\n /** Tear down the dialog semantics and restore focus. */\n closeDialog?: () => void;\n}\n\nlet editorSeq = 0;\n\n/**\n * Full-screen screenshot annotation editor. Construct it, call {@link open}\n * (which returns a promise resolving to the composited WebP, or `null` when the\n * user skips), and the controller manages its own DOM and teardown.\n */\nexport class AnnotationEditor {\n readonly shapes: Shape[] = [];\n tool: ShapeType = \"rect\";\n\n private readonly opts: AnnotationEditorOptions;\n private readonly labels: AnnotationLabels;\n private color: string;\n private readonly palette: string[];\n private readonly strokeWidth: number;\n private readonly uid = `qaid-annotate-${++editorSeq}`;\n\n private container!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D | null = null;\n private toolbar!: HTMLDivElement;\n private undoBtn!: HTMLButtonElement;\n private img: HTMLImageElement | null = null;\n\n private drawing = false;\n private current: Shape | null = null;\n private settled = false;\n // Assigned in open() before any settle() is reachable (done/skip only fire\n // after the editor is mounted).\n private resolveResult!: (value: string | null) => void;\n\n constructor(opts: AnnotationEditorOptions) {\n this.opts = opts;\n this.labels = { ...DEFAULT_LABELS, ...(opts.labels ?? {}) };\n this.color = opts.color ?? DEFAULT_STROKE;\n this.palette = (opts.palette && opts.palette.length ? opts.palette : DEFAULT_PALETTE).slice();\n // Ensure the active colour is offered as a swatch so it stays selectable.\n if (!this.palette.some((c) => c.toLowerCase() === this.color.toLowerCase())) {\n this.palette.unshift(this.color);\n }\n this.strokeWidth = opts.strokeWidth ?? DEFAULT_STROKE_WIDTH;\n }\n\n /** Mount the editor and resolve when the user commits (Done) or skips. */\n open(): Promise<string | null> {\n const promise = new Promise<string | null>((resolve) => {\n this.resolveResult = resolve;\n });\n this.mount();\n return promise;\n }\n\n private toolButton(tool: ShapeType, icon: string, label: string): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-tool\",\n \"data-qaid-tool\": tool,\n \"aria-pressed\": tool === this.tool ? \"true\" : \"false\",\n \"aria-label\": `${label} tool`,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n /** A round colour swatch button for the toolbar's colour group. */\n private swatchButton(color: string): HTMLButtonElement {\n const name = colorName(color);\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-swatch\",\n \"data-qaid-color\": color,\n \"aria-pressed\": color.toLowerCase() === this.color.toLowerCase() ? \"true\" : \"false\",\n \"aria-label\": `Draw in ${name}`,\n title: name,\n });\n btn.style.setProperty(\"--qaid-swatch\", color);\n return btn;\n }\n\n private actionButton(\n action: string,\n icon: string,\n label: string,\n extraClass: string\n ): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: `qaid-annotate-action ${extraClass}`,\n \"data-qaid-action\": action,\n \"aria-label\": label,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n private mount(): void {\n const titleId = `${this.uid}-title`;\n const descId = `${this.uid}-desc`;\n\n this.container = createElement(\"div\", { class: \"qaid-annotate\" });\n this.opts.applyVars?.(this.container);\n\n const title = createElement(\"h2\", {\n id: titleId,\n class: \"qaid-annotate-title\",\n });\n title.textContent = this.labels.title;\n\n const desc = createElement(\"p\", {\n id: descId,\n class: \"qaid-annotate-desc\",\n });\n desc.textContent = this.labels.instructions;\n\n const stage = createElement(\"div\", { class: \"qaid-annotate-stage\" });\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"qaid-annotate-canvas\";\n this.canvas.width = 1;\n this.canvas.height = 1;\n this.canvas.setAttribute(\"role\", \"img\");\n this.canvas.setAttribute(\"aria-label\", this.labels.title);\n this.ctx = this.canvas.getContext(\"2d\");\n stage.appendChild(this.canvas);\n\n this.toolbar = createElement(\"div\", {\n class: \"qaid-annotate-toolbar\",\n role: \"toolbar\",\n \"aria-label\": this.labels.title,\n });\n\n const tools = createElement(\"div\", { class: \"qaid-annotate-tools\" });\n tools.appendChild(this.toolButton(\"rect\", RECTANGLE_ICON, this.labels.rectangle));\n tools.appendChild(this.toolButton(\"arrow\", ARROW_ICON, this.labels.arrow));\n tools.appendChild(this.toolButton(\"pen\", PENCIL_ICON, this.labels.pen));\n tools.appendChild(this.toolButton(\"blur\", BLUR_ICON, this.labels.blur));\n\n const colors = createElement(\"div\", {\n class: \"qaid-annotate-colors\",\n role: \"group\",\n \"aria-label\": this.labels.colors,\n });\n for (const c of this.palette) colors.appendChild(this.swatchButton(c));\n\n this.undoBtn = this.actionButton(\"undo\", UNDO_ICON, this.labels.undo, \"qaid-annotate-undo\");\n this.undoBtn.disabled = true;\n\n const skipBtn = this.actionButton(\"skip\", SKIP_ICON, this.labels.skip, \"qaid-annotate-skip\");\n const doneBtn = this.actionButton(\"done\", DONE_ICON, this.labels.done, \"qaid-annotate-done\");\n\n const actions = createElement(\"div\", { class: \"qaid-annotate-actions\" });\n actions.appendChild(this.undoBtn);\n actions.appendChild(skipBtn);\n actions.appendChild(doneBtn);\n\n this.toolbar.appendChild(tools);\n this.toolbar.appendChild(colors);\n this.toolbar.appendChild(actions);\n\n this.container.appendChild(title);\n this.container.appendChild(desc);\n this.container.appendChild(stage);\n this.container.appendChild(this.toolbar);\n this.opts.root.appendChild(this.container);\n\n this.toolbar.addEventListener(\"click\", this.onToolbarClick);\n this.canvas.addEventListener(\"pointerdown\", this.onPointerDown);\n this.container.addEventListener(\"pointermove\", this.onPointerMove);\n this.container.addEventListener(\"pointerup\", this.onPointerUp);\n this.container.addEventListener(\"keydown\", this.onKeyDown);\n\n this.opts.openDialog?.(this.container, {\n labelledbyId: titleId,\n describedbyId: descId,\n });\n this.opts.announce?.(this.labels.instructions);\n\n loadImage(this.opts.dataUrl)\n .then((img) => {\n this.img = img;\n this.canvas.width = img.naturalWidth || img.width || 1;\n this.canvas.height = img.naturalHeight || img.height || 1;\n this.redraw();\n })\n .catch(() => {\n this.img = null;\n this.redraw();\n });\n }\n\n /** Convert a pointer event's client coordinates into natural image pixels. */\n private toImageCoords(e: { clientX: number; clientY: number }): Point {\n const rect = this.canvas.getBoundingClientRect();\n const scaleX = this.canvas.width / (rect.width || this.canvas.width);\n const scaleY = this.canvas.height / (rect.height || this.canvas.height);\n return {\n x: (e.clientX - rect.left) * scaleX,\n y: (e.clientY - rect.top) * scaleY,\n };\n }\n\n /** Repaint the display canvas: base image (if loaded) then every shape. */\n redraw(): void {\n const ctx = this.ctx;\n if (!ctx) return;\n ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n if (this.img) {\n ctx.drawImage(this.img, 0, 0, this.canvas.width, this.canvas.height);\n }\n for (const shape of this.shapes) {\n drawShape(ctx, shape);\n }\n if (this.current) {\n drawShape(ctx, this.current);\n }\n }\n\n /** Select the active drawing tool and reflect it on the toolbar. */\n selectTool(tool: ShapeType): void {\n this.tool = tool;\n const buttons = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-tool]\");\n buttons.forEach((btn) => {\n btn.setAttribute(\n \"aria-pressed\",\n btn.getAttribute(\"data-qaid-tool\") === tool ? \"true\" : \"false\"\n );\n });\n const label = this.labels[LABEL_KEY[tool]];\n this.opts.announce?.(`${label} tool selected`);\n }\n\n /** Set the active drawing colour; subsequent shapes use it. */\n selectColor(color: string): void {\n this.color = color;\n const swatches = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-color]\");\n swatches.forEach((s) => {\n s.setAttribute(\n \"aria-pressed\",\n (s.getAttribute(\"data-qaid-color\") ?? \"\").toLowerCase() === color.toLowerCase()\n ? \"true\"\n : \"false\"\n );\n });\n this.opts.announce?.(`${colorName(color)} colour selected`);\n }\n\n private onToolbarClick = (e: Event): void => {\n const target = e.target as HTMLElement | null;\n const btn = target?.closest<HTMLElement>(\n \"[data-qaid-tool],[data-qaid-action],[data-qaid-color]\"\n );\n if (!btn) return;\n const color = btn.getAttribute(\"data-qaid-color\");\n if (color) {\n this.selectColor(color);\n return;\n }\n const tool = btn.getAttribute(\"data-qaid-tool\");\n if (tool) {\n this.selectTool(tool as ShapeType);\n return;\n }\n const action = btn.getAttribute(\"data-qaid-action\");\n if (action === \"undo\") this.undo();\n else if (action === \"skip\") this.skip();\n else if (action === \"done\") void this.done();\n };\n\n onPointerDown = (e: PointerEvent): void => {\n e.preventDefault?.();\n this.drawing = true;\n const p = this.toImageCoords(e);\n this.current = {\n type: this.tool,\n color: this.color,\n strokeWidth: this.strokeWidth,\n points: this.tool === \"pen\" ? [p] : [p, { ...p }],\n };\n this.redraw();\n };\n\n onPointerMove = (e: PointerEvent): void => {\n if (!this.drawing || !this.current) return;\n const p = this.toImageCoords(e);\n if (this.current.type === \"pen\") {\n this.current.points.push(p);\n } else {\n this.current.points[1] = p;\n }\n this.redraw();\n };\n\n onPointerUp = (): void => {\n if (!this.drawing || !this.current) return;\n this.drawing = false;\n const shape = this.current;\n this.current = null;\n if (isDegenerate(shape)) {\n this.redraw();\n return;\n }\n this.shapes.push(shape);\n this.undoBtn.disabled = false;\n this.opts.announce?.(\"Annotation added\");\n this.redraw();\n };\n\n onKeyDown = (e: KeyboardEvent): void => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n this.skip();\n return;\n }\n if (e.key === \"Enter\") {\n const target = e.target as HTMLElement | null;\n // Let Enter activate a focused tool/action button natively.\n if (target?.getAttribute(\"data-qaid-tool\") || target?.getAttribute(\"data-qaid-action\")) {\n return;\n }\n e.preventDefault();\n void this.done();\n }\n };\n\n /** Remove the most recently added shape. */\n undo(): void {\n if (this.shapes.length === 0) {\n this.opts.announce?.(\"Nothing to undo\");\n return;\n }\n this.shapes.pop();\n this.undoBtn.disabled = this.shapes.length === 0;\n this.opts.announce?.(\"Removed last annotation\");\n this.redraw();\n }\n\n /** Composite the annotations and resolve with the new WebP data URL. */\n async done(): Promise<void> {\n if (this.settled) return;\n const out = await compositeAnnotations(this.opts.dataUrl, this.shapes, {\n quality: this.opts.quality,\n });\n this.opts.announce?.(\"Annotated screenshot attached\");\n this.settle(out);\n }\n\n /** Discard annotations and resolve with `null` (caller keeps the original). */\n skip(): void {\n if (this.settled) return;\n this.opts.announce?.(\"Annotation skipped\");\n this.settle(null);\n }\n\n private settle(value: string | null): void {\n this.settled = true;\n this.teardown();\n this.resolveResult(value);\n }\n\n private teardown(): void {\n this.toolbar.removeEventListener(\"click\", this.onToolbarClick);\n this.canvas.removeEventListener(\"pointerdown\", this.onPointerDown);\n this.container.removeEventListener(\"pointermove\", this.onPointerMove);\n this.container.removeEventListener(\"pointerup\", this.onPointerUp);\n this.container.removeEventListener(\"keydown\", this.onKeyDown);\n this.opts.closeDialog?.();\n this.container.remove();\n }\n}\n\n/** Maps a tool id to its label key, for announcements. */\nconst LABEL_KEY: Record<ShapeType, keyof AnnotationLabels> = {\n rect: \"rectangle\",\n arrow: \"arrow\",\n pen: \"pen\",\n blur: \"blur\",\n};\n\n/**\n * Open the annotation editor and resolve with the composited WebP data URL, or\n * `null` if the user skipped. Thin entry point used by the embed.\n */\nexport function openAnnotationEditor(\n opts: AnnotationEditorOptions\n): Promise<string | null> {\n return new AnnotationEditor(opts).open();\n}\n"],"names":["THUMBS_UP_ICON","THUMBS_DOWN_ICON","RECORD_ICON","FEEDBACK_ICON","RECTANGLE_ICON","ARROW_ICON","PENCIL_ICON","BLUR_ICON","UNDO_ICON","DONE_ICON","SKIP_ICON","createElement","tag","attrs","children","el","key","value","isMobileViewport","breakpoint","getElementAtPointUnderOverlay","x","y","hosts","saved","h","element","i","isEmbedElement","getElementBounds","padding","rect","BLUR_TILE","DEFAULT_STROKE","DEFAULT_PALETTE","COLOR_NAMES","colorName","hex","DEFAULT_STROKE_WIDTH","DEFAULT_QUALITY","DEFAULT_LABELS","rectFromPoints","a","b","isDegenerate","shape","r","drawArrow","ctx","from","to","width","angle","head","drawPen","points","drawBlur","w","ty","tx","drawShape","loadImage","src","resolve","reject","img","compositeAnnotations","dataUrl","shapes","opts","quality","canvas","editorSeq","AnnotationEditor","c","promise","tool","icon","label","btn","color","name","action","extraClass","titleId","descId","title","desc","stage","tools","colors","skipBtn","doneBtn","actions","e","scaleX","scaleY","LABEL_KEY","s","p","target","out","openAnnotationEditor"],"mappings":"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"}
|