@jarve/bug-reporter 0.4.0 → 0.4.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/README.md CHANGED
@@ -73,13 +73,13 @@ Click the blue bug icon (bottom-right) to report bugs. They'll appear in your JA
73
73
 
74
74
  ## Props
75
75
 
76
- | Prop | Type | Required | Description |
77
- | ---------------- | --------------------------------- | -------- | ---------------------------------------------------------- |
78
- | `apiUrl` | `string` | Yes | Base URL for the external bug reporter API |
79
- | `apiKey` | `string` | Yes | Your site's API key (starts with `brk_`) |
76
+ | Prop | Type | Required | Description |
77
+ | ---------------- | --------------------------------- | -------- | -------------------------------------------------------------------------------------- |
78
+ | `apiUrl` | `string` | Yes | Base URL for the external bug reporter API |
79
+ | `apiKey` | `string` | Yes | Your site's API key (starts with `brk_`) |
80
80
  | `buttonPosition` | `'left' \| 'right'` | No | Floating button position. Default: `'right'`. Sibling-aware with other @jarve widgets. |
81
- | `user` | `{ name: string, email: string }` | No | User info. If omitted, the AI will ask during conversation |
82
- | `children` | `ReactNode` | Yes | Your app content |
81
+ | `user` | `{ name: string, email: string }` | No | User info. If omitted, the AI will ask during conversation |
82
+ | `children` | `ReactNode` | Yes | Your app content |
83
83
 
84
84
  ## What gets captured
85
85
 
package/dist/index.js CHANGED
@@ -814,7 +814,7 @@ function ReportModal({
814
814
  const apiHeaders = (0, import_react3.useMemo)(
815
815
  () => ({
816
816
  "Content-Type": "application/json",
817
- "X-Bug-Reporter-Key": apiConfig.apiKey
817
+ "X-Bug-Reporter-Key": apiConfig.apiKey.trim()
818
818
  }),
819
819
  [apiConfig.apiKey]
820
820
  );
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/bug-reporter.tsx","../src/floating-button.tsx","../src/cn.ts","../src/capture-overlay.tsx","../src/utils.ts","../src/console-capture.ts","../src/network-capture.ts","../src/report-modal.tsx"],"sourcesContent":["export { JarveBugReporter } from './bug-reporter'\nexport type { BugReporterUser, BugReporterApiConfig } from './types'\nexport type { FloatingButtonPosition } from './floating-button'\n","import { useState, useEffect, useCallback } from 'react'\nimport { FloatingButton, type FloatingButtonPosition } from './floating-button'\nimport { CaptureOverlay } from './capture-overlay'\nimport { ReportModal } from './report-modal'\nimport { startCapturing, stopCapturing, clearCapturedErrors } from './console-capture'\nimport {\n startNetworkCapture,\n stopNetworkCapture,\n clearCapturedNetworkErrors,\n} from './network-capture'\nimport type { CaptureResult, BugReporterUser } from './types'\n\ndeclare global {\n interface Window {\n __jarve_widgets?: Map<string, {\n type: string\n label: string\n tooltip: string\n iconName: string\n accentColor: string\n isActive: boolean\n trigger: () => void\n }>\n }\n}\n\ninterface JarveBugReporterProps {\n /** Base URL for the external bug reporter API (e.g. \"https://www.jarve.com.au/api/bug-reporter/external\") */\n apiUrl: string\n /** API key for your site (starts with \"brk_\") */\n apiKey: string\n /** Optional position for the floating button (default: 'right') */\n buttonPosition?: FloatingButtonPosition\n /** Optional user info. If not provided, the AI will ask for name/email during the conversation. */\n user?: BugReporterUser\n children: React.ReactNode\n}\n\nexport function JarveBugReporter({\n apiUrl,\n apiKey,\n user,\n buttonPosition,\n children,\n}: JarveBugReporterProps) {\n const safeApiKey = apiKey || ''\n\n const [captureMode, setCaptureMode] = useState(false)\n const [captureResult, setCaptureResult] = useState<CaptureResult | null>(null)\n const [showModal, setShowModal] = useState(false)\n\n // Always start console + network capture on mount\n useEffect(() => {\n startCapturing()\n startNetworkCapture()\n return () => {\n stopCapturing()\n stopNetworkCapture()\n }\n }, [])\n\n const toggleCaptureMode = useCallback(() => {\n setCaptureMode((prev) => !prev)\n }, [])\n\n // Register with launcher widget registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n if (!window.__jarve_widgets) {\n window.__jarve_widgets = new Map()\n }\n\n window.__jarve_widgets.set('bug-reporter', {\n type: 'bug-reporter',\n label: 'Report a Bug',\n tooltip: 'Spotted something broken? Click here to take a screenshot, describe the problem, and we\\'ll handle the rest.',\n iconName: 'Bug',\n accentColor: 'red',\n isActive: false,\n trigger: toggleCaptureMode,\n })\n\n window.dispatchEvent(\n new CustomEvent('jarve:widget-registered', { detail: { type: 'bug-reporter' } })\n )\n\n return () => {\n window.__jarve_widgets?.delete('bug-reporter')\n window.dispatchEvent(\n new CustomEvent('jarve:widget-deregistered', { detail: { type: 'bug-reporter' } })\n )\n }\n }, [toggleCaptureMode])\n\n // Sync active state with launcher registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n const entry = window.__jarve_widgets?.get('bug-reporter')\n if (entry) {\n entry.isActive = captureMode || showModal\n window.dispatchEvent(\n new CustomEvent('jarve:state-change', { detail: { type: 'bug-reporter' } })\n )\n }\n }, [captureMode, showModal])\n\n const handleCapture = useCallback((result: CaptureResult) => {\n setCaptureResult(result)\n setCaptureMode(false)\n setShowModal(true)\n }, [])\n\n const handleCancelCapture = useCallback(() => {\n setCaptureMode(false)\n }, [])\n\n const handleCloseModal = useCallback(() => {\n setShowModal(false)\n setCaptureResult(null)\n clearCapturedErrors()\n clearCapturedNetworkErrors()\n }, [])\n\n // Derive a stable siteId from the apiKey for metadata\n const siteId = safeApiKey.startsWith('brk_') ? safeApiKey.slice(4, 12) : 'external'\n const reporterName = user?.name || 'Anonymous'\n const reporterEmail = user?.email || 'unknown@external'\n\n return (\n <>\n {children}\n\n <FloatingButton\n isActive={captureMode}\n onClick={toggleCaptureMode}\n position={buttonPosition}\n />\n\n <CaptureOverlay\n isActive={captureMode}\n siteId={siteId}\n reporterName={reporterName}\n reporterEmail={reporterEmail}\n onCapture={handleCapture}\n onCancel={handleCancelCapture}\n />\n\n <ReportModal\n isOpen={showModal}\n captureResult={captureResult}\n apiConfig={{ apiUrl, apiKey }}\n siteId={siteId}\n user={{ name: reporterName, email: reporterEmail }}\n onClose={handleCloseModal}\n />\n </>\n )\n}\n","import { useState, useEffect, useRef } from 'react'\nimport { Bug } from 'lucide-react'\nimport { cn } from './cn'\n\nexport type FloatingButtonPosition = 'left' | 'right'\n\ninterface FloatingButtonProps {\n isActive: boolean\n onClick: () => void\n position?: FloatingButtonPosition\n}\n\nconst STACK_OFFSET = 56 // button height (~48px) + gap (8px)\n\nexport function FloatingButton({ isActive, onClick, position = 'right' }: FloatingButtonProps) {\n const [hovered, setHovered] = useState(false)\n const [stackOffset, setStackOffset] = useState(0)\n const [launcherPresent, setLauncherPresent] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const isLeft = position === 'left'\n const sideClasses = isLeft ? 'left-4 md:left-6' : 'right-4 md:right-6'\n\n // Auto-stack with other @jarve widgets on the same side\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n let rafId: number\n\n const recalculate = () => {\n const widgets = Array.from(\n document.querySelectorAll(`[data-jarve-widget][data-jarve-position=\"${position}\"]`),\n )\n // Sort by widget name for deterministic ordering\n widgets.sort((a, b) =>\n (a.getAttribute('data-jarve-widget') || '').localeCompare(\n b.getAttribute('data-jarve-widget') || '',\n ),\n )\n const index = widgets.indexOf(el)\n setStackOffset(index > 0 ? index * STACK_OFFSET : 0)\n }\n\n recalculate()\n\n // Watch for siblings appearing/disappearing\n const observer = new MutationObserver(() => {\n cancelAnimationFrame(rafId)\n rafId = requestAnimationFrame(recalculate)\n })\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => {\n observer.disconnect()\n cancelAnimationFrame(rafId)\n }\n }, [position])\n\n // Hide when launcher is present\n useEffect(() => {\n const checkLauncher = () => {\n setLauncherPresent(!!document.querySelector('[data-jarve-launcher]'))\n }\n\n checkLauncher()\n\n const observer = new MutationObserver(checkLauncher)\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => observer.disconnect()\n }, [])\n\n if (launcherPresent) return null\n\n return (\n <div\n ref={ref}\n data-jarve-widget=\"bug-reporter\"\n data-jarve-position={position}\n className={cn('fixed z-[9999]', 'bottom-4 md:bottom-6', sideClasses)}\n style={stackOffset > 0 ? { transform: `translateY(-${stackOffset}px)` } : undefined}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n {/* Tooltip */}\n <div\n className={cn(\n 'pointer-events-none absolute bottom-full mb-3 w-max max-w-[240px] rounded-xl bg-gray-900/95 px-3.5 py-2.5 text-xs leading-relaxed text-white shadow-xl backdrop-blur-sm transition-all duration-200',\n isLeft ? 'left-0' : 'right-0',\n hovered && !isActive ? 'translate-y-0 opacity-100' : 'translate-y-1 opacity-0',\n )}\n >\n <span className=\"font-semibold text-red-300\">🐛 Report a Bug</span>\n <br />\n <span className=\"text-gray-300\">\n Spotted something broken? Click here to take a screenshot, describe the problem, and\n we&apos;ll handle the rest.\n </span>\n {/* Arrow */}\n <div\n className={cn(\n 'absolute top-full h-0 w-0 border-x-[6px] border-t-[6px] border-x-transparent border-t-gray-900',\n isLeft ? 'left-4' : 'right-4',\n )}\n />\n </div>\n\n {/* Button */}\n <button\n onClick={onClick}\n className={cn(\n 'flex items-center justify-center rounded-full shadow-lg transition-all duration-200',\n 'hover:scale-110 focus:ring-2 focus:ring-offset-2 focus:outline-none',\n 'h-11 w-11 md:h-12 md:w-12',\n isActive\n ? 'animate-pulse bg-red-500 text-white focus:ring-red-400'\n : 'bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-400',\n )}\n title={isActive ? 'Cancel bug capture' : 'Report a bug'}\n aria-label={isActive ? 'Cancel bug capture' : 'Report a bug'}\n >\n <Bug className=\"h-4 w-4 md:h-5 md:w-5\" />\n </button>\n </div>\n )\n}\n","import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useEffect, useState, useCallback, useRef } from 'react'\nimport { toPng } from 'html-to-image'\nimport {\n getNearestSection,\n collectMetadata,\n collectElementInfo,\n isTouchCapable,\n extractCoordinates,\n} from './utils'\nimport { getCapturedErrors } from './console-capture'\nimport { getCapturedNetworkErrors } from './network-capture'\nimport type {\n CaptureResult,\n CaptureMetadata,\n ConsoleError,\n FailedNetworkRequest,\n PointerCoordinates,\n} from './types'\n\ninterface CaptureOverlayProps {\n isActive: boolean\n siteId: string\n reporterName: string\n reporterEmail: string\n onCapture: (result: CaptureResult) => void\n onCancel: () => void\n}\n\nfunction dataUrlToBlob(dataUrl: string): Blob {\n const [header, base64] = dataUrl.split(',')\n const mime = header.match(/:(.*?);/)?.[1] || 'image/png'\n const bytes = atob(base64)\n const arr = new Uint8Array(bytes.length)\n for (let i = 0; i < bytes.length; i++) arr[i] = bytes.charCodeAt(i)\n return new Blob([arr], { type: mime })\n}\n\nexport function CaptureOverlay({\n isActive,\n siteId,\n reporterName,\n reporterEmail,\n onCapture,\n onCancel,\n}: CaptureOverlayProps) {\n const [hoveredElement, setHoveredElement] = useState<HTMLElement | null>(null)\n const [hoveredRect, setHoveredRect] = useState<DOMRect | null>(null)\n const [isCapturing, setIsCapturing] = useState(false)\n const [isTouchMode, setIsTouchMode] = useState(false)\n const [selectedSection, setSelectedSection] = useState<HTMLElement | null>(null)\n const [selectedRect, setSelectedRect] = useState<DOMRect | null>(null)\n const [selectedTarget, setSelectedTarget] = useState<HTMLElement | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n const hoveredElementRef = useRef<HTMLElement | null>(null)\n const rafRef = useRef<number | null>(null)\n const touchCoordsRef = useRef<PointerCoordinates | null>(null)\n\n // Initialize touch mode on activation\n useEffect(() => {\n if (isActive) {\n setIsTouchMode(isTouchCapable())\n }\n }, [isActive])\n\n const captureScreenshot = useCallback(\n async (section: HTMLElement, target: HTMLElement, coords: PointerCoordinates) => {\n const elementInfo = collectElementInfo(target, section, coords)\n setIsCapturing(true)\n\n try {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n\n await new Promise((r) => setTimeout(r, 50))\n\n const MAX_DIMENSION = 2000\n const sectionRect = section.getBoundingClientRect()\n const pixelRatio =\n sectionRect.width > MAX_DIMENSION || sectionRect.height > MAX_DIMENSION ? 1 : 2\n\n const dataUrl = await toPng(section, {\n quality: 0.9,\n pixelRatio,\n skipFonts: true,\n })\n\n const blob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: blob, metadata, consoleErrors, networkErrors })\n } catch (err) {\n console.warn(\n 'Bug reporter: first capture attempt failed, retrying with simpler settings',\n err,\n )\n\n try {\n const dataUrl = await toPng(section, {\n quality: 0.6,\n pixelRatio: 1,\n skipFonts: true,\n cacheBust: true,\n })\n\n const retryBlob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: retryBlob, metadata, consoleErrors, networkErrors })\n } catch {\n console.error('Bug reporter: screenshot capture failed after retry')\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n onCapture({\n screenshot: new Blob(),\n metadata: { ...metadata, screenshotCaptureFailed: true },\n consoleErrors,\n networkErrors,\n })\n }\n } finally {\n setIsCapturing(false)\n }\n },\n [siteId, reporterName, reporterEmail, onCapture],\n )\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n if (rafRef.current) return\n\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n\n if (target.closest('[data-bug-reporter]')) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n return\n }\n\n const section = getNearestSection(target)\n setHoveredElement(section)\n hoveredElementRef.current = section\n setHoveredRect(section ? section.getBoundingClientRect() : null)\n })\n },\n [isActive, isCapturing, isTouchMode],\n )\n\n const handleClick = useCallback(\n async (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const section = getNearestSection(target)\n if (!section) return\n\n await captureScreenshot(section, target, extractCoordinates(e))\n },\n [isActive, isCapturing, isTouchMode, captureScreenshot],\n )\n\n const handleTouchEnd = useCallback(\n (e: TouchEvent) => {\n if (!isActive || isCapturing) return\n\n const touch = e.changedTouches[0]\n if (!touch) return\n\n const target = document.elementFromPoint(touch.clientX, touch.clientY)\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n const section = getNearestSection(target)\n if (!section) return\n\n setSelectedSection(section)\n setSelectedRect(section.getBoundingClientRect())\n setSelectedTarget(target)\n touchCoordsRef.current = extractCoordinates(touch)\n },\n [isActive, isCapturing],\n )\n\n const handleConfirmCapture = useCallback(async () => {\n if (!selectedSection || !selectedTarget || !touchCoordsRef.current) return\n await captureScreenshot(selectedSection, selectedTarget, touchCoordsRef.current)\n }, [selectedSection, selectedTarget, captureScreenshot])\n\n const handlePointerDown = useCallback(\n (e: PointerEvent) => {\n if (!isActive) return\n if (e.pointerType === 'touch') {\n setIsTouchMode(true)\n } else if (e.pointerType === 'mouse') {\n setIsTouchMode(false)\n }\n },\n [isActive],\n )\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isActive) {\n e.preventDefault()\n e.stopPropagation()\n onCancel()\n }\n },\n [isActive, onCancel],\n )\n\n const handleScroll = useCallback(() => {\n if (hoveredElementRef.current) {\n setHoveredRect(hoveredElementRef.current.getBoundingClientRect())\n }\n if (selectedSection) {\n setSelectedRect(selectedSection.getBoundingClientRect())\n }\n }, [selectedSection])\n\n useEffect(() => {\n if (!isActive) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n return\n }\n\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n window.addEventListener('scroll', handleScroll, { passive: true })\n\n if (isTouchMode) {\n document.addEventListener('touchend', handleTouchEnd, { passive: true })\n } else {\n document.addEventListener('mousemove', handleMouseMove, true)\n document.addEventListener('click', handleClick, true)\n }\n\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n window.removeEventListener('scroll', handleScroll)\n document.removeEventListener('touchend', handleTouchEnd)\n document.removeEventListener('mousemove', handleMouseMove, true)\n document.removeEventListener('click', handleClick, true)\n if (rafRef.current) cancelAnimationFrame(rafRef.current)\n }\n }, [\n isActive,\n isTouchMode,\n handleMouseMove,\n handleClick,\n handleTouchEnd,\n handlePointerDown,\n handleKeyDown,\n handleScroll,\n ])\n\n const highlightRect = isTouchMode ? selectedRect : hoveredRect\n const showHighlight = isTouchMode ? !!selectedSection : !!hoveredElement && !!hoveredRect\n\n if (!isActive) return null\n\n return (\n <>\n {/* Instruction banner */}\n <div\n data-bug-reporter\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"fixed top-0 right-0 left-0 z-[10000] flex items-center justify-center gap-3 bg-indigo-600 px-4 py-2 text-center text-sm font-medium text-white\"\n >\n {isTouchMode ? (\n <>\n <span>Tap the section with the bug</span>\n <button\n onClick={onCancel}\n className=\"min-h-[44px] rounded-md bg-white/20 px-3 py-1 text-sm font-medium\"\n >\n Cancel\n </button>\n </>\n ) : (\n <>\n Click on the section with the bug. Press{' '}\n <kbd className=\"mx-1 rounded bg-indigo-800 px-1.5 py-0.5 text-xs\">Esc</kbd> to cancel.\n </>\n )}\n </div>\n\n {/* Section highlight overlay */}\n {showHighlight && highlightRect && (\n <div\n ref={overlayRef}\n data-bug-reporter\n className=\"pointer-events-none fixed z-[9998] rounded-sm border-2 border-indigo-500 transition-all duration-150 ease-out\"\n style={{\n top: highlightRect.top - 2,\n left: highlightRect.left - 2,\n width: highlightRect.width + 4,\n height: highlightRect.height + 4,\n backgroundColor: 'rgba(99, 102, 241, 0.08)',\n }}\n />\n )}\n\n {/* Touch confirmation bar */}\n {isTouchMode && selectedSection && !isCapturing && (\n <div\n data-bug-reporter\n className=\"fixed right-0 bottom-0 left-0 z-[10000] border-t border-gray-200 bg-white shadow-lg\"\n style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}\n >\n <div className=\"flex items-center justify-between gap-3 px-4 py-3\">\n <span className=\"truncate text-sm font-medium text-gray-900\">\n Capture this section?\n </span>\n <div className=\"flex shrink-0 gap-2\">\n <button\n onClick={() => {\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n }}\n className=\"min-h-[44px] rounded-md border border-gray-300 px-4 text-sm font-medium text-gray-700\"\n >\n Cancel\n </button>\n <button\n onClick={handleConfirmCapture}\n className=\"min-h-[44px] rounded-md bg-indigo-600 px-4 text-sm font-medium text-white\"\n >\n Capture\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Full-page cursor override — desktop only */}\n {!isTouchMode && <style>{`* { cursor: crosshair !important; }`}</style>}\n </>\n )\n}\n","import { UAParser } from 'ua-parser-js'\nimport type { CaptureMetadata, ClickedElement, PointerCoordinates } from './types'\n\n/**\n * Walk up the DOM tree to find the nearest semantically meaningful section.\n */\nexport function getNearestSection(element: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = element\n\n while (current && current !== document.body) {\n if (\n current.id ||\n current.dataset.section ||\n current.tagName === 'SECTION' ||\n current.tagName === 'MAIN' ||\n current.tagName === 'ARTICLE' ||\n current.tagName === 'NAV' ||\n current.tagName === 'HEADER' ||\n current.tagName === 'FOOTER' ||\n current.role === 'main' ||\n current.role === 'navigation'\n ) {\n return current\n }\n current = current.parentElement\n }\n\n return document.querySelector('main') || document.body\n}\n\n/**\n * Get a human-readable section identifier from an element.\n */\nexport function getSectionId(element: HTMLElement): string | null {\n if (element.id) return `#${element.id}`\n if (element.dataset.section) return element.dataset.section\n if (element.tagName === 'MAIN') return 'main'\n if (element.tagName === 'SECTION') {\n const heading = element.querySelector('h1, h2, h3')\n if (heading) return heading.textContent?.trim().slice(0, 60) || null\n }\n return element.tagName.toLowerCase()\n}\n\n/**\n * Derive device type from viewport width and touch capability.\n */\nexport function getDeviceType(): 'desktop' | 'tablet' | 'mobile' {\n const width = window.innerWidth\n const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0\n\n if (width <= 768 && hasTouch) return 'mobile'\n if (width <= 1024 && hasTouch) return 'tablet'\n return 'desktop'\n}\n\n/**\n * Parse user agent string into readable browser and OS info.\n */\nexport function parseUserAgent(): { browser: string; os: string } {\n const parser = new UAParser(navigator.userAgent)\n const browser = parser.getBrowser()\n const osInfo = parser.getOS()\n\n return {\n browser: `${browser.name || 'Unknown'} ${browser.version || ''}`.trim(),\n os: `${osInfo.name || 'Unknown'} ${osInfo.version || ''}`.trim(),\n }\n}\n\n/**\n * Build a CSS selector path from an element up to its section container.\n */\nexport function buildSelectorPath(element: HTMLElement, stopAt?: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = element\n\n while (current && current !== document.body && current !== stopAt) {\n let selector = current.tagName.toLowerCase()\n if (current.id) {\n selector += `#${current.id}`\n } else if (current.className && typeof current.className === 'string') {\n const classes = current.className.trim().split(/\\s+/).slice(0, 2).join('.')\n if (classes) selector += `.${classes}`\n }\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\n/**\n * Check if the device supports touch input.\n */\nexport function isTouchCapable(): boolean {\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0\n}\n\n/**\n * Normalize coordinates from MouseEvent or Touch into a common shape.\n */\nexport function extractCoordinates(e: MouseEvent | Touch): PointerCoordinates {\n return {\n pageX: e.pageX,\n pageY: e.pageY,\n clientX: e.clientX,\n clientY: e.clientY,\n }\n}\n\n/**\n * Collect info about the exact element the user clicked/tapped.\n */\nexport function collectElementInfo(\n target: HTMLElement,\n section: HTMLElement,\n coords: PointerCoordinates,\n): ClickedElement {\n const sectionRect = section.getBoundingClientRect()\n\n const dataAttributes: Record<string, string> = {}\n for (const attr of Array.from(target.attributes)) {\n if (attr.name.startsWith('data-') && attr.name !== 'data-bug-reporter') {\n dataAttributes[attr.name] = attr.value\n }\n }\n\n return {\n tagName: target.tagName,\n textContent: (target.textContent || '').trim().slice(0, 200) || null,\n className: typeof target.className === 'string' ? target.className : '',\n id: target.id || null,\n ariaLabel: target.getAttribute('aria-label') || null,\n dataAttributes,\n selectorPath: buildSelectorPath(target, section),\n clickX: coords.pageX,\n clickY: coords.pageY,\n relativeClickX: coords.clientX - sectionRect.left,\n relativeClickY: coords.clientY - sectionRect.top,\n }\n}\n\n/**\n * Collect all metadata at the moment of capture.\n */\nexport function collectMetadata(\n sectionElement: HTMLElement,\n siteId: string,\n reporterName: string,\n reporterEmail: string,\n clickedElement?: ClickedElement,\n): CaptureMetadata {\n const { browser, os } = parseUserAgent()\n const now = new Date()\n\n return {\n pageUrl: window.location.href,\n sectionId: getSectionId(sectionElement),\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n deviceType: getDeviceType(),\n browser,\n os,\n timestamp: now.toISOString(),\n timestampUtc: now.toUTCString(),\n siteId,\n reporterName,\n reporterEmail,\n clickedElement,\n }\n}\n","import type { ConsoleError } from './types'\n\nconst MAX_ERRORS = 50\n\nlet capturedErrors: ConsoleError[] = []\nlet isCapturing = false\nlet originalConsoleError: typeof console.error | null = null\nlet errorListener: ((event: ErrorEvent) => void) | null = null\nlet rejectionListener: ((event: PromiseRejectionEvent) => void) | null = null\n\n/**\n * Start capturing console errors and unhandled exceptions.\n * Stores the last N errors for inclusion in bug reports.\n */\nexport function startCapturing(): void {\n if (isCapturing) return\n if ((console.error as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedErrors = []\n\n originalConsoleError = console.error\n const patchedConsoleError = (...args: unknown[]) => {\n const MAX_MSG_LEN = 500\n const message = args\n .map((a) => {\n if (typeof a === 'string') return a.slice(0, MAX_MSG_LEN)\n try {\n const s = JSON.stringify(a)\n return s.slice(0, MAX_MSG_LEN)\n } catch {\n return String(a).slice(0, MAX_MSG_LEN)\n }\n })\n .join(' ')\n .slice(0, MAX_MSG_LEN)\n\n capturedErrors.push({\n message,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n\n originalConsoleError!.apply(console, args)\n }\n ;(patchedConsoleError as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n console.error = patchedConsoleError\n\n errorListener = (event: ErrorEvent) => {\n capturedErrors.push({\n message: event.message,\n source: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('error', errorListener)\n\n rejectionListener = (event: PromiseRejectionEvent) => {\n capturedErrors.push({\n message: `Unhandled Promise Rejection: ${event.reason instanceof Error ? event.reason.message : String(event.reason)}`,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('unhandledrejection', rejectionListener)\n}\n\n/**\n * Stop capturing and restore original console.error.\n */\nexport function stopCapturing(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalConsoleError) {\n const restoreTarget = originalConsoleError\n console.error = restoreTarget\n if (console.error !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original console.error')\n }\n originalConsoleError = null\n }\n\n if (errorListener) {\n window.removeEventListener('error', errorListener)\n errorListener = null\n }\n\n if (rejectionListener) {\n window.removeEventListener('unhandledrejection', rejectionListener)\n rejectionListener = null\n }\n}\n\n/**\n * Get a copy of all captured errors.\n */\nexport function getCapturedErrors(): ConsoleError[] {\n return [...capturedErrors]\n}\n\n/**\n * Clear the captured errors buffer.\n */\nexport function clearCapturedErrors(): void {\n capturedErrors = []\n}\n","import type { FailedNetworkRequest } from './types'\n\nconst MAX_REQUESTS = 30\n/** Skip reading bodies larger than 64 KB to avoid memory/latency cost */\nconst MAX_BODY_READ_BYTES = 64 * 1024\nconst TRUNCATE_LEN = 500\n\nlet capturedRequests: FailedNetworkRequest[] = []\nlet isCapturing = false\nlet originalFetch: typeof window.fetch | null = null\n\nfunction truncateBody(body: string | null | undefined, maxLen = TRUNCATE_LEN): string | null {\n if (!body) return null\n if (body.length <= maxLen) return body\n return body.slice(0, maxLen) + '...(truncated)'\n}\n\n/**\n * Read a bounded amount of the response body for error capture.\n * Returns truncated text for small bodies, or a size-only metadata\n * string when the body exceeds MAX_BODY_READ_BYTES.\n */\nasync function readBoundedBody(response: Response): Promise<string | null> {\n try {\n // Fast path: check Content-Length header\n const contentLength = response.headers.get('Content-Length')\n if (contentLength) {\n const size = parseInt(contentLength, 10)\n if (!isNaN(size) && size > MAX_BODY_READ_BYTES) {\n return `[body too large: ${size} bytes]`\n }\n }\n\n // Stream-based bounded read when ReadableStream is available\n const cloned = response.clone()\n if (cloned.body && typeof cloned.body.getReader === 'function') {\n const reader = cloned.body.getReader()\n const chunks: Uint8Array[] = []\n let totalBytes = 0\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n totalBytes += value.byteLength\n if (totalBytes > MAX_BODY_READ_BYTES) {\n reader.cancel().catch(() => {})\n return `[body too large: >${MAX_BODY_READ_BYTES} bytes]`\n }\n chunks.push(value)\n }\n\n const decoder = new TextDecoder()\n const text = chunks.map((c) => decoder.decode(c, { stream: true })).join('')\n return truncateBody(text)\n }\n\n // Fallback: clone().text() for environments without ReadableStream\n const text = await cloned.text()\n return truncateBody(text)\n } catch {\n // Body may not be readable\n return null\n }\n}\n\n/**\n * Start capturing failed network requests (fetch only).\n * Intercepts window.fetch to log requests that return status >= 400 or throw.\n */\nexport function startNetworkCapture(): void {\n if (isCapturing || typeof window === 'undefined') return\n if ((window.fetch as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedRequests = []\n\n originalFetch = window.fetch\n const patchedFetch = async function patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n const method =\n init?.method ||\n (typeof input !== 'string' && !(input instanceof URL) ? input.method : 'GET') ||\n 'GET'\n\n // Skip our own bug reporter endpoints and non-HTTP URLs\n if (\n url.includes('/api/bug-reporter/') ||\n url.includes('/bug-reporter/external/') ||\n url.startsWith('data:') ||\n url.startsWith('blob:')\n ) {\n return originalFetch!.call(window, input, init)\n }\n\n try {\n const response = await originalFetch!.call(window, input, init)\n\n if (response.status >= 400) {\n const responseBody = await readBoundedBody(response)\n\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: response.status,\n statusText: response.statusText,\n responseBody,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n }\n\n return response\n } catch (error) {\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: 0,\n statusText: error instanceof Error ? error.message : 'Network error',\n responseBody: null,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n\n throw error\n }\n }\n ;(patchedFetch as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n window.fetch = patchedFetch\n}\n\n/**\n * Stop capturing and restore original fetch.\n */\nexport function stopNetworkCapture(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalFetch) {\n const restoreTarget = originalFetch\n window.fetch = restoreTarget\n if (window.fetch !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original fetch')\n }\n originalFetch = null\n }\n}\n\n/**\n * Get a copy of all captured failed requests.\n */\nexport function getCapturedNetworkErrors(): FailedNetworkRequest[] {\n return [...capturedRequests]\n}\n\n/**\n * Clear the captured requests buffer.\n */\nexport function clearCapturedNetworkErrors(): void {\n capturedRequests = []\n}\n","import { useState, useRef, useEffect, useCallback, useMemo } from 'react'\nimport { X, Send, Loader2, CheckCircle2 } from 'lucide-react'\nimport { cn } from './cn'\nimport { isTouchCapable } from './utils'\nimport type {\n CaptureResult,\n ConversationMessage,\n BugReporterApiConfig,\n BugReporterUser,\n} from './types'\n\ninterface ReportModalProps {\n isOpen: boolean\n captureResult: CaptureResult | null\n apiConfig: BugReporterApiConfig\n siteId: string\n user: BugReporterUser\n onClose: () => void\n}\n\ntype ModalState = 'chatting' | 'submitting' | 'submitted' | 'error'\n\nexport function ReportModal({\n isOpen,\n captureResult,\n apiConfig,\n siteId,\n user,\n onClose,\n}: ReportModalProps) {\n const [messages, setMessages] = useState<ConversationMessage[]>([])\n const [input, setInput] = useState('')\n const [isLoading, setIsLoading] = useState(false)\n const [modalState, setModalState] = useState<ModalState>('chatting')\n const [reportId, setReportId] = useState<string | null>(null)\n const [screenshotUrl, setScreenshotUrl] = useState<string | null>(null)\n const [errorMessage, setErrorMessage] = useState<string | null>(null)\n const chatEndRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLTextAreaElement>(null)\n const hasInitRef = useRef(false)\n\n const apiHeaders = useMemo<Record<string, string>>(\n () => ({\n 'Content-Type': 'application/json',\n 'X-Bug-Reporter-Key': apiConfig.apiKey,\n }),\n [apiConfig.apiKey],\n )\n\n // Create screenshot preview URL — revoke previous URL on replacement and\n // reset to null when there is no valid screenshot.\n useEffect(() => {\n if (captureResult?.screenshot && captureResult.screenshot.size > 0) {\n const url = URL.createObjectURL(captureResult.screenshot)\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return url\n })\n return () => {\n URL.revokeObjectURL(url)\n setScreenshotUrl(null)\n }\n }\n\n // No valid screenshot — clear stale URL\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return null\n })\n }, [captureResult])\n\n // Stable callback for sending initial AI message\n const sendInitialMessage = useCallback(async () => {\n if (!captureResult) return\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: [],\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n setMessages([{ role: 'assistant', content: data.message }])\n } catch {\n setMessages([\n {\n role: 'assistant',\n content:\n \"I can see you've captured a section of the page. What's going wrong here? Please describe the issue you're experiencing.\",\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }, [captureResult, apiConfig.apiUrl, apiHeaders])\n\n // Send initial AI message when modal opens\n useEffect(() => {\n if (isOpen && captureResult && !hasInitRef.current) {\n hasInitRef.current = true\n sendInitialMessage()\n }\n }, [isOpen, captureResult, sendInitialMessage])\n\n // Auto-scroll chat\n useEffect(() => {\n chatEndRef.current?.scrollIntoView({ behavior: 'smooth' })\n }, [messages])\n\n // Focus input\n useEffect(() => {\n if (isOpen && !isLoading) {\n inputRef.current?.focus()\n }\n }, [isOpen, isLoading, messages])\n\n const submitReport = useCallback(\n async (\n conversation: ConversationMessage[],\n structuredReport?: {\n summary: string\n expected_behavior: string\n actual_behavior: string\n reproducibility: string\n specific_data: string\n severity: string\n },\n ) => {\n if (!captureResult || modalState !== 'chatting') return\n setModalState('submitting')\n\n try {\n // Convert screenshot blob to base64\n let screenshotBase64: string | undefined\n if (captureResult.screenshot.size > 0) {\n const reader = new FileReader()\n screenshotBase64 = await new Promise<string>((resolve, reject) => {\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(captureResult.screenshot)\n })\n }\n\n // If no structured report from AI, request one via the API\n let report = structuredReport\n if (!report) {\n try {\n const summaryResponse = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: conversation,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n requestSummary: true,\n }),\n })\n if (summaryResponse.ok) {\n const data = await summaryResponse.json()\n report = data.structuredReport\n }\n } catch {\n // Fall through to default\n }\n }\n\n // Submit via the external submit endpoint\n const response = await fetch(`${apiConfig.apiUrl}/submit`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n screenshot: screenshotBase64,\n metadata: captureResult.metadata,\n conversation,\n structuredReport: report,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}))\n throw new Error(errorData.error || 'Failed to submit report')\n }\n\n const data = await response.json()\n setReportId(data.id)\n setModalState('submitted')\n } catch (err) {\n console.error('Bug reporter: failed to submit report', err)\n setErrorMessage(err instanceof Error ? err.message : 'Failed to submit report')\n setModalState('error')\n }\n },\n [captureResult, apiConfig.apiUrl, apiHeaders, modalState],\n )\n\n const handleManualSubmit = useCallback(() => {\n submitReport(messages)\n }, [submitReport, messages])\n\n async function sendMessage() {\n if (!input.trim() || isLoading || !captureResult) return\n\n const userMessage = input.trim()\n setInput('')\n\n const newMessages: ConversationMessage[] = [...messages, { role: 'user', content: userMessage }]\n setMessages(newMessages)\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: newMessages,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n\n setMessages([...newMessages, { role: 'assistant', content: data.message }])\n\n if (data.readyToSubmit && data.structuredReport) {\n await submitReport(\n [...newMessages, { role: 'assistant', content: data.message }],\n data.structuredReport,\n )\n }\n } catch {\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content: 'I had trouble processing that. Could you try describing the issue again?',\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }\n\n function handleClose() {\n setMessages([])\n setInput('')\n setModalState('chatting')\n setReportId(null)\n setErrorMessage(null)\n // Revoke and clear stale screenshot URL\n if (screenshotUrl) {\n URL.revokeObjectURL(screenshotUrl)\n }\n setScreenshotUrl(null)\n hasInitRef.current = false\n onClose()\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Enter' && !e.shiftKey && !isTouchCapable()) {\n e.preventDefault()\n sendMessage()\n }\n }\n\n if (!isOpen) return null\n\n return (\n <div\n data-bug-reporter\n className=\"fixed inset-0 z-[10001] flex items-center justify-center bg-black/50 backdrop-blur-sm\"\n onClick={(e) => {\n if (e.target === e.currentTarget) handleClose()\n }}\n >\n <div\n className={cn(\n 'flex flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-gray-950',\n 'mx-4 w-full max-w-lg',\n 'max-[768px]:mx-0 max-[768px]:h-full max-[768px]:max-w-none max-[768px]:rounded-none',\n 'min-[769px]:max-h-[85vh]',\n )}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-gray-200 bg-gray-50/30 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/30\">\n <div>\n <h2 className=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">Bug Report</h2>\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">{siteId}</p>\n </div>\n <button\n onClick={handleClose}\n className=\"rounded-md p-1.5 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-gray-600 dark:text-gray-400\" />\n </button>\n </div>\n\n {/* Screenshot preview */}\n {screenshotUrl && (\n <div className=\"border-b border-gray-200 bg-gray-50/10 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/10\">\n <img\n src={screenshotUrl}\n alt=\"Captured section\"\n className=\"max-h-40 w-full rounded-md border border-gray-200 object-contain dark:border-gray-700\"\n />\n </div>\n )}\n\n {/* Chat area */}\n <div className=\"min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-3\">\n {modalState === 'submitted' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <CheckCircle2 className=\"mb-3 h-12 w-12 text-green-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Report Submitted\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n Reference:{' '}\n <code className=\"rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-800\">\n {reportId?.slice(0, 8)}\n </code>\n </p>\n <p className=\"mt-2 text-sm text-gray-500 dark:text-gray-400\">\n Thanks for the report — we&apos;ll look into it.\n </p>\n <button\n onClick={handleClose}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Done\n </button>\n </div>\n ) : modalState === 'error' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <X className=\"mb-3 h-12 w-12 text-red-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Submission Failed\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n {errorMessage || 'Something went wrong. Please try again.'}\n </p>\n <button\n onClick={() => setModalState('chatting')}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Try Again\n </button>\n </div>\n ) : modalState === 'submitting' ? (\n <div className=\"flex flex-col items-center justify-center py-8\">\n <Loader2 className=\"mb-3 h-8 w-8 animate-spin text-indigo-500\" />\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">Submitting your report...</p>\n </div>\n ) : (\n <>\n {captureResult?.screenshot.size === 0 && (\n <p className=\"text-xs text-amber-600\">\n Screenshot could not be captured. Please describe the visual issue in detail.\n </p>\n )}\n\n {messages.map((msg, i) => (\n <div\n key={i}\n className={cn(\n 'text-sm leading-relaxed',\n msg.role === 'assistant'\n ? 'rounded-lg bg-gray-100/50 p-3 text-gray-900 dark:bg-gray-800/50 dark:text-gray-100'\n : 'ml-8 rounded-lg bg-indigo-600 p-3 text-white',\n )}\n >\n {msg.content}\n </div>\n ))}\n\n {isLoading && (\n <div className=\"flex items-center gap-2 rounded-lg bg-gray-100/50 p-3 dark:bg-gray-800/50\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin text-gray-500\" />\n <span className=\"text-sm text-gray-500 dark:text-gray-400\">Thinking...</span>\n </div>\n )}\n\n <div ref={chatEndRef} />\n </>\n )}\n </div>\n\n {/* Input area — only show during chatting */}\n {modalState === 'chatting' && (\n <div className=\"border-t border-gray-200 px-4 py-3 dark:border-gray-800\">\n <div className=\"flex flex-col gap-2\">\n <textarea\n ref={inputRef}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe what's wrong...\"\n rows={2}\n className=\"w-full resize-none rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 focus:border-transparent focus:ring-2 focus:ring-indigo-400 focus:outline-none dark:border-gray-700 dark:bg-gray-950 dark:text-gray-100\"\n disabled={isLoading}\n />\n <div className=\"flex flex-col gap-2\">\n {captureResult &&\n (captureResult.consoleErrors.length > 0 ||\n captureResult.networkErrors.length > 0) && (\n <p className=\"flex-1 text-xs text-amber-600\">\n {[\n captureResult.consoleErrors.length > 0\n ? `${captureResult.consoleErrors.length} console error${captureResult.consoleErrors.length !== 1 ? 's' : ''}`\n : null,\n captureResult.networkErrors.length > 0\n ? `${captureResult.networkErrors.length} failed request${captureResult.networkErrors.length !== 1 ? 's' : ''}`\n : null,\n ]\n .filter(Boolean)\n .join(' + ')}{' '}\n captured — these will be included in the report.\n </p>\n )}\n <div className=\"flex justify-end gap-2\">\n <button\n onClick={sendMessage}\n disabled={!input.trim() || isLoading}\n className=\"flex items-center gap-1 rounded-md bg-indigo-600 px-3 py-2 text-nowrap text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"Send message\"\n >\n <Send className=\"h-4 w-4\" />\n <span className=\"text-sm font-medium\">Send message</span>\n </button>\n {messages.length >= 2 && (\n <button\n onClick={handleManualSubmit}\n disabled={isLoading}\n className=\"rounded-md bg-green-600 px-3 py-2 text-xs font-medium text-nowrap text-white transition-colors hover:bg-green-700 disabled:opacity-50\"\n title=\"Submit report now\"\n >\n <span className=\"text-sm font-medium\">Submit report</span>\n </button>\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_react","isCapturing","text","patchedFetch","import_jsx_runtime","isCapturing","import_react","import_lucide_react","import_jsx_runtime","data","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/bug-reporter.tsx","../src/floating-button.tsx","../src/cn.ts","../src/capture-overlay.tsx","../src/utils.ts","../src/console-capture.ts","../src/network-capture.ts","../src/report-modal.tsx"],"sourcesContent":["export { JarveBugReporter } from './bug-reporter'\nexport type { BugReporterUser, BugReporterApiConfig } from './types'\nexport type { FloatingButtonPosition } from './floating-button'\n","import { useState, useEffect, useCallback } from 'react'\nimport { FloatingButton, type FloatingButtonPosition } from './floating-button'\nimport { CaptureOverlay } from './capture-overlay'\nimport { ReportModal } from './report-modal'\nimport { startCapturing, stopCapturing, clearCapturedErrors } from './console-capture'\nimport {\n startNetworkCapture,\n stopNetworkCapture,\n clearCapturedNetworkErrors,\n} from './network-capture'\nimport type { CaptureResult, BugReporterUser } from './types'\n\ndeclare global {\n interface Window {\n __jarve_widgets?: Map<\n string,\n {\n type: string\n label: string\n tooltip: string\n iconName: string\n accentColor: string\n isActive: boolean\n trigger: () => void\n }\n >\n }\n}\n\ninterface JarveBugReporterProps {\n /** Base URL for the external bug reporter API (e.g. \"https://www.jarve.com.au/api/bug-reporter/external\") */\n apiUrl: string\n /** API key for your site (starts with \"brk_\") */\n apiKey: string\n /** Optional position for the floating button (default: 'right') */\n buttonPosition?: FloatingButtonPosition\n /** Optional user info. If not provided, the AI will ask for name/email during the conversation. */\n user?: BugReporterUser\n children: React.ReactNode\n}\n\nexport function JarveBugReporter({\n apiUrl,\n apiKey,\n user,\n buttonPosition,\n children,\n}: JarveBugReporterProps) {\n const safeApiKey = apiKey || ''\n\n const [captureMode, setCaptureMode] = useState(false)\n const [captureResult, setCaptureResult] = useState<CaptureResult | null>(null)\n const [showModal, setShowModal] = useState(false)\n\n // Always start console + network capture on mount\n useEffect(() => {\n startCapturing()\n startNetworkCapture()\n return () => {\n stopCapturing()\n stopNetworkCapture()\n }\n }, [])\n\n const toggleCaptureMode = useCallback(() => {\n setCaptureMode((prev) => !prev)\n }, [])\n\n // Register with launcher widget registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n if (!window.__jarve_widgets) {\n window.__jarve_widgets = new Map()\n }\n\n window.__jarve_widgets.set('bug-reporter', {\n type: 'bug-reporter',\n label: 'Report a Bug',\n tooltip:\n \"Spotted something broken? Click here to take a screenshot, describe the problem, and we'll handle the rest.\",\n iconName: 'Bug',\n accentColor: 'red',\n isActive: false,\n trigger: toggleCaptureMode,\n })\n\n window.dispatchEvent(\n new CustomEvent('jarve:widget-registered', { detail: { type: 'bug-reporter' } }),\n )\n\n return () => {\n window.__jarve_widgets?.delete('bug-reporter')\n window.dispatchEvent(\n new CustomEvent('jarve:widget-deregistered', { detail: { type: 'bug-reporter' } }),\n )\n }\n }, [toggleCaptureMode])\n\n // Sync active state with launcher registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n const entry = window.__jarve_widgets?.get('bug-reporter')\n if (entry) {\n entry.isActive = captureMode || showModal\n window.dispatchEvent(\n new CustomEvent('jarve:state-change', { detail: { type: 'bug-reporter' } }),\n )\n }\n }, [captureMode, showModal])\n\n const handleCapture = useCallback((result: CaptureResult) => {\n setCaptureResult(result)\n setCaptureMode(false)\n setShowModal(true)\n }, [])\n\n const handleCancelCapture = useCallback(() => {\n setCaptureMode(false)\n }, [])\n\n const handleCloseModal = useCallback(() => {\n setShowModal(false)\n setCaptureResult(null)\n clearCapturedErrors()\n clearCapturedNetworkErrors()\n }, [])\n\n // Derive a stable siteId from the apiKey for metadata\n const siteId = safeApiKey.startsWith('brk_') ? safeApiKey.slice(4, 12) : 'external'\n const reporterName = user?.name || 'Anonymous'\n const reporterEmail = user?.email || 'unknown@external'\n\n return (\n <>\n {children}\n\n <FloatingButton\n isActive={captureMode}\n onClick={toggleCaptureMode}\n position={buttonPosition}\n />\n\n <CaptureOverlay\n isActive={captureMode}\n siteId={siteId}\n reporterName={reporterName}\n reporterEmail={reporterEmail}\n onCapture={handleCapture}\n onCancel={handleCancelCapture}\n />\n\n <ReportModal\n isOpen={showModal}\n captureResult={captureResult}\n apiConfig={{ apiUrl, apiKey }}\n siteId={siteId}\n user={{ name: reporterName, email: reporterEmail }}\n onClose={handleCloseModal}\n />\n </>\n )\n}\n","import { useState, useEffect, useRef } from 'react'\nimport { Bug } from 'lucide-react'\nimport { cn } from './cn'\n\nexport type FloatingButtonPosition = 'left' | 'right'\n\ninterface FloatingButtonProps {\n isActive: boolean\n onClick: () => void\n position?: FloatingButtonPosition\n}\n\nconst STACK_OFFSET = 56 // button height (~48px) + gap (8px)\n\nexport function FloatingButton({ isActive, onClick, position = 'right' }: FloatingButtonProps) {\n const [hovered, setHovered] = useState(false)\n const [stackOffset, setStackOffset] = useState(0)\n const [launcherPresent, setLauncherPresent] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const isLeft = position === 'left'\n const sideClasses = isLeft ? 'left-4 md:left-6' : 'right-4 md:right-6'\n\n // Auto-stack with other @jarve widgets on the same side\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n let rafId: number\n\n const recalculate = () => {\n const widgets = Array.from(\n document.querySelectorAll(`[data-jarve-widget][data-jarve-position=\"${position}\"]`),\n )\n // Sort by widget name for deterministic ordering\n widgets.sort((a, b) =>\n (a.getAttribute('data-jarve-widget') || '').localeCompare(\n b.getAttribute('data-jarve-widget') || '',\n ),\n )\n const index = widgets.indexOf(el)\n setStackOffset(index > 0 ? index * STACK_OFFSET : 0)\n }\n\n recalculate()\n\n // Watch for siblings appearing/disappearing\n const observer = new MutationObserver(() => {\n cancelAnimationFrame(rafId)\n rafId = requestAnimationFrame(recalculate)\n })\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => {\n observer.disconnect()\n cancelAnimationFrame(rafId)\n }\n }, [position])\n\n // Hide when launcher is present\n useEffect(() => {\n const checkLauncher = () => {\n setLauncherPresent(!!document.querySelector('[data-jarve-launcher]'))\n }\n\n checkLauncher()\n\n const observer = new MutationObserver(checkLauncher)\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => observer.disconnect()\n }, [])\n\n if (launcherPresent) return null\n\n return (\n <div\n ref={ref}\n data-jarve-widget=\"bug-reporter\"\n data-jarve-position={position}\n className={cn('fixed z-[9999]', 'bottom-4 md:bottom-6', sideClasses)}\n style={stackOffset > 0 ? { transform: `translateY(-${stackOffset}px)` } : undefined}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n {/* Tooltip */}\n <div\n className={cn(\n 'pointer-events-none absolute bottom-full mb-3 w-max max-w-[240px] rounded-xl bg-gray-900/95 px-3.5 py-2.5 text-xs leading-relaxed text-white shadow-xl backdrop-blur-sm transition-all duration-200',\n isLeft ? 'left-0' : 'right-0',\n hovered && !isActive ? 'translate-y-0 opacity-100' : 'translate-y-1 opacity-0',\n )}\n >\n <span className=\"font-semibold text-red-300\">🐛 Report a Bug</span>\n <br />\n <span className=\"text-gray-300\">\n Spotted something broken? Click here to take a screenshot, describe the problem, and\n we&apos;ll handle the rest.\n </span>\n {/* Arrow */}\n <div\n className={cn(\n 'absolute top-full h-0 w-0 border-x-[6px] border-t-[6px] border-x-transparent border-t-gray-900',\n isLeft ? 'left-4' : 'right-4',\n )}\n />\n </div>\n\n {/* Button */}\n <button\n onClick={onClick}\n className={cn(\n 'flex items-center justify-center rounded-full shadow-lg transition-all duration-200',\n 'hover:scale-110 focus:ring-2 focus:ring-offset-2 focus:outline-none',\n 'h-11 w-11 md:h-12 md:w-12',\n isActive\n ? 'animate-pulse bg-red-500 text-white focus:ring-red-400'\n : 'bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-400',\n )}\n title={isActive ? 'Cancel bug capture' : 'Report a bug'}\n aria-label={isActive ? 'Cancel bug capture' : 'Report a bug'}\n >\n <Bug className=\"h-4 w-4 md:h-5 md:w-5\" />\n </button>\n </div>\n )\n}\n","import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useEffect, useState, useCallback, useRef } from 'react'\nimport { toPng } from 'html-to-image'\nimport {\n getNearestSection,\n collectMetadata,\n collectElementInfo,\n isTouchCapable,\n extractCoordinates,\n} from './utils'\nimport { getCapturedErrors } from './console-capture'\nimport { getCapturedNetworkErrors } from './network-capture'\nimport type {\n CaptureResult,\n CaptureMetadata,\n ConsoleError,\n FailedNetworkRequest,\n PointerCoordinates,\n} from './types'\n\ninterface CaptureOverlayProps {\n isActive: boolean\n siteId: string\n reporterName: string\n reporterEmail: string\n onCapture: (result: CaptureResult) => void\n onCancel: () => void\n}\n\nfunction dataUrlToBlob(dataUrl: string): Blob {\n const [header, base64] = dataUrl.split(',')\n const mime = header.match(/:(.*?);/)?.[1] || 'image/png'\n const bytes = atob(base64)\n const arr = new Uint8Array(bytes.length)\n for (let i = 0; i < bytes.length; i++) arr[i] = bytes.charCodeAt(i)\n return new Blob([arr], { type: mime })\n}\n\nexport function CaptureOverlay({\n isActive,\n siteId,\n reporterName,\n reporterEmail,\n onCapture,\n onCancel,\n}: CaptureOverlayProps) {\n const [hoveredElement, setHoveredElement] = useState<HTMLElement | null>(null)\n const [hoveredRect, setHoveredRect] = useState<DOMRect | null>(null)\n const [isCapturing, setIsCapturing] = useState(false)\n const [isTouchMode, setIsTouchMode] = useState(false)\n const [selectedSection, setSelectedSection] = useState<HTMLElement | null>(null)\n const [selectedRect, setSelectedRect] = useState<DOMRect | null>(null)\n const [selectedTarget, setSelectedTarget] = useState<HTMLElement | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n const hoveredElementRef = useRef<HTMLElement | null>(null)\n const rafRef = useRef<number | null>(null)\n const touchCoordsRef = useRef<PointerCoordinates | null>(null)\n\n // Initialize touch mode on activation\n useEffect(() => {\n if (isActive) {\n setIsTouchMode(isTouchCapable())\n }\n }, [isActive])\n\n const captureScreenshot = useCallback(\n async (section: HTMLElement, target: HTMLElement, coords: PointerCoordinates) => {\n const elementInfo = collectElementInfo(target, section, coords)\n setIsCapturing(true)\n\n try {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n\n await new Promise((r) => setTimeout(r, 50))\n\n const MAX_DIMENSION = 2000\n const sectionRect = section.getBoundingClientRect()\n const pixelRatio =\n sectionRect.width > MAX_DIMENSION || sectionRect.height > MAX_DIMENSION ? 1 : 2\n\n const dataUrl = await toPng(section, {\n quality: 0.9,\n pixelRatio,\n skipFonts: true,\n })\n\n const blob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: blob, metadata, consoleErrors, networkErrors })\n } catch (err) {\n console.warn(\n 'Bug reporter: first capture attempt failed, retrying with simpler settings',\n err,\n )\n\n try {\n const dataUrl = await toPng(section, {\n quality: 0.6,\n pixelRatio: 1,\n skipFonts: true,\n cacheBust: true,\n })\n\n const retryBlob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: retryBlob, metadata, consoleErrors, networkErrors })\n } catch {\n console.error('Bug reporter: screenshot capture failed after retry')\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n onCapture({\n screenshot: new Blob(),\n metadata: { ...metadata, screenshotCaptureFailed: true },\n consoleErrors,\n networkErrors,\n })\n }\n } finally {\n setIsCapturing(false)\n }\n },\n [siteId, reporterName, reporterEmail, onCapture],\n )\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n if (rafRef.current) return\n\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n\n if (target.closest('[data-bug-reporter]')) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n return\n }\n\n const section = getNearestSection(target)\n setHoveredElement(section)\n hoveredElementRef.current = section\n setHoveredRect(section ? section.getBoundingClientRect() : null)\n })\n },\n [isActive, isCapturing, isTouchMode],\n )\n\n const handleClick = useCallback(\n async (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const section = getNearestSection(target)\n if (!section) return\n\n await captureScreenshot(section, target, extractCoordinates(e))\n },\n [isActive, isCapturing, isTouchMode, captureScreenshot],\n )\n\n const handleTouchEnd = useCallback(\n (e: TouchEvent) => {\n if (!isActive || isCapturing) return\n\n const touch = e.changedTouches[0]\n if (!touch) return\n\n const target = document.elementFromPoint(touch.clientX, touch.clientY)\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n const section = getNearestSection(target)\n if (!section) return\n\n setSelectedSection(section)\n setSelectedRect(section.getBoundingClientRect())\n setSelectedTarget(target)\n touchCoordsRef.current = extractCoordinates(touch)\n },\n [isActive, isCapturing],\n )\n\n const handleConfirmCapture = useCallback(async () => {\n if (!selectedSection || !selectedTarget || !touchCoordsRef.current) return\n await captureScreenshot(selectedSection, selectedTarget, touchCoordsRef.current)\n }, [selectedSection, selectedTarget, captureScreenshot])\n\n const handlePointerDown = useCallback(\n (e: PointerEvent) => {\n if (!isActive) return\n if (e.pointerType === 'touch') {\n setIsTouchMode(true)\n } else if (e.pointerType === 'mouse') {\n setIsTouchMode(false)\n }\n },\n [isActive],\n )\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isActive) {\n e.preventDefault()\n e.stopPropagation()\n onCancel()\n }\n },\n [isActive, onCancel],\n )\n\n const handleScroll = useCallback(() => {\n if (hoveredElementRef.current) {\n setHoveredRect(hoveredElementRef.current.getBoundingClientRect())\n }\n if (selectedSection) {\n setSelectedRect(selectedSection.getBoundingClientRect())\n }\n }, [selectedSection])\n\n useEffect(() => {\n if (!isActive) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n return\n }\n\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n window.addEventListener('scroll', handleScroll, { passive: true })\n\n if (isTouchMode) {\n document.addEventListener('touchend', handleTouchEnd, { passive: true })\n } else {\n document.addEventListener('mousemove', handleMouseMove, true)\n document.addEventListener('click', handleClick, true)\n }\n\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n window.removeEventListener('scroll', handleScroll)\n document.removeEventListener('touchend', handleTouchEnd)\n document.removeEventListener('mousemove', handleMouseMove, true)\n document.removeEventListener('click', handleClick, true)\n if (rafRef.current) cancelAnimationFrame(rafRef.current)\n }\n }, [\n isActive,\n isTouchMode,\n handleMouseMove,\n handleClick,\n handleTouchEnd,\n handlePointerDown,\n handleKeyDown,\n handleScroll,\n ])\n\n const highlightRect = isTouchMode ? selectedRect : hoveredRect\n const showHighlight = isTouchMode ? !!selectedSection : !!hoveredElement && !!hoveredRect\n\n if (!isActive) return null\n\n return (\n <>\n {/* Instruction banner */}\n <div\n data-bug-reporter\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"fixed top-0 right-0 left-0 z-[10000] flex items-center justify-center gap-3 bg-indigo-600 px-4 py-2 text-center text-sm font-medium text-white\"\n >\n {isTouchMode ? (\n <>\n <span>Tap the section with the bug</span>\n <button\n onClick={onCancel}\n className=\"min-h-[44px] rounded-md bg-white/20 px-3 py-1 text-sm font-medium\"\n >\n Cancel\n </button>\n </>\n ) : (\n <>\n Click on the section with the bug. Press{' '}\n <kbd className=\"mx-1 rounded bg-indigo-800 px-1.5 py-0.5 text-xs\">Esc</kbd> to cancel.\n </>\n )}\n </div>\n\n {/* Section highlight overlay */}\n {showHighlight && highlightRect && (\n <div\n ref={overlayRef}\n data-bug-reporter\n className=\"pointer-events-none fixed z-[9998] rounded-sm border-2 border-indigo-500 transition-all duration-150 ease-out\"\n style={{\n top: highlightRect.top - 2,\n left: highlightRect.left - 2,\n width: highlightRect.width + 4,\n height: highlightRect.height + 4,\n backgroundColor: 'rgba(99, 102, 241, 0.08)',\n }}\n />\n )}\n\n {/* Touch confirmation bar */}\n {isTouchMode && selectedSection && !isCapturing && (\n <div\n data-bug-reporter\n className=\"fixed right-0 bottom-0 left-0 z-[10000] border-t border-gray-200 bg-white shadow-lg\"\n style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}\n >\n <div className=\"flex items-center justify-between gap-3 px-4 py-3\">\n <span className=\"truncate text-sm font-medium text-gray-900\">\n Capture this section?\n </span>\n <div className=\"flex shrink-0 gap-2\">\n <button\n onClick={() => {\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n }}\n className=\"min-h-[44px] rounded-md border border-gray-300 px-4 text-sm font-medium text-gray-700\"\n >\n Cancel\n </button>\n <button\n onClick={handleConfirmCapture}\n className=\"min-h-[44px] rounded-md bg-indigo-600 px-4 text-sm font-medium text-white\"\n >\n Capture\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Full-page cursor override — desktop only */}\n {!isTouchMode && <style>{`* { cursor: crosshair !important; }`}</style>}\n </>\n )\n}\n","import { UAParser } from 'ua-parser-js'\nimport type { CaptureMetadata, ClickedElement, PointerCoordinates } from './types'\n\n/**\n * Walk up the DOM tree to find the nearest semantically meaningful section.\n */\nexport function getNearestSection(element: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = element\n\n while (current && current !== document.body) {\n if (\n current.id ||\n current.dataset.section ||\n current.tagName === 'SECTION' ||\n current.tagName === 'MAIN' ||\n current.tagName === 'ARTICLE' ||\n current.tagName === 'NAV' ||\n current.tagName === 'HEADER' ||\n current.tagName === 'FOOTER' ||\n current.role === 'main' ||\n current.role === 'navigation'\n ) {\n return current\n }\n current = current.parentElement\n }\n\n return document.querySelector('main') || document.body\n}\n\n/**\n * Get a human-readable section identifier from an element.\n */\nexport function getSectionId(element: HTMLElement): string | null {\n if (element.id) return `#${element.id}`\n if (element.dataset.section) return element.dataset.section\n if (element.tagName === 'MAIN') return 'main'\n if (element.tagName === 'SECTION') {\n const heading = element.querySelector('h1, h2, h3')\n if (heading) return heading.textContent?.trim().slice(0, 60) || null\n }\n return element.tagName.toLowerCase()\n}\n\n/**\n * Derive device type from viewport width and touch capability.\n */\nexport function getDeviceType(): 'desktop' | 'tablet' | 'mobile' {\n const width = window.innerWidth\n const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0\n\n if (width <= 768 && hasTouch) return 'mobile'\n if (width <= 1024 && hasTouch) return 'tablet'\n return 'desktop'\n}\n\n/**\n * Parse user agent string into readable browser and OS info.\n */\nexport function parseUserAgent(): { browser: string; os: string } {\n const parser = new UAParser(navigator.userAgent)\n const browser = parser.getBrowser()\n const osInfo = parser.getOS()\n\n return {\n browser: `${browser.name || 'Unknown'} ${browser.version || ''}`.trim(),\n os: `${osInfo.name || 'Unknown'} ${osInfo.version || ''}`.trim(),\n }\n}\n\n/**\n * Build a CSS selector path from an element up to its section container.\n */\nexport function buildSelectorPath(element: HTMLElement, stopAt?: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = element\n\n while (current && current !== document.body && current !== stopAt) {\n let selector = current.tagName.toLowerCase()\n if (current.id) {\n selector += `#${current.id}`\n } else if (current.className && typeof current.className === 'string') {\n const classes = current.className.trim().split(/\\s+/).slice(0, 2).join('.')\n if (classes) selector += `.${classes}`\n }\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\n/**\n * Check if the device supports touch input.\n */\nexport function isTouchCapable(): boolean {\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0\n}\n\n/**\n * Normalize coordinates from MouseEvent or Touch into a common shape.\n */\nexport function extractCoordinates(e: MouseEvent | Touch): PointerCoordinates {\n return {\n pageX: e.pageX,\n pageY: e.pageY,\n clientX: e.clientX,\n clientY: e.clientY,\n }\n}\n\n/**\n * Collect info about the exact element the user clicked/tapped.\n */\nexport function collectElementInfo(\n target: HTMLElement,\n section: HTMLElement,\n coords: PointerCoordinates,\n): ClickedElement {\n const sectionRect = section.getBoundingClientRect()\n\n const dataAttributes: Record<string, string> = {}\n for (const attr of Array.from(target.attributes)) {\n if (attr.name.startsWith('data-') && attr.name !== 'data-bug-reporter') {\n dataAttributes[attr.name] = attr.value\n }\n }\n\n return {\n tagName: target.tagName,\n textContent: (target.textContent || '').trim().slice(0, 200) || null,\n className: typeof target.className === 'string' ? target.className : '',\n id: target.id || null,\n ariaLabel: target.getAttribute('aria-label') || null,\n dataAttributes,\n selectorPath: buildSelectorPath(target, section),\n clickX: coords.pageX,\n clickY: coords.pageY,\n relativeClickX: coords.clientX - sectionRect.left,\n relativeClickY: coords.clientY - sectionRect.top,\n }\n}\n\n/**\n * Collect all metadata at the moment of capture.\n */\nexport function collectMetadata(\n sectionElement: HTMLElement,\n siteId: string,\n reporterName: string,\n reporterEmail: string,\n clickedElement?: ClickedElement,\n): CaptureMetadata {\n const { browser, os } = parseUserAgent()\n const now = new Date()\n\n return {\n pageUrl: window.location.href,\n sectionId: getSectionId(sectionElement),\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n deviceType: getDeviceType(),\n browser,\n os,\n timestamp: now.toISOString(),\n timestampUtc: now.toUTCString(),\n siteId,\n reporterName,\n reporterEmail,\n clickedElement,\n }\n}\n","import type { ConsoleError } from './types'\n\nconst MAX_ERRORS = 50\n\nlet capturedErrors: ConsoleError[] = []\nlet isCapturing = false\nlet originalConsoleError: typeof console.error | null = null\nlet errorListener: ((event: ErrorEvent) => void) | null = null\nlet rejectionListener: ((event: PromiseRejectionEvent) => void) | null = null\n\n/**\n * Start capturing console errors and unhandled exceptions.\n * Stores the last N errors for inclusion in bug reports.\n */\nexport function startCapturing(): void {\n if (isCapturing) return\n if ((console.error as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedErrors = []\n\n originalConsoleError = console.error\n const patchedConsoleError = (...args: unknown[]) => {\n const MAX_MSG_LEN = 500\n const message = args\n .map((a) => {\n if (typeof a === 'string') return a.slice(0, MAX_MSG_LEN)\n try {\n const s = JSON.stringify(a)\n return s.slice(0, MAX_MSG_LEN)\n } catch {\n return String(a).slice(0, MAX_MSG_LEN)\n }\n })\n .join(' ')\n .slice(0, MAX_MSG_LEN)\n\n capturedErrors.push({\n message,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n\n originalConsoleError!.apply(console, args)\n }\n ;(patchedConsoleError as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n console.error = patchedConsoleError\n\n errorListener = (event: ErrorEvent) => {\n capturedErrors.push({\n message: event.message,\n source: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('error', errorListener)\n\n rejectionListener = (event: PromiseRejectionEvent) => {\n capturedErrors.push({\n message: `Unhandled Promise Rejection: ${event.reason instanceof Error ? event.reason.message : String(event.reason)}`,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('unhandledrejection', rejectionListener)\n}\n\n/**\n * Stop capturing and restore original console.error.\n */\nexport function stopCapturing(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalConsoleError) {\n const restoreTarget = originalConsoleError\n console.error = restoreTarget\n if (console.error !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original console.error')\n }\n originalConsoleError = null\n }\n\n if (errorListener) {\n window.removeEventListener('error', errorListener)\n errorListener = null\n }\n\n if (rejectionListener) {\n window.removeEventListener('unhandledrejection', rejectionListener)\n rejectionListener = null\n }\n}\n\n/**\n * Get a copy of all captured errors.\n */\nexport function getCapturedErrors(): ConsoleError[] {\n return [...capturedErrors]\n}\n\n/**\n * Clear the captured errors buffer.\n */\nexport function clearCapturedErrors(): void {\n capturedErrors = []\n}\n","import type { FailedNetworkRequest } from './types'\n\nconst MAX_REQUESTS = 30\n/** Skip reading bodies larger than 64 KB to avoid memory/latency cost */\nconst MAX_BODY_READ_BYTES = 64 * 1024\nconst TRUNCATE_LEN = 500\n\nlet capturedRequests: FailedNetworkRequest[] = []\nlet isCapturing = false\nlet originalFetch: typeof window.fetch | null = null\n\nfunction truncateBody(body: string | null | undefined, maxLen = TRUNCATE_LEN): string | null {\n if (!body) return null\n if (body.length <= maxLen) return body\n return body.slice(0, maxLen) + '...(truncated)'\n}\n\n/**\n * Read a bounded amount of the response body for error capture.\n * Returns truncated text for small bodies, or a size-only metadata\n * string when the body exceeds MAX_BODY_READ_BYTES.\n */\nasync function readBoundedBody(response: Response): Promise<string | null> {\n try {\n // Fast path: check Content-Length header\n const contentLength = response.headers.get('Content-Length')\n if (contentLength) {\n const size = parseInt(contentLength, 10)\n if (!isNaN(size) && size > MAX_BODY_READ_BYTES) {\n return `[body too large: ${size} bytes]`\n }\n }\n\n // Stream-based bounded read when ReadableStream is available\n const cloned = response.clone()\n if (cloned.body && typeof cloned.body.getReader === 'function') {\n const reader = cloned.body.getReader()\n const chunks: Uint8Array[] = []\n let totalBytes = 0\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n totalBytes += value.byteLength\n if (totalBytes > MAX_BODY_READ_BYTES) {\n reader.cancel().catch(() => {})\n return `[body too large: >${MAX_BODY_READ_BYTES} bytes]`\n }\n chunks.push(value)\n }\n\n const decoder = new TextDecoder()\n const text = chunks.map((c) => decoder.decode(c, { stream: true })).join('')\n return truncateBody(text)\n }\n\n // Fallback: clone().text() for environments without ReadableStream\n const text = await cloned.text()\n return truncateBody(text)\n } catch {\n // Body may not be readable\n return null\n }\n}\n\n/**\n * Start capturing failed network requests (fetch only).\n * Intercepts window.fetch to log requests that return status >= 400 or throw.\n */\nexport function startNetworkCapture(): void {\n if (isCapturing || typeof window === 'undefined') return\n if ((window.fetch as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedRequests = []\n\n originalFetch = window.fetch\n const patchedFetch = async function patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n const method =\n init?.method ||\n (typeof input !== 'string' && !(input instanceof URL) ? input.method : 'GET') ||\n 'GET'\n\n // Skip our own bug reporter endpoints and non-HTTP URLs\n if (\n url.includes('/api/bug-reporter/') ||\n url.includes('/bug-reporter/external/') ||\n url.startsWith('data:') ||\n url.startsWith('blob:')\n ) {\n return originalFetch!.call(window, input, init)\n }\n\n try {\n const response = await originalFetch!.call(window, input, init)\n\n if (response.status >= 400) {\n const responseBody = await readBoundedBody(response)\n\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: response.status,\n statusText: response.statusText,\n responseBody,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n }\n\n return response\n } catch (error) {\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: 0,\n statusText: error instanceof Error ? error.message : 'Network error',\n responseBody: null,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n\n throw error\n }\n }\n ;(patchedFetch as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n window.fetch = patchedFetch\n}\n\n/**\n * Stop capturing and restore original fetch.\n */\nexport function stopNetworkCapture(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalFetch) {\n const restoreTarget = originalFetch\n window.fetch = restoreTarget\n if (window.fetch !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original fetch')\n }\n originalFetch = null\n }\n}\n\n/**\n * Get a copy of all captured failed requests.\n */\nexport function getCapturedNetworkErrors(): FailedNetworkRequest[] {\n return [...capturedRequests]\n}\n\n/**\n * Clear the captured requests buffer.\n */\nexport function clearCapturedNetworkErrors(): void {\n capturedRequests = []\n}\n","import { useState, useRef, useEffect, useCallback, useMemo } from 'react'\nimport { X, Send, Loader2, CheckCircle2 } from 'lucide-react'\nimport { cn } from './cn'\nimport { isTouchCapable } from './utils'\nimport type {\n CaptureResult,\n ConversationMessage,\n BugReporterApiConfig,\n BugReporterUser,\n} from './types'\n\ninterface ReportModalProps {\n isOpen: boolean\n captureResult: CaptureResult | null\n apiConfig: BugReporterApiConfig\n siteId: string\n user: BugReporterUser\n onClose: () => void\n}\n\ntype ModalState = 'chatting' | 'submitting' | 'submitted' | 'error'\n\nexport function ReportModal({\n isOpen,\n captureResult,\n apiConfig,\n siteId,\n user,\n onClose,\n}: ReportModalProps) {\n const [messages, setMessages] = useState<ConversationMessage[]>([])\n const [input, setInput] = useState('')\n const [isLoading, setIsLoading] = useState(false)\n const [modalState, setModalState] = useState<ModalState>('chatting')\n const [reportId, setReportId] = useState<string | null>(null)\n const [screenshotUrl, setScreenshotUrl] = useState<string | null>(null)\n const [errorMessage, setErrorMessage] = useState<string | null>(null)\n const chatEndRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLTextAreaElement>(null)\n const hasInitRef = useRef(false)\n\n const apiHeaders = useMemo<Record<string, string>>(\n () => ({\n 'Content-Type': 'application/json',\n 'X-Bug-Reporter-Key': apiConfig.apiKey.trim(),\n }),\n [apiConfig.apiKey],\n )\n\n // Create screenshot preview URL — revoke previous URL on replacement and\n // reset to null when there is no valid screenshot.\n useEffect(() => {\n if (captureResult?.screenshot && captureResult.screenshot.size > 0) {\n const url = URL.createObjectURL(captureResult.screenshot)\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return url\n })\n return () => {\n URL.revokeObjectURL(url)\n setScreenshotUrl(null)\n }\n }\n\n // No valid screenshot — clear stale URL\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return null\n })\n }, [captureResult])\n\n // Stable callback for sending initial AI message\n const sendInitialMessage = useCallback(async () => {\n if (!captureResult) return\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: [],\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n setMessages([{ role: 'assistant', content: data.message }])\n } catch {\n setMessages([\n {\n role: 'assistant',\n content:\n \"I can see you've captured a section of the page. What's going wrong here? Please describe the issue you're experiencing.\",\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }, [captureResult, apiConfig.apiUrl, apiHeaders])\n\n // Send initial AI message when modal opens\n useEffect(() => {\n if (isOpen && captureResult && !hasInitRef.current) {\n hasInitRef.current = true\n sendInitialMessage()\n }\n }, [isOpen, captureResult, sendInitialMessage])\n\n // Auto-scroll chat\n useEffect(() => {\n chatEndRef.current?.scrollIntoView({ behavior: 'smooth' })\n }, [messages])\n\n // Focus input\n useEffect(() => {\n if (isOpen && !isLoading) {\n inputRef.current?.focus()\n }\n }, [isOpen, isLoading, messages])\n\n const submitReport = useCallback(\n async (\n conversation: ConversationMessage[],\n structuredReport?: {\n summary: string\n expected_behavior: string\n actual_behavior: string\n reproducibility: string\n specific_data: string\n severity: string\n },\n ) => {\n if (!captureResult || modalState !== 'chatting') return\n setModalState('submitting')\n\n try {\n // Convert screenshot blob to base64\n let screenshotBase64: string | undefined\n if (captureResult.screenshot.size > 0) {\n const reader = new FileReader()\n screenshotBase64 = await new Promise<string>((resolve, reject) => {\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(captureResult.screenshot)\n })\n }\n\n // If no structured report from AI, request one via the API\n let report = structuredReport\n if (!report) {\n try {\n const summaryResponse = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: conversation,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n requestSummary: true,\n }),\n })\n if (summaryResponse.ok) {\n const data = await summaryResponse.json()\n report = data.structuredReport\n }\n } catch {\n // Fall through to default\n }\n }\n\n // Submit via the external submit endpoint\n const response = await fetch(`${apiConfig.apiUrl}/submit`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n screenshot: screenshotBase64,\n metadata: captureResult.metadata,\n conversation,\n structuredReport: report,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}))\n throw new Error(errorData.error || 'Failed to submit report')\n }\n\n const data = await response.json()\n setReportId(data.id)\n setModalState('submitted')\n } catch (err) {\n console.error('Bug reporter: failed to submit report', err)\n setErrorMessage(err instanceof Error ? err.message : 'Failed to submit report')\n setModalState('error')\n }\n },\n [captureResult, apiConfig.apiUrl, apiHeaders, modalState],\n )\n\n const handleManualSubmit = useCallback(() => {\n submitReport(messages)\n }, [submitReport, messages])\n\n async function sendMessage() {\n if (!input.trim() || isLoading || !captureResult) return\n\n const userMessage = input.trim()\n setInput('')\n\n const newMessages: ConversationMessage[] = [...messages, { role: 'user', content: userMessage }]\n setMessages(newMessages)\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: newMessages,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n\n setMessages([...newMessages, { role: 'assistant', content: data.message }])\n\n if (data.readyToSubmit && data.structuredReport) {\n await submitReport(\n [...newMessages, { role: 'assistant', content: data.message }],\n data.structuredReport,\n )\n }\n } catch {\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content: 'I had trouble processing that. Could you try describing the issue again?',\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }\n\n function handleClose() {\n setMessages([])\n setInput('')\n setModalState('chatting')\n setReportId(null)\n setErrorMessage(null)\n // Revoke and clear stale screenshot URL\n if (screenshotUrl) {\n URL.revokeObjectURL(screenshotUrl)\n }\n setScreenshotUrl(null)\n hasInitRef.current = false\n onClose()\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Enter' && !e.shiftKey && !isTouchCapable()) {\n e.preventDefault()\n sendMessage()\n }\n }\n\n if (!isOpen) return null\n\n return (\n <div\n data-bug-reporter\n className=\"fixed inset-0 z-[10001] flex items-center justify-center bg-black/50 backdrop-blur-sm\"\n onClick={(e) => {\n if (e.target === e.currentTarget) handleClose()\n }}\n >\n <div\n className={cn(\n 'flex flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-gray-950',\n 'mx-4 w-full max-w-lg',\n 'max-[768px]:mx-0 max-[768px]:h-full max-[768px]:max-w-none max-[768px]:rounded-none',\n 'min-[769px]:max-h-[85vh]',\n )}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-gray-200 bg-gray-50/30 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/30\">\n <div>\n <h2 className=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">Bug Report</h2>\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">{siteId}</p>\n </div>\n <button\n onClick={handleClose}\n className=\"rounded-md p-1.5 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-gray-600 dark:text-gray-400\" />\n </button>\n </div>\n\n {/* Screenshot preview */}\n {screenshotUrl && (\n <div className=\"border-b border-gray-200 bg-gray-50/10 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/10\">\n <img\n src={screenshotUrl}\n alt=\"Captured section\"\n className=\"max-h-40 w-full rounded-md border border-gray-200 object-contain dark:border-gray-700\"\n />\n </div>\n )}\n\n {/* Chat area */}\n <div className=\"min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-3\">\n {modalState === 'submitted' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <CheckCircle2 className=\"mb-3 h-12 w-12 text-green-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Report Submitted\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n Reference:{' '}\n <code className=\"rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-800\">\n {reportId?.slice(0, 8)}\n </code>\n </p>\n <p className=\"mt-2 text-sm text-gray-500 dark:text-gray-400\">\n Thanks for the report — we&apos;ll look into it.\n </p>\n <button\n onClick={handleClose}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Done\n </button>\n </div>\n ) : modalState === 'error' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <X className=\"mb-3 h-12 w-12 text-red-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Submission Failed\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n {errorMessage || 'Something went wrong. Please try again.'}\n </p>\n <button\n onClick={() => setModalState('chatting')}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Try Again\n </button>\n </div>\n ) : modalState === 'submitting' ? (\n <div className=\"flex flex-col items-center justify-center py-8\">\n <Loader2 className=\"mb-3 h-8 w-8 animate-spin text-indigo-500\" />\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">Submitting your report...</p>\n </div>\n ) : (\n <>\n {captureResult?.screenshot.size === 0 && (\n <p className=\"text-xs text-amber-600\">\n Screenshot could not be captured. Please describe the visual issue in detail.\n </p>\n )}\n\n {messages.map((msg, i) => (\n <div\n key={i}\n className={cn(\n 'text-sm leading-relaxed',\n msg.role === 'assistant'\n ? 'rounded-lg bg-gray-100/50 p-3 text-gray-900 dark:bg-gray-800/50 dark:text-gray-100'\n : 'ml-8 rounded-lg bg-indigo-600 p-3 text-white',\n )}\n >\n {msg.content}\n </div>\n ))}\n\n {isLoading && (\n <div className=\"flex items-center gap-2 rounded-lg bg-gray-100/50 p-3 dark:bg-gray-800/50\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin text-gray-500\" />\n <span className=\"text-sm text-gray-500 dark:text-gray-400\">Thinking...</span>\n </div>\n )}\n\n <div ref={chatEndRef} />\n </>\n )}\n </div>\n\n {/* Input area — only show during chatting */}\n {modalState === 'chatting' && (\n <div className=\"border-t border-gray-200 px-4 py-3 dark:border-gray-800\">\n <div className=\"flex flex-col gap-2\">\n <textarea\n ref={inputRef}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe what's wrong...\"\n rows={2}\n className=\"w-full resize-none rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 focus:border-transparent focus:ring-2 focus:ring-indigo-400 focus:outline-none dark:border-gray-700 dark:bg-gray-950 dark:text-gray-100\"\n disabled={isLoading}\n />\n <div className=\"flex flex-col gap-2\">\n {captureResult &&\n (captureResult.consoleErrors.length > 0 ||\n captureResult.networkErrors.length > 0) && (\n <p className=\"flex-1 text-xs text-amber-600\">\n {[\n captureResult.consoleErrors.length > 0\n ? `${captureResult.consoleErrors.length} console error${captureResult.consoleErrors.length !== 1 ? 's' : ''}`\n : null,\n captureResult.networkErrors.length > 0\n ? `${captureResult.networkErrors.length} failed request${captureResult.networkErrors.length !== 1 ? 's' : ''}`\n : null,\n ]\n .filter(Boolean)\n .join(' + ')}{' '}\n captured — these will be included in the report.\n </p>\n )}\n <div className=\"flex justify-end gap-2\">\n <button\n onClick={sendMessage}\n disabled={!input.trim() || isLoading}\n className=\"flex items-center gap-1 rounded-md bg-indigo-600 px-3 py-2 text-nowrap text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"Send message\"\n >\n <Send className=\"h-4 w-4\" />\n <span className=\"text-sm font-medium\">Send message</span>\n </button>\n {messages.length >= 2 && (\n <button\n onClick={handleManualSubmit}\n disabled={isLoading}\n className=\"rounded-md bg-green-600 px-3 py-2 text-xs font-medium text-nowrap text-white transition-colors hover:bg-green-700 disabled:opacity-50\"\n title=\"Submit report now\"\n >\n <span className=\"text-sm font-medium\">Submit report</span>\n </button>\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_react","isCapturing","text","patchedFetch","import_jsx_runtime","isCapturing","import_react","import_lucide_react","import_jsx_runtime","data","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -791,7 +791,7 @@ function ReportModal({
791
791
  const apiHeaders = useMemo(
792
792
  () => ({
793
793
  "Content-Type": "application/json",
794
- "X-Bug-Reporter-Key": apiConfig.apiKey
794
+ "X-Bug-Reporter-Key": apiConfig.apiKey.trim()
795
795
  }),
796
796
  [apiConfig.apiKey]
797
797
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bug-reporter.tsx","../src/floating-button.tsx","../src/cn.ts","../src/capture-overlay.tsx","../src/utils.ts","../src/console-capture.ts","../src/network-capture.ts","../src/report-modal.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react'\nimport { FloatingButton, type FloatingButtonPosition } from './floating-button'\nimport { CaptureOverlay } from './capture-overlay'\nimport { ReportModal } from './report-modal'\nimport { startCapturing, stopCapturing, clearCapturedErrors } from './console-capture'\nimport {\n startNetworkCapture,\n stopNetworkCapture,\n clearCapturedNetworkErrors,\n} from './network-capture'\nimport type { CaptureResult, BugReporterUser } from './types'\n\ndeclare global {\n interface Window {\n __jarve_widgets?: Map<string, {\n type: string\n label: string\n tooltip: string\n iconName: string\n accentColor: string\n isActive: boolean\n trigger: () => void\n }>\n }\n}\n\ninterface JarveBugReporterProps {\n /** Base URL for the external bug reporter API (e.g. \"https://www.jarve.com.au/api/bug-reporter/external\") */\n apiUrl: string\n /** API key for your site (starts with \"brk_\") */\n apiKey: string\n /** Optional position for the floating button (default: 'right') */\n buttonPosition?: FloatingButtonPosition\n /** Optional user info. If not provided, the AI will ask for name/email during the conversation. */\n user?: BugReporterUser\n children: React.ReactNode\n}\n\nexport function JarveBugReporter({\n apiUrl,\n apiKey,\n user,\n buttonPosition,\n children,\n}: JarveBugReporterProps) {\n const safeApiKey = apiKey || ''\n\n const [captureMode, setCaptureMode] = useState(false)\n const [captureResult, setCaptureResult] = useState<CaptureResult | null>(null)\n const [showModal, setShowModal] = useState(false)\n\n // Always start console + network capture on mount\n useEffect(() => {\n startCapturing()\n startNetworkCapture()\n return () => {\n stopCapturing()\n stopNetworkCapture()\n }\n }, [])\n\n const toggleCaptureMode = useCallback(() => {\n setCaptureMode((prev) => !prev)\n }, [])\n\n // Register with launcher widget registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n if (!window.__jarve_widgets) {\n window.__jarve_widgets = new Map()\n }\n\n window.__jarve_widgets.set('bug-reporter', {\n type: 'bug-reporter',\n label: 'Report a Bug',\n tooltip: 'Spotted something broken? Click here to take a screenshot, describe the problem, and we\\'ll handle the rest.',\n iconName: 'Bug',\n accentColor: 'red',\n isActive: false,\n trigger: toggleCaptureMode,\n })\n\n window.dispatchEvent(\n new CustomEvent('jarve:widget-registered', { detail: { type: 'bug-reporter' } })\n )\n\n return () => {\n window.__jarve_widgets?.delete('bug-reporter')\n window.dispatchEvent(\n new CustomEvent('jarve:widget-deregistered', { detail: { type: 'bug-reporter' } })\n )\n }\n }, [toggleCaptureMode])\n\n // Sync active state with launcher registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n const entry = window.__jarve_widgets?.get('bug-reporter')\n if (entry) {\n entry.isActive = captureMode || showModal\n window.dispatchEvent(\n new CustomEvent('jarve:state-change', { detail: { type: 'bug-reporter' } })\n )\n }\n }, [captureMode, showModal])\n\n const handleCapture = useCallback((result: CaptureResult) => {\n setCaptureResult(result)\n setCaptureMode(false)\n setShowModal(true)\n }, [])\n\n const handleCancelCapture = useCallback(() => {\n setCaptureMode(false)\n }, [])\n\n const handleCloseModal = useCallback(() => {\n setShowModal(false)\n setCaptureResult(null)\n clearCapturedErrors()\n clearCapturedNetworkErrors()\n }, [])\n\n // Derive a stable siteId from the apiKey for metadata\n const siteId = safeApiKey.startsWith('brk_') ? safeApiKey.slice(4, 12) : 'external'\n const reporterName = user?.name || 'Anonymous'\n const reporterEmail = user?.email || 'unknown@external'\n\n return (\n <>\n {children}\n\n <FloatingButton\n isActive={captureMode}\n onClick={toggleCaptureMode}\n position={buttonPosition}\n />\n\n <CaptureOverlay\n isActive={captureMode}\n siteId={siteId}\n reporterName={reporterName}\n reporterEmail={reporterEmail}\n onCapture={handleCapture}\n onCancel={handleCancelCapture}\n />\n\n <ReportModal\n isOpen={showModal}\n captureResult={captureResult}\n apiConfig={{ apiUrl, apiKey }}\n siteId={siteId}\n user={{ name: reporterName, email: reporterEmail }}\n onClose={handleCloseModal}\n />\n </>\n )\n}\n","import { useState, useEffect, useRef } from 'react'\nimport { Bug } from 'lucide-react'\nimport { cn } from './cn'\n\nexport type FloatingButtonPosition = 'left' | 'right'\n\ninterface FloatingButtonProps {\n isActive: boolean\n onClick: () => void\n position?: FloatingButtonPosition\n}\n\nconst STACK_OFFSET = 56 // button height (~48px) + gap (8px)\n\nexport function FloatingButton({ isActive, onClick, position = 'right' }: FloatingButtonProps) {\n const [hovered, setHovered] = useState(false)\n const [stackOffset, setStackOffset] = useState(0)\n const [launcherPresent, setLauncherPresent] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const isLeft = position === 'left'\n const sideClasses = isLeft ? 'left-4 md:left-6' : 'right-4 md:right-6'\n\n // Auto-stack with other @jarve widgets on the same side\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n let rafId: number\n\n const recalculate = () => {\n const widgets = Array.from(\n document.querySelectorAll(`[data-jarve-widget][data-jarve-position=\"${position}\"]`),\n )\n // Sort by widget name for deterministic ordering\n widgets.sort((a, b) =>\n (a.getAttribute('data-jarve-widget') || '').localeCompare(\n b.getAttribute('data-jarve-widget') || '',\n ),\n )\n const index = widgets.indexOf(el)\n setStackOffset(index > 0 ? index * STACK_OFFSET : 0)\n }\n\n recalculate()\n\n // Watch for siblings appearing/disappearing\n const observer = new MutationObserver(() => {\n cancelAnimationFrame(rafId)\n rafId = requestAnimationFrame(recalculate)\n })\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => {\n observer.disconnect()\n cancelAnimationFrame(rafId)\n }\n }, [position])\n\n // Hide when launcher is present\n useEffect(() => {\n const checkLauncher = () => {\n setLauncherPresent(!!document.querySelector('[data-jarve-launcher]'))\n }\n\n checkLauncher()\n\n const observer = new MutationObserver(checkLauncher)\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => observer.disconnect()\n }, [])\n\n if (launcherPresent) return null\n\n return (\n <div\n ref={ref}\n data-jarve-widget=\"bug-reporter\"\n data-jarve-position={position}\n className={cn('fixed z-[9999]', 'bottom-4 md:bottom-6', sideClasses)}\n style={stackOffset > 0 ? { transform: `translateY(-${stackOffset}px)` } : undefined}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n {/* Tooltip */}\n <div\n className={cn(\n 'pointer-events-none absolute bottom-full mb-3 w-max max-w-[240px] rounded-xl bg-gray-900/95 px-3.5 py-2.5 text-xs leading-relaxed text-white shadow-xl backdrop-blur-sm transition-all duration-200',\n isLeft ? 'left-0' : 'right-0',\n hovered && !isActive ? 'translate-y-0 opacity-100' : 'translate-y-1 opacity-0',\n )}\n >\n <span className=\"font-semibold text-red-300\">🐛 Report a Bug</span>\n <br />\n <span className=\"text-gray-300\">\n Spotted something broken? Click here to take a screenshot, describe the problem, and\n we&apos;ll handle the rest.\n </span>\n {/* Arrow */}\n <div\n className={cn(\n 'absolute top-full h-0 w-0 border-x-[6px] border-t-[6px] border-x-transparent border-t-gray-900',\n isLeft ? 'left-4' : 'right-4',\n )}\n />\n </div>\n\n {/* Button */}\n <button\n onClick={onClick}\n className={cn(\n 'flex items-center justify-center rounded-full shadow-lg transition-all duration-200',\n 'hover:scale-110 focus:ring-2 focus:ring-offset-2 focus:outline-none',\n 'h-11 w-11 md:h-12 md:w-12',\n isActive\n ? 'animate-pulse bg-red-500 text-white focus:ring-red-400'\n : 'bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-400',\n )}\n title={isActive ? 'Cancel bug capture' : 'Report a bug'}\n aria-label={isActive ? 'Cancel bug capture' : 'Report a bug'}\n >\n <Bug className=\"h-4 w-4 md:h-5 md:w-5\" />\n </button>\n </div>\n )\n}\n","import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useEffect, useState, useCallback, useRef } from 'react'\nimport { toPng } from 'html-to-image'\nimport {\n getNearestSection,\n collectMetadata,\n collectElementInfo,\n isTouchCapable,\n extractCoordinates,\n} from './utils'\nimport { getCapturedErrors } from './console-capture'\nimport { getCapturedNetworkErrors } from './network-capture'\nimport type {\n CaptureResult,\n CaptureMetadata,\n ConsoleError,\n FailedNetworkRequest,\n PointerCoordinates,\n} from './types'\n\ninterface CaptureOverlayProps {\n isActive: boolean\n siteId: string\n reporterName: string\n reporterEmail: string\n onCapture: (result: CaptureResult) => void\n onCancel: () => void\n}\n\nfunction dataUrlToBlob(dataUrl: string): Blob {\n const [header, base64] = dataUrl.split(',')\n const mime = header.match(/:(.*?);/)?.[1] || 'image/png'\n const bytes = atob(base64)\n const arr = new Uint8Array(bytes.length)\n for (let i = 0; i < bytes.length; i++) arr[i] = bytes.charCodeAt(i)\n return new Blob([arr], { type: mime })\n}\n\nexport function CaptureOverlay({\n isActive,\n siteId,\n reporterName,\n reporterEmail,\n onCapture,\n onCancel,\n}: CaptureOverlayProps) {\n const [hoveredElement, setHoveredElement] = useState<HTMLElement | null>(null)\n const [hoveredRect, setHoveredRect] = useState<DOMRect | null>(null)\n const [isCapturing, setIsCapturing] = useState(false)\n const [isTouchMode, setIsTouchMode] = useState(false)\n const [selectedSection, setSelectedSection] = useState<HTMLElement | null>(null)\n const [selectedRect, setSelectedRect] = useState<DOMRect | null>(null)\n const [selectedTarget, setSelectedTarget] = useState<HTMLElement | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n const hoveredElementRef = useRef<HTMLElement | null>(null)\n const rafRef = useRef<number | null>(null)\n const touchCoordsRef = useRef<PointerCoordinates | null>(null)\n\n // Initialize touch mode on activation\n useEffect(() => {\n if (isActive) {\n setIsTouchMode(isTouchCapable())\n }\n }, [isActive])\n\n const captureScreenshot = useCallback(\n async (section: HTMLElement, target: HTMLElement, coords: PointerCoordinates) => {\n const elementInfo = collectElementInfo(target, section, coords)\n setIsCapturing(true)\n\n try {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n\n await new Promise((r) => setTimeout(r, 50))\n\n const MAX_DIMENSION = 2000\n const sectionRect = section.getBoundingClientRect()\n const pixelRatio =\n sectionRect.width > MAX_DIMENSION || sectionRect.height > MAX_DIMENSION ? 1 : 2\n\n const dataUrl = await toPng(section, {\n quality: 0.9,\n pixelRatio,\n skipFonts: true,\n })\n\n const blob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: blob, metadata, consoleErrors, networkErrors })\n } catch (err) {\n console.warn(\n 'Bug reporter: first capture attempt failed, retrying with simpler settings',\n err,\n )\n\n try {\n const dataUrl = await toPng(section, {\n quality: 0.6,\n pixelRatio: 1,\n skipFonts: true,\n cacheBust: true,\n })\n\n const retryBlob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: retryBlob, metadata, consoleErrors, networkErrors })\n } catch {\n console.error('Bug reporter: screenshot capture failed after retry')\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n onCapture({\n screenshot: new Blob(),\n metadata: { ...metadata, screenshotCaptureFailed: true },\n consoleErrors,\n networkErrors,\n })\n }\n } finally {\n setIsCapturing(false)\n }\n },\n [siteId, reporterName, reporterEmail, onCapture],\n )\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n if (rafRef.current) return\n\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n\n if (target.closest('[data-bug-reporter]')) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n return\n }\n\n const section = getNearestSection(target)\n setHoveredElement(section)\n hoveredElementRef.current = section\n setHoveredRect(section ? section.getBoundingClientRect() : null)\n })\n },\n [isActive, isCapturing, isTouchMode],\n )\n\n const handleClick = useCallback(\n async (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const section = getNearestSection(target)\n if (!section) return\n\n await captureScreenshot(section, target, extractCoordinates(e))\n },\n [isActive, isCapturing, isTouchMode, captureScreenshot],\n )\n\n const handleTouchEnd = useCallback(\n (e: TouchEvent) => {\n if (!isActive || isCapturing) return\n\n const touch = e.changedTouches[0]\n if (!touch) return\n\n const target = document.elementFromPoint(touch.clientX, touch.clientY)\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n const section = getNearestSection(target)\n if (!section) return\n\n setSelectedSection(section)\n setSelectedRect(section.getBoundingClientRect())\n setSelectedTarget(target)\n touchCoordsRef.current = extractCoordinates(touch)\n },\n [isActive, isCapturing],\n )\n\n const handleConfirmCapture = useCallback(async () => {\n if (!selectedSection || !selectedTarget || !touchCoordsRef.current) return\n await captureScreenshot(selectedSection, selectedTarget, touchCoordsRef.current)\n }, [selectedSection, selectedTarget, captureScreenshot])\n\n const handlePointerDown = useCallback(\n (e: PointerEvent) => {\n if (!isActive) return\n if (e.pointerType === 'touch') {\n setIsTouchMode(true)\n } else if (e.pointerType === 'mouse') {\n setIsTouchMode(false)\n }\n },\n [isActive],\n )\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isActive) {\n e.preventDefault()\n e.stopPropagation()\n onCancel()\n }\n },\n [isActive, onCancel],\n )\n\n const handleScroll = useCallback(() => {\n if (hoveredElementRef.current) {\n setHoveredRect(hoveredElementRef.current.getBoundingClientRect())\n }\n if (selectedSection) {\n setSelectedRect(selectedSection.getBoundingClientRect())\n }\n }, [selectedSection])\n\n useEffect(() => {\n if (!isActive) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n return\n }\n\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n window.addEventListener('scroll', handleScroll, { passive: true })\n\n if (isTouchMode) {\n document.addEventListener('touchend', handleTouchEnd, { passive: true })\n } else {\n document.addEventListener('mousemove', handleMouseMove, true)\n document.addEventListener('click', handleClick, true)\n }\n\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n window.removeEventListener('scroll', handleScroll)\n document.removeEventListener('touchend', handleTouchEnd)\n document.removeEventListener('mousemove', handleMouseMove, true)\n document.removeEventListener('click', handleClick, true)\n if (rafRef.current) cancelAnimationFrame(rafRef.current)\n }\n }, [\n isActive,\n isTouchMode,\n handleMouseMove,\n handleClick,\n handleTouchEnd,\n handlePointerDown,\n handleKeyDown,\n handleScroll,\n ])\n\n const highlightRect = isTouchMode ? selectedRect : hoveredRect\n const showHighlight = isTouchMode ? !!selectedSection : !!hoveredElement && !!hoveredRect\n\n if (!isActive) return null\n\n return (\n <>\n {/* Instruction banner */}\n <div\n data-bug-reporter\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"fixed top-0 right-0 left-0 z-[10000] flex items-center justify-center gap-3 bg-indigo-600 px-4 py-2 text-center text-sm font-medium text-white\"\n >\n {isTouchMode ? (\n <>\n <span>Tap the section with the bug</span>\n <button\n onClick={onCancel}\n className=\"min-h-[44px] rounded-md bg-white/20 px-3 py-1 text-sm font-medium\"\n >\n Cancel\n </button>\n </>\n ) : (\n <>\n Click on the section with the bug. Press{' '}\n <kbd className=\"mx-1 rounded bg-indigo-800 px-1.5 py-0.5 text-xs\">Esc</kbd> to cancel.\n </>\n )}\n </div>\n\n {/* Section highlight overlay */}\n {showHighlight && highlightRect && (\n <div\n ref={overlayRef}\n data-bug-reporter\n className=\"pointer-events-none fixed z-[9998] rounded-sm border-2 border-indigo-500 transition-all duration-150 ease-out\"\n style={{\n top: highlightRect.top - 2,\n left: highlightRect.left - 2,\n width: highlightRect.width + 4,\n height: highlightRect.height + 4,\n backgroundColor: 'rgba(99, 102, 241, 0.08)',\n }}\n />\n )}\n\n {/* Touch confirmation bar */}\n {isTouchMode && selectedSection && !isCapturing && (\n <div\n data-bug-reporter\n className=\"fixed right-0 bottom-0 left-0 z-[10000] border-t border-gray-200 bg-white shadow-lg\"\n style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}\n >\n <div className=\"flex items-center justify-between gap-3 px-4 py-3\">\n <span className=\"truncate text-sm font-medium text-gray-900\">\n Capture this section?\n </span>\n <div className=\"flex shrink-0 gap-2\">\n <button\n onClick={() => {\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n }}\n className=\"min-h-[44px] rounded-md border border-gray-300 px-4 text-sm font-medium text-gray-700\"\n >\n Cancel\n </button>\n <button\n onClick={handleConfirmCapture}\n className=\"min-h-[44px] rounded-md bg-indigo-600 px-4 text-sm font-medium text-white\"\n >\n Capture\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Full-page cursor override — desktop only */}\n {!isTouchMode && <style>{`* { cursor: crosshair !important; }`}</style>}\n </>\n )\n}\n","import { UAParser } from 'ua-parser-js'\nimport type { CaptureMetadata, ClickedElement, PointerCoordinates } from './types'\n\n/**\n * Walk up the DOM tree to find the nearest semantically meaningful section.\n */\nexport function getNearestSection(element: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = element\n\n while (current && current !== document.body) {\n if (\n current.id ||\n current.dataset.section ||\n current.tagName === 'SECTION' ||\n current.tagName === 'MAIN' ||\n current.tagName === 'ARTICLE' ||\n current.tagName === 'NAV' ||\n current.tagName === 'HEADER' ||\n current.tagName === 'FOOTER' ||\n current.role === 'main' ||\n current.role === 'navigation'\n ) {\n return current\n }\n current = current.parentElement\n }\n\n return document.querySelector('main') || document.body\n}\n\n/**\n * Get a human-readable section identifier from an element.\n */\nexport function getSectionId(element: HTMLElement): string | null {\n if (element.id) return `#${element.id}`\n if (element.dataset.section) return element.dataset.section\n if (element.tagName === 'MAIN') return 'main'\n if (element.tagName === 'SECTION') {\n const heading = element.querySelector('h1, h2, h3')\n if (heading) return heading.textContent?.trim().slice(0, 60) || null\n }\n return element.tagName.toLowerCase()\n}\n\n/**\n * Derive device type from viewport width and touch capability.\n */\nexport function getDeviceType(): 'desktop' | 'tablet' | 'mobile' {\n const width = window.innerWidth\n const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0\n\n if (width <= 768 && hasTouch) return 'mobile'\n if (width <= 1024 && hasTouch) return 'tablet'\n return 'desktop'\n}\n\n/**\n * Parse user agent string into readable browser and OS info.\n */\nexport function parseUserAgent(): { browser: string; os: string } {\n const parser = new UAParser(navigator.userAgent)\n const browser = parser.getBrowser()\n const osInfo = parser.getOS()\n\n return {\n browser: `${browser.name || 'Unknown'} ${browser.version || ''}`.trim(),\n os: `${osInfo.name || 'Unknown'} ${osInfo.version || ''}`.trim(),\n }\n}\n\n/**\n * Build a CSS selector path from an element up to its section container.\n */\nexport function buildSelectorPath(element: HTMLElement, stopAt?: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = element\n\n while (current && current !== document.body && current !== stopAt) {\n let selector = current.tagName.toLowerCase()\n if (current.id) {\n selector += `#${current.id}`\n } else if (current.className && typeof current.className === 'string') {\n const classes = current.className.trim().split(/\\s+/).slice(0, 2).join('.')\n if (classes) selector += `.${classes}`\n }\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\n/**\n * Check if the device supports touch input.\n */\nexport function isTouchCapable(): boolean {\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0\n}\n\n/**\n * Normalize coordinates from MouseEvent or Touch into a common shape.\n */\nexport function extractCoordinates(e: MouseEvent | Touch): PointerCoordinates {\n return {\n pageX: e.pageX,\n pageY: e.pageY,\n clientX: e.clientX,\n clientY: e.clientY,\n }\n}\n\n/**\n * Collect info about the exact element the user clicked/tapped.\n */\nexport function collectElementInfo(\n target: HTMLElement,\n section: HTMLElement,\n coords: PointerCoordinates,\n): ClickedElement {\n const sectionRect = section.getBoundingClientRect()\n\n const dataAttributes: Record<string, string> = {}\n for (const attr of Array.from(target.attributes)) {\n if (attr.name.startsWith('data-') && attr.name !== 'data-bug-reporter') {\n dataAttributes[attr.name] = attr.value\n }\n }\n\n return {\n tagName: target.tagName,\n textContent: (target.textContent || '').trim().slice(0, 200) || null,\n className: typeof target.className === 'string' ? target.className : '',\n id: target.id || null,\n ariaLabel: target.getAttribute('aria-label') || null,\n dataAttributes,\n selectorPath: buildSelectorPath(target, section),\n clickX: coords.pageX,\n clickY: coords.pageY,\n relativeClickX: coords.clientX - sectionRect.left,\n relativeClickY: coords.clientY - sectionRect.top,\n }\n}\n\n/**\n * Collect all metadata at the moment of capture.\n */\nexport function collectMetadata(\n sectionElement: HTMLElement,\n siteId: string,\n reporterName: string,\n reporterEmail: string,\n clickedElement?: ClickedElement,\n): CaptureMetadata {\n const { browser, os } = parseUserAgent()\n const now = new Date()\n\n return {\n pageUrl: window.location.href,\n sectionId: getSectionId(sectionElement),\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n deviceType: getDeviceType(),\n browser,\n os,\n timestamp: now.toISOString(),\n timestampUtc: now.toUTCString(),\n siteId,\n reporterName,\n reporterEmail,\n clickedElement,\n }\n}\n","import type { ConsoleError } from './types'\n\nconst MAX_ERRORS = 50\n\nlet capturedErrors: ConsoleError[] = []\nlet isCapturing = false\nlet originalConsoleError: typeof console.error | null = null\nlet errorListener: ((event: ErrorEvent) => void) | null = null\nlet rejectionListener: ((event: PromiseRejectionEvent) => void) | null = null\n\n/**\n * Start capturing console errors and unhandled exceptions.\n * Stores the last N errors for inclusion in bug reports.\n */\nexport function startCapturing(): void {\n if (isCapturing) return\n if ((console.error as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedErrors = []\n\n originalConsoleError = console.error\n const patchedConsoleError = (...args: unknown[]) => {\n const MAX_MSG_LEN = 500\n const message = args\n .map((a) => {\n if (typeof a === 'string') return a.slice(0, MAX_MSG_LEN)\n try {\n const s = JSON.stringify(a)\n return s.slice(0, MAX_MSG_LEN)\n } catch {\n return String(a).slice(0, MAX_MSG_LEN)\n }\n })\n .join(' ')\n .slice(0, MAX_MSG_LEN)\n\n capturedErrors.push({\n message,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n\n originalConsoleError!.apply(console, args)\n }\n ;(patchedConsoleError as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n console.error = patchedConsoleError\n\n errorListener = (event: ErrorEvent) => {\n capturedErrors.push({\n message: event.message,\n source: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('error', errorListener)\n\n rejectionListener = (event: PromiseRejectionEvent) => {\n capturedErrors.push({\n message: `Unhandled Promise Rejection: ${event.reason instanceof Error ? event.reason.message : String(event.reason)}`,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('unhandledrejection', rejectionListener)\n}\n\n/**\n * Stop capturing and restore original console.error.\n */\nexport function stopCapturing(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalConsoleError) {\n const restoreTarget = originalConsoleError\n console.error = restoreTarget\n if (console.error !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original console.error')\n }\n originalConsoleError = null\n }\n\n if (errorListener) {\n window.removeEventListener('error', errorListener)\n errorListener = null\n }\n\n if (rejectionListener) {\n window.removeEventListener('unhandledrejection', rejectionListener)\n rejectionListener = null\n }\n}\n\n/**\n * Get a copy of all captured errors.\n */\nexport function getCapturedErrors(): ConsoleError[] {\n return [...capturedErrors]\n}\n\n/**\n * Clear the captured errors buffer.\n */\nexport function clearCapturedErrors(): void {\n capturedErrors = []\n}\n","import type { FailedNetworkRequest } from './types'\n\nconst MAX_REQUESTS = 30\n/** Skip reading bodies larger than 64 KB to avoid memory/latency cost */\nconst MAX_BODY_READ_BYTES = 64 * 1024\nconst TRUNCATE_LEN = 500\n\nlet capturedRequests: FailedNetworkRequest[] = []\nlet isCapturing = false\nlet originalFetch: typeof window.fetch | null = null\n\nfunction truncateBody(body: string | null | undefined, maxLen = TRUNCATE_LEN): string | null {\n if (!body) return null\n if (body.length <= maxLen) return body\n return body.slice(0, maxLen) + '...(truncated)'\n}\n\n/**\n * Read a bounded amount of the response body for error capture.\n * Returns truncated text for small bodies, or a size-only metadata\n * string when the body exceeds MAX_BODY_READ_BYTES.\n */\nasync function readBoundedBody(response: Response): Promise<string | null> {\n try {\n // Fast path: check Content-Length header\n const contentLength = response.headers.get('Content-Length')\n if (contentLength) {\n const size = parseInt(contentLength, 10)\n if (!isNaN(size) && size > MAX_BODY_READ_BYTES) {\n return `[body too large: ${size} bytes]`\n }\n }\n\n // Stream-based bounded read when ReadableStream is available\n const cloned = response.clone()\n if (cloned.body && typeof cloned.body.getReader === 'function') {\n const reader = cloned.body.getReader()\n const chunks: Uint8Array[] = []\n let totalBytes = 0\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n totalBytes += value.byteLength\n if (totalBytes > MAX_BODY_READ_BYTES) {\n reader.cancel().catch(() => {})\n return `[body too large: >${MAX_BODY_READ_BYTES} bytes]`\n }\n chunks.push(value)\n }\n\n const decoder = new TextDecoder()\n const text = chunks.map((c) => decoder.decode(c, { stream: true })).join('')\n return truncateBody(text)\n }\n\n // Fallback: clone().text() for environments without ReadableStream\n const text = await cloned.text()\n return truncateBody(text)\n } catch {\n // Body may not be readable\n return null\n }\n}\n\n/**\n * Start capturing failed network requests (fetch only).\n * Intercepts window.fetch to log requests that return status >= 400 or throw.\n */\nexport function startNetworkCapture(): void {\n if (isCapturing || typeof window === 'undefined') return\n if ((window.fetch as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedRequests = []\n\n originalFetch = window.fetch\n const patchedFetch = async function patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n const method =\n init?.method ||\n (typeof input !== 'string' && !(input instanceof URL) ? input.method : 'GET') ||\n 'GET'\n\n // Skip our own bug reporter endpoints and non-HTTP URLs\n if (\n url.includes('/api/bug-reporter/') ||\n url.includes('/bug-reporter/external/') ||\n url.startsWith('data:') ||\n url.startsWith('blob:')\n ) {\n return originalFetch!.call(window, input, init)\n }\n\n try {\n const response = await originalFetch!.call(window, input, init)\n\n if (response.status >= 400) {\n const responseBody = await readBoundedBody(response)\n\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: response.status,\n statusText: response.statusText,\n responseBody,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n }\n\n return response\n } catch (error) {\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: 0,\n statusText: error instanceof Error ? error.message : 'Network error',\n responseBody: null,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n\n throw error\n }\n }\n ;(patchedFetch as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n window.fetch = patchedFetch\n}\n\n/**\n * Stop capturing and restore original fetch.\n */\nexport function stopNetworkCapture(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalFetch) {\n const restoreTarget = originalFetch\n window.fetch = restoreTarget\n if (window.fetch !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original fetch')\n }\n originalFetch = null\n }\n}\n\n/**\n * Get a copy of all captured failed requests.\n */\nexport function getCapturedNetworkErrors(): FailedNetworkRequest[] {\n return [...capturedRequests]\n}\n\n/**\n * Clear the captured requests buffer.\n */\nexport function clearCapturedNetworkErrors(): void {\n capturedRequests = []\n}\n","import { useState, useRef, useEffect, useCallback, useMemo } from 'react'\nimport { X, Send, Loader2, CheckCircle2 } from 'lucide-react'\nimport { cn } from './cn'\nimport { isTouchCapable } from './utils'\nimport type {\n CaptureResult,\n ConversationMessage,\n BugReporterApiConfig,\n BugReporterUser,\n} from './types'\n\ninterface ReportModalProps {\n isOpen: boolean\n captureResult: CaptureResult | null\n apiConfig: BugReporterApiConfig\n siteId: string\n user: BugReporterUser\n onClose: () => void\n}\n\ntype ModalState = 'chatting' | 'submitting' | 'submitted' | 'error'\n\nexport function ReportModal({\n isOpen,\n captureResult,\n apiConfig,\n siteId,\n user,\n onClose,\n}: ReportModalProps) {\n const [messages, setMessages] = useState<ConversationMessage[]>([])\n const [input, setInput] = useState('')\n const [isLoading, setIsLoading] = useState(false)\n const [modalState, setModalState] = useState<ModalState>('chatting')\n const [reportId, setReportId] = useState<string | null>(null)\n const [screenshotUrl, setScreenshotUrl] = useState<string | null>(null)\n const [errorMessage, setErrorMessage] = useState<string | null>(null)\n const chatEndRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLTextAreaElement>(null)\n const hasInitRef = useRef(false)\n\n const apiHeaders = useMemo<Record<string, string>>(\n () => ({\n 'Content-Type': 'application/json',\n 'X-Bug-Reporter-Key': apiConfig.apiKey,\n }),\n [apiConfig.apiKey],\n )\n\n // Create screenshot preview URL — revoke previous URL on replacement and\n // reset to null when there is no valid screenshot.\n useEffect(() => {\n if (captureResult?.screenshot && captureResult.screenshot.size > 0) {\n const url = URL.createObjectURL(captureResult.screenshot)\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return url\n })\n return () => {\n URL.revokeObjectURL(url)\n setScreenshotUrl(null)\n }\n }\n\n // No valid screenshot — clear stale URL\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return null\n })\n }, [captureResult])\n\n // Stable callback for sending initial AI message\n const sendInitialMessage = useCallback(async () => {\n if (!captureResult) return\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: [],\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n setMessages([{ role: 'assistant', content: data.message }])\n } catch {\n setMessages([\n {\n role: 'assistant',\n content:\n \"I can see you've captured a section of the page. What's going wrong here? Please describe the issue you're experiencing.\",\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }, [captureResult, apiConfig.apiUrl, apiHeaders])\n\n // Send initial AI message when modal opens\n useEffect(() => {\n if (isOpen && captureResult && !hasInitRef.current) {\n hasInitRef.current = true\n sendInitialMessage()\n }\n }, [isOpen, captureResult, sendInitialMessage])\n\n // Auto-scroll chat\n useEffect(() => {\n chatEndRef.current?.scrollIntoView({ behavior: 'smooth' })\n }, [messages])\n\n // Focus input\n useEffect(() => {\n if (isOpen && !isLoading) {\n inputRef.current?.focus()\n }\n }, [isOpen, isLoading, messages])\n\n const submitReport = useCallback(\n async (\n conversation: ConversationMessage[],\n structuredReport?: {\n summary: string\n expected_behavior: string\n actual_behavior: string\n reproducibility: string\n specific_data: string\n severity: string\n },\n ) => {\n if (!captureResult || modalState !== 'chatting') return\n setModalState('submitting')\n\n try {\n // Convert screenshot blob to base64\n let screenshotBase64: string | undefined\n if (captureResult.screenshot.size > 0) {\n const reader = new FileReader()\n screenshotBase64 = await new Promise<string>((resolve, reject) => {\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(captureResult.screenshot)\n })\n }\n\n // If no structured report from AI, request one via the API\n let report = structuredReport\n if (!report) {\n try {\n const summaryResponse = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: conversation,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n requestSummary: true,\n }),\n })\n if (summaryResponse.ok) {\n const data = await summaryResponse.json()\n report = data.structuredReport\n }\n } catch {\n // Fall through to default\n }\n }\n\n // Submit via the external submit endpoint\n const response = await fetch(`${apiConfig.apiUrl}/submit`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n screenshot: screenshotBase64,\n metadata: captureResult.metadata,\n conversation,\n structuredReport: report,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}))\n throw new Error(errorData.error || 'Failed to submit report')\n }\n\n const data = await response.json()\n setReportId(data.id)\n setModalState('submitted')\n } catch (err) {\n console.error('Bug reporter: failed to submit report', err)\n setErrorMessage(err instanceof Error ? err.message : 'Failed to submit report')\n setModalState('error')\n }\n },\n [captureResult, apiConfig.apiUrl, apiHeaders, modalState],\n )\n\n const handleManualSubmit = useCallback(() => {\n submitReport(messages)\n }, [submitReport, messages])\n\n async function sendMessage() {\n if (!input.trim() || isLoading || !captureResult) return\n\n const userMessage = input.trim()\n setInput('')\n\n const newMessages: ConversationMessage[] = [...messages, { role: 'user', content: userMessage }]\n setMessages(newMessages)\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: newMessages,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n\n setMessages([...newMessages, { role: 'assistant', content: data.message }])\n\n if (data.readyToSubmit && data.structuredReport) {\n await submitReport(\n [...newMessages, { role: 'assistant', content: data.message }],\n data.structuredReport,\n )\n }\n } catch {\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content: 'I had trouble processing that. Could you try describing the issue again?',\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }\n\n function handleClose() {\n setMessages([])\n setInput('')\n setModalState('chatting')\n setReportId(null)\n setErrorMessage(null)\n // Revoke and clear stale screenshot URL\n if (screenshotUrl) {\n URL.revokeObjectURL(screenshotUrl)\n }\n setScreenshotUrl(null)\n hasInitRef.current = false\n onClose()\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Enter' && !e.shiftKey && !isTouchCapable()) {\n e.preventDefault()\n sendMessage()\n }\n }\n\n if (!isOpen) return null\n\n return (\n <div\n data-bug-reporter\n className=\"fixed inset-0 z-[10001] flex items-center justify-center bg-black/50 backdrop-blur-sm\"\n onClick={(e) => {\n if (e.target === e.currentTarget) handleClose()\n }}\n >\n <div\n className={cn(\n 'flex flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-gray-950',\n 'mx-4 w-full max-w-lg',\n 'max-[768px]:mx-0 max-[768px]:h-full max-[768px]:max-w-none max-[768px]:rounded-none',\n 'min-[769px]:max-h-[85vh]',\n )}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-gray-200 bg-gray-50/30 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/30\">\n <div>\n <h2 className=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">Bug Report</h2>\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">{siteId}</p>\n </div>\n <button\n onClick={handleClose}\n className=\"rounded-md p-1.5 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-gray-600 dark:text-gray-400\" />\n </button>\n </div>\n\n {/* Screenshot preview */}\n {screenshotUrl && (\n <div className=\"border-b border-gray-200 bg-gray-50/10 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/10\">\n <img\n src={screenshotUrl}\n alt=\"Captured section\"\n className=\"max-h-40 w-full rounded-md border border-gray-200 object-contain dark:border-gray-700\"\n />\n </div>\n )}\n\n {/* Chat area */}\n <div className=\"min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-3\">\n {modalState === 'submitted' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <CheckCircle2 className=\"mb-3 h-12 w-12 text-green-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Report Submitted\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n Reference:{' '}\n <code className=\"rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-800\">\n {reportId?.slice(0, 8)}\n </code>\n </p>\n <p className=\"mt-2 text-sm text-gray-500 dark:text-gray-400\">\n Thanks for the report — we&apos;ll look into it.\n </p>\n <button\n onClick={handleClose}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Done\n </button>\n </div>\n ) : modalState === 'error' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <X className=\"mb-3 h-12 w-12 text-red-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Submission Failed\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n {errorMessage || 'Something went wrong. Please try again.'}\n </p>\n <button\n onClick={() => setModalState('chatting')}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Try Again\n </button>\n </div>\n ) : modalState === 'submitting' ? (\n <div className=\"flex flex-col items-center justify-center py-8\">\n <Loader2 className=\"mb-3 h-8 w-8 animate-spin text-indigo-500\" />\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">Submitting your report...</p>\n </div>\n ) : (\n <>\n {captureResult?.screenshot.size === 0 && (\n <p className=\"text-xs text-amber-600\">\n Screenshot could not be captured. Please describe the visual issue in detail.\n </p>\n )}\n\n {messages.map((msg, i) => (\n <div\n key={i}\n className={cn(\n 'text-sm leading-relaxed',\n msg.role === 'assistant'\n ? 'rounded-lg bg-gray-100/50 p-3 text-gray-900 dark:bg-gray-800/50 dark:text-gray-100'\n : 'ml-8 rounded-lg bg-indigo-600 p-3 text-white',\n )}\n >\n {msg.content}\n </div>\n ))}\n\n {isLoading && (\n <div className=\"flex items-center gap-2 rounded-lg bg-gray-100/50 p-3 dark:bg-gray-800/50\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin text-gray-500\" />\n <span className=\"text-sm text-gray-500 dark:text-gray-400\">Thinking...</span>\n </div>\n )}\n\n <div ref={chatEndRef} />\n </>\n )}\n </div>\n\n {/* Input area — only show during chatting */}\n {modalState === 'chatting' && (\n <div className=\"border-t border-gray-200 px-4 py-3 dark:border-gray-800\">\n <div className=\"flex flex-col gap-2\">\n <textarea\n ref={inputRef}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe what's wrong...\"\n rows={2}\n className=\"w-full resize-none rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 focus:border-transparent focus:ring-2 focus:ring-indigo-400 focus:outline-none dark:border-gray-700 dark:bg-gray-950 dark:text-gray-100\"\n disabled={isLoading}\n />\n <div className=\"flex flex-col gap-2\">\n {captureResult &&\n (captureResult.consoleErrors.length > 0 ||\n captureResult.networkErrors.length > 0) && (\n <p className=\"flex-1 text-xs text-amber-600\">\n {[\n captureResult.consoleErrors.length > 0\n ? `${captureResult.consoleErrors.length} console error${captureResult.consoleErrors.length !== 1 ? 's' : ''}`\n : null,\n captureResult.networkErrors.length > 0\n ? `${captureResult.networkErrors.length} failed request${captureResult.networkErrors.length !== 1 ? 's' : ''}`\n : null,\n ]\n .filter(Boolean)\n .join(' + ')}{' '}\n captured — these will be included in the report.\n </p>\n )}\n <div className=\"flex justify-end gap-2\">\n <button\n onClick={sendMessage}\n disabled={!input.trim() || isLoading}\n className=\"flex items-center gap-1 rounded-md bg-indigo-600 px-3 py-2 text-nowrap text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"Send message\"\n >\n <Send className=\"h-4 w-4\" />\n <span className=\"text-sm font-medium\">Send message</span>\n </button>\n {messages.length >= 2 && (\n <button\n onClick={handleManualSubmit}\n disabled={isLoading}\n className=\"rounded-md bg-green-600 px-3 py-2 text-xs font-medium text-nowrap text-white transition-colors hover:bg-green-700 disabled:opacity-50\"\n title=\"Submit report now\"\n >\n <span className=\"text-sm font-medium\">Submit report</span>\n </button>\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","useEffect","useState","useRef","isCapturing","text","patchedFetch","jsx","jsxs","useState","isCapturing","useRef","useEffect","useState","useRef","useEffect","useCallback","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","data","Fragment","jsx","jsxs","useState","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../src/bug-reporter.tsx","../src/floating-button.tsx","../src/cn.ts","../src/capture-overlay.tsx","../src/utils.ts","../src/console-capture.ts","../src/network-capture.ts","../src/report-modal.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react'\nimport { FloatingButton, type FloatingButtonPosition } from './floating-button'\nimport { CaptureOverlay } from './capture-overlay'\nimport { ReportModal } from './report-modal'\nimport { startCapturing, stopCapturing, clearCapturedErrors } from './console-capture'\nimport {\n startNetworkCapture,\n stopNetworkCapture,\n clearCapturedNetworkErrors,\n} from './network-capture'\nimport type { CaptureResult, BugReporterUser } from './types'\n\ndeclare global {\n interface Window {\n __jarve_widgets?: Map<\n string,\n {\n type: string\n label: string\n tooltip: string\n iconName: string\n accentColor: string\n isActive: boolean\n trigger: () => void\n }\n >\n }\n}\n\ninterface JarveBugReporterProps {\n /** Base URL for the external bug reporter API (e.g. \"https://www.jarve.com.au/api/bug-reporter/external\") */\n apiUrl: string\n /** API key for your site (starts with \"brk_\") */\n apiKey: string\n /** Optional position for the floating button (default: 'right') */\n buttonPosition?: FloatingButtonPosition\n /** Optional user info. If not provided, the AI will ask for name/email during the conversation. */\n user?: BugReporterUser\n children: React.ReactNode\n}\n\nexport function JarveBugReporter({\n apiUrl,\n apiKey,\n user,\n buttonPosition,\n children,\n}: JarveBugReporterProps) {\n const safeApiKey = apiKey || ''\n\n const [captureMode, setCaptureMode] = useState(false)\n const [captureResult, setCaptureResult] = useState<CaptureResult | null>(null)\n const [showModal, setShowModal] = useState(false)\n\n // Always start console + network capture on mount\n useEffect(() => {\n startCapturing()\n startNetworkCapture()\n return () => {\n stopCapturing()\n stopNetworkCapture()\n }\n }, [])\n\n const toggleCaptureMode = useCallback(() => {\n setCaptureMode((prev) => !prev)\n }, [])\n\n // Register with launcher widget registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n if (!window.__jarve_widgets) {\n window.__jarve_widgets = new Map()\n }\n\n window.__jarve_widgets.set('bug-reporter', {\n type: 'bug-reporter',\n label: 'Report a Bug',\n tooltip:\n \"Spotted something broken? Click here to take a screenshot, describe the problem, and we'll handle the rest.\",\n iconName: 'Bug',\n accentColor: 'red',\n isActive: false,\n trigger: toggleCaptureMode,\n })\n\n window.dispatchEvent(\n new CustomEvent('jarve:widget-registered', { detail: { type: 'bug-reporter' } }),\n )\n\n return () => {\n window.__jarve_widgets?.delete('bug-reporter')\n window.dispatchEvent(\n new CustomEvent('jarve:widget-deregistered', { detail: { type: 'bug-reporter' } }),\n )\n }\n }, [toggleCaptureMode])\n\n // Sync active state with launcher registry\n useEffect(() => {\n if (typeof window === 'undefined') return\n const entry = window.__jarve_widgets?.get('bug-reporter')\n if (entry) {\n entry.isActive = captureMode || showModal\n window.dispatchEvent(\n new CustomEvent('jarve:state-change', { detail: { type: 'bug-reporter' } }),\n )\n }\n }, [captureMode, showModal])\n\n const handleCapture = useCallback((result: CaptureResult) => {\n setCaptureResult(result)\n setCaptureMode(false)\n setShowModal(true)\n }, [])\n\n const handleCancelCapture = useCallback(() => {\n setCaptureMode(false)\n }, [])\n\n const handleCloseModal = useCallback(() => {\n setShowModal(false)\n setCaptureResult(null)\n clearCapturedErrors()\n clearCapturedNetworkErrors()\n }, [])\n\n // Derive a stable siteId from the apiKey for metadata\n const siteId = safeApiKey.startsWith('brk_') ? safeApiKey.slice(4, 12) : 'external'\n const reporterName = user?.name || 'Anonymous'\n const reporterEmail = user?.email || 'unknown@external'\n\n return (\n <>\n {children}\n\n <FloatingButton\n isActive={captureMode}\n onClick={toggleCaptureMode}\n position={buttonPosition}\n />\n\n <CaptureOverlay\n isActive={captureMode}\n siteId={siteId}\n reporterName={reporterName}\n reporterEmail={reporterEmail}\n onCapture={handleCapture}\n onCancel={handleCancelCapture}\n />\n\n <ReportModal\n isOpen={showModal}\n captureResult={captureResult}\n apiConfig={{ apiUrl, apiKey }}\n siteId={siteId}\n user={{ name: reporterName, email: reporterEmail }}\n onClose={handleCloseModal}\n />\n </>\n )\n}\n","import { useState, useEffect, useRef } from 'react'\nimport { Bug } from 'lucide-react'\nimport { cn } from './cn'\n\nexport type FloatingButtonPosition = 'left' | 'right'\n\ninterface FloatingButtonProps {\n isActive: boolean\n onClick: () => void\n position?: FloatingButtonPosition\n}\n\nconst STACK_OFFSET = 56 // button height (~48px) + gap (8px)\n\nexport function FloatingButton({ isActive, onClick, position = 'right' }: FloatingButtonProps) {\n const [hovered, setHovered] = useState(false)\n const [stackOffset, setStackOffset] = useState(0)\n const [launcherPresent, setLauncherPresent] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const isLeft = position === 'left'\n const sideClasses = isLeft ? 'left-4 md:left-6' : 'right-4 md:right-6'\n\n // Auto-stack with other @jarve widgets on the same side\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n let rafId: number\n\n const recalculate = () => {\n const widgets = Array.from(\n document.querySelectorAll(`[data-jarve-widget][data-jarve-position=\"${position}\"]`),\n )\n // Sort by widget name for deterministic ordering\n widgets.sort((a, b) =>\n (a.getAttribute('data-jarve-widget') || '').localeCompare(\n b.getAttribute('data-jarve-widget') || '',\n ),\n )\n const index = widgets.indexOf(el)\n setStackOffset(index > 0 ? index * STACK_OFFSET : 0)\n }\n\n recalculate()\n\n // Watch for siblings appearing/disappearing\n const observer = new MutationObserver(() => {\n cancelAnimationFrame(rafId)\n rafId = requestAnimationFrame(recalculate)\n })\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => {\n observer.disconnect()\n cancelAnimationFrame(rafId)\n }\n }, [position])\n\n // Hide when launcher is present\n useEffect(() => {\n const checkLauncher = () => {\n setLauncherPresent(!!document.querySelector('[data-jarve-launcher]'))\n }\n\n checkLauncher()\n\n const observer = new MutationObserver(checkLauncher)\n observer.observe(document.body, { childList: true, subtree: true })\n\n return () => observer.disconnect()\n }, [])\n\n if (launcherPresent) return null\n\n return (\n <div\n ref={ref}\n data-jarve-widget=\"bug-reporter\"\n data-jarve-position={position}\n className={cn('fixed z-[9999]', 'bottom-4 md:bottom-6', sideClasses)}\n style={stackOffset > 0 ? { transform: `translateY(-${stackOffset}px)` } : undefined}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n {/* Tooltip */}\n <div\n className={cn(\n 'pointer-events-none absolute bottom-full mb-3 w-max max-w-[240px] rounded-xl bg-gray-900/95 px-3.5 py-2.5 text-xs leading-relaxed text-white shadow-xl backdrop-blur-sm transition-all duration-200',\n isLeft ? 'left-0' : 'right-0',\n hovered && !isActive ? 'translate-y-0 opacity-100' : 'translate-y-1 opacity-0',\n )}\n >\n <span className=\"font-semibold text-red-300\">🐛 Report a Bug</span>\n <br />\n <span className=\"text-gray-300\">\n Spotted something broken? Click here to take a screenshot, describe the problem, and\n we&apos;ll handle the rest.\n </span>\n {/* Arrow */}\n <div\n className={cn(\n 'absolute top-full h-0 w-0 border-x-[6px] border-t-[6px] border-x-transparent border-t-gray-900',\n isLeft ? 'left-4' : 'right-4',\n )}\n />\n </div>\n\n {/* Button */}\n <button\n onClick={onClick}\n className={cn(\n 'flex items-center justify-center rounded-full shadow-lg transition-all duration-200',\n 'hover:scale-110 focus:ring-2 focus:ring-offset-2 focus:outline-none',\n 'h-11 w-11 md:h-12 md:w-12',\n isActive\n ? 'animate-pulse bg-red-500 text-white focus:ring-red-400'\n : 'bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-400',\n )}\n title={isActive ? 'Cancel bug capture' : 'Report a bug'}\n aria-label={isActive ? 'Cancel bug capture' : 'Report a bug'}\n >\n <Bug className=\"h-4 w-4 md:h-5 md:w-5\" />\n </button>\n </div>\n )\n}\n","import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useEffect, useState, useCallback, useRef } from 'react'\nimport { toPng } from 'html-to-image'\nimport {\n getNearestSection,\n collectMetadata,\n collectElementInfo,\n isTouchCapable,\n extractCoordinates,\n} from './utils'\nimport { getCapturedErrors } from './console-capture'\nimport { getCapturedNetworkErrors } from './network-capture'\nimport type {\n CaptureResult,\n CaptureMetadata,\n ConsoleError,\n FailedNetworkRequest,\n PointerCoordinates,\n} from './types'\n\ninterface CaptureOverlayProps {\n isActive: boolean\n siteId: string\n reporterName: string\n reporterEmail: string\n onCapture: (result: CaptureResult) => void\n onCancel: () => void\n}\n\nfunction dataUrlToBlob(dataUrl: string): Blob {\n const [header, base64] = dataUrl.split(',')\n const mime = header.match(/:(.*?);/)?.[1] || 'image/png'\n const bytes = atob(base64)\n const arr = new Uint8Array(bytes.length)\n for (let i = 0; i < bytes.length; i++) arr[i] = bytes.charCodeAt(i)\n return new Blob([arr], { type: mime })\n}\n\nexport function CaptureOverlay({\n isActive,\n siteId,\n reporterName,\n reporterEmail,\n onCapture,\n onCancel,\n}: CaptureOverlayProps) {\n const [hoveredElement, setHoveredElement] = useState<HTMLElement | null>(null)\n const [hoveredRect, setHoveredRect] = useState<DOMRect | null>(null)\n const [isCapturing, setIsCapturing] = useState(false)\n const [isTouchMode, setIsTouchMode] = useState(false)\n const [selectedSection, setSelectedSection] = useState<HTMLElement | null>(null)\n const [selectedRect, setSelectedRect] = useState<DOMRect | null>(null)\n const [selectedTarget, setSelectedTarget] = useState<HTMLElement | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n const hoveredElementRef = useRef<HTMLElement | null>(null)\n const rafRef = useRef<number | null>(null)\n const touchCoordsRef = useRef<PointerCoordinates | null>(null)\n\n // Initialize touch mode on activation\n useEffect(() => {\n if (isActive) {\n setIsTouchMode(isTouchCapable())\n }\n }, [isActive])\n\n const captureScreenshot = useCallback(\n async (section: HTMLElement, target: HTMLElement, coords: PointerCoordinates) => {\n const elementInfo = collectElementInfo(target, section, coords)\n setIsCapturing(true)\n\n try {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n\n await new Promise((r) => setTimeout(r, 50))\n\n const MAX_DIMENSION = 2000\n const sectionRect = section.getBoundingClientRect()\n const pixelRatio =\n sectionRect.width > MAX_DIMENSION || sectionRect.height > MAX_DIMENSION ? 1 : 2\n\n const dataUrl = await toPng(section, {\n quality: 0.9,\n pixelRatio,\n skipFonts: true,\n })\n\n const blob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: blob, metadata, consoleErrors, networkErrors })\n } catch (err) {\n console.warn(\n 'Bug reporter: first capture attempt failed, retrying with simpler settings',\n err,\n )\n\n try {\n const dataUrl = await toPng(section, {\n quality: 0.6,\n pixelRatio: 1,\n skipFonts: true,\n cacheBust: true,\n })\n\n const retryBlob = dataUrlToBlob(dataUrl)\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: retryBlob, metadata, consoleErrors, networkErrors })\n } catch {\n console.error('Bug reporter: screenshot capture failed after retry')\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo,\n )\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n onCapture({\n screenshot: new Blob(),\n metadata: { ...metadata, screenshotCaptureFailed: true },\n consoleErrors,\n networkErrors,\n })\n }\n } finally {\n setIsCapturing(false)\n }\n },\n [siteId, reporterName, reporterEmail, onCapture],\n )\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n if (rafRef.current) return\n\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n\n if (target.closest('[data-bug-reporter]')) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n return\n }\n\n const section = getNearestSection(target)\n setHoveredElement(section)\n hoveredElementRef.current = section\n setHoveredRect(section ? section.getBoundingClientRect() : null)\n })\n },\n [isActive, isCapturing, isTouchMode],\n )\n\n const handleClick = useCallback(\n async (e: MouseEvent) => {\n if (!isActive || isCapturing || isTouchMode) return\n\n const target = e.target\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const section = getNearestSection(target)\n if (!section) return\n\n await captureScreenshot(section, target, extractCoordinates(e))\n },\n [isActive, isCapturing, isTouchMode, captureScreenshot],\n )\n\n const handleTouchEnd = useCallback(\n (e: TouchEvent) => {\n if (!isActive || isCapturing) return\n\n const touch = e.changedTouches[0]\n if (!touch) return\n\n const target = document.elementFromPoint(touch.clientX, touch.clientY)\n if (!(target instanceof HTMLElement)) return\n if (target.closest('[data-bug-reporter]')) return\n\n const section = getNearestSection(target)\n if (!section) return\n\n setSelectedSection(section)\n setSelectedRect(section.getBoundingClientRect())\n setSelectedTarget(target)\n touchCoordsRef.current = extractCoordinates(touch)\n },\n [isActive, isCapturing],\n )\n\n const handleConfirmCapture = useCallback(async () => {\n if (!selectedSection || !selectedTarget || !touchCoordsRef.current) return\n await captureScreenshot(selectedSection, selectedTarget, touchCoordsRef.current)\n }, [selectedSection, selectedTarget, captureScreenshot])\n\n const handlePointerDown = useCallback(\n (e: PointerEvent) => {\n if (!isActive) return\n if (e.pointerType === 'touch') {\n setIsTouchMode(true)\n } else if (e.pointerType === 'mouse') {\n setIsTouchMode(false)\n }\n },\n [isActive],\n )\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isActive) {\n e.preventDefault()\n e.stopPropagation()\n onCancel()\n }\n },\n [isActive, onCancel],\n )\n\n const handleScroll = useCallback(() => {\n if (hoveredElementRef.current) {\n setHoveredRect(hoveredElementRef.current.getBoundingClientRect())\n }\n if (selectedSection) {\n setSelectedRect(selectedSection.getBoundingClientRect())\n }\n }, [selectedSection])\n\n useEffect(() => {\n if (!isActive) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n return\n }\n\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n window.addEventListener('scroll', handleScroll, { passive: true })\n\n if (isTouchMode) {\n document.addEventListener('touchend', handleTouchEnd, { passive: true })\n } else {\n document.addEventListener('mousemove', handleMouseMove, true)\n document.addEventListener('click', handleClick, true)\n }\n\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n window.removeEventListener('scroll', handleScroll)\n document.removeEventListener('touchend', handleTouchEnd)\n document.removeEventListener('mousemove', handleMouseMove, true)\n document.removeEventListener('click', handleClick, true)\n if (rafRef.current) cancelAnimationFrame(rafRef.current)\n }\n }, [\n isActive,\n isTouchMode,\n handleMouseMove,\n handleClick,\n handleTouchEnd,\n handlePointerDown,\n handleKeyDown,\n handleScroll,\n ])\n\n const highlightRect = isTouchMode ? selectedRect : hoveredRect\n const showHighlight = isTouchMode ? !!selectedSection : !!hoveredElement && !!hoveredRect\n\n if (!isActive) return null\n\n return (\n <>\n {/* Instruction banner */}\n <div\n data-bug-reporter\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"fixed top-0 right-0 left-0 z-[10000] flex items-center justify-center gap-3 bg-indigo-600 px-4 py-2 text-center text-sm font-medium text-white\"\n >\n {isTouchMode ? (\n <>\n <span>Tap the section with the bug</span>\n <button\n onClick={onCancel}\n className=\"min-h-[44px] rounded-md bg-white/20 px-3 py-1 text-sm font-medium\"\n >\n Cancel\n </button>\n </>\n ) : (\n <>\n Click on the section with the bug. Press{' '}\n <kbd className=\"mx-1 rounded bg-indigo-800 px-1.5 py-0.5 text-xs\">Esc</kbd> to cancel.\n </>\n )}\n </div>\n\n {/* Section highlight overlay */}\n {showHighlight && highlightRect && (\n <div\n ref={overlayRef}\n data-bug-reporter\n className=\"pointer-events-none fixed z-[9998] rounded-sm border-2 border-indigo-500 transition-all duration-150 ease-out\"\n style={{\n top: highlightRect.top - 2,\n left: highlightRect.left - 2,\n width: highlightRect.width + 4,\n height: highlightRect.height + 4,\n backgroundColor: 'rgba(99, 102, 241, 0.08)',\n }}\n />\n )}\n\n {/* Touch confirmation bar */}\n {isTouchMode && selectedSection && !isCapturing && (\n <div\n data-bug-reporter\n className=\"fixed right-0 bottom-0 left-0 z-[10000] border-t border-gray-200 bg-white shadow-lg\"\n style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}\n >\n <div className=\"flex items-center justify-between gap-3 px-4 py-3\">\n <span className=\"truncate text-sm font-medium text-gray-900\">\n Capture this section?\n </span>\n <div className=\"flex shrink-0 gap-2\">\n <button\n onClick={() => {\n setSelectedSection(null)\n setSelectedRect(null)\n setSelectedTarget(null)\n touchCoordsRef.current = null\n }}\n className=\"min-h-[44px] rounded-md border border-gray-300 px-4 text-sm font-medium text-gray-700\"\n >\n Cancel\n </button>\n <button\n onClick={handleConfirmCapture}\n className=\"min-h-[44px] rounded-md bg-indigo-600 px-4 text-sm font-medium text-white\"\n >\n Capture\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Full-page cursor override — desktop only */}\n {!isTouchMode && <style>{`* { cursor: crosshair !important; }`}</style>}\n </>\n )\n}\n","import { UAParser } from 'ua-parser-js'\nimport type { CaptureMetadata, ClickedElement, PointerCoordinates } from './types'\n\n/**\n * Walk up the DOM tree to find the nearest semantically meaningful section.\n */\nexport function getNearestSection(element: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = element\n\n while (current && current !== document.body) {\n if (\n current.id ||\n current.dataset.section ||\n current.tagName === 'SECTION' ||\n current.tagName === 'MAIN' ||\n current.tagName === 'ARTICLE' ||\n current.tagName === 'NAV' ||\n current.tagName === 'HEADER' ||\n current.tagName === 'FOOTER' ||\n current.role === 'main' ||\n current.role === 'navigation'\n ) {\n return current\n }\n current = current.parentElement\n }\n\n return document.querySelector('main') || document.body\n}\n\n/**\n * Get a human-readable section identifier from an element.\n */\nexport function getSectionId(element: HTMLElement): string | null {\n if (element.id) return `#${element.id}`\n if (element.dataset.section) return element.dataset.section\n if (element.tagName === 'MAIN') return 'main'\n if (element.tagName === 'SECTION') {\n const heading = element.querySelector('h1, h2, h3')\n if (heading) return heading.textContent?.trim().slice(0, 60) || null\n }\n return element.tagName.toLowerCase()\n}\n\n/**\n * Derive device type from viewport width and touch capability.\n */\nexport function getDeviceType(): 'desktop' | 'tablet' | 'mobile' {\n const width = window.innerWidth\n const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0\n\n if (width <= 768 && hasTouch) return 'mobile'\n if (width <= 1024 && hasTouch) return 'tablet'\n return 'desktop'\n}\n\n/**\n * Parse user agent string into readable browser and OS info.\n */\nexport function parseUserAgent(): { browser: string; os: string } {\n const parser = new UAParser(navigator.userAgent)\n const browser = parser.getBrowser()\n const osInfo = parser.getOS()\n\n return {\n browser: `${browser.name || 'Unknown'} ${browser.version || ''}`.trim(),\n os: `${osInfo.name || 'Unknown'} ${osInfo.version || ''}`.trim(),\n }\n}\n\n/**\n * Build a CSS selector path from an element up to its section container.\n */\nexport function buildSelectorPath(element: HTMLElement, stopAt?: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = element\n\n while (current && current !== document.body && current !== stopAt) {\n let selector = current.tagName.toLowerCase()\n if (current.id) {\n selector += `#${current.id}`\n } else if (current.className && typeof current.className === 'string') {\n const classes = current.className.trim().split(/\\s+/).slice(0, 2).join('.')\n if (classes) selector += `.${classes}`\n }\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\n/**\n * Check if the device supports touch input.\n */\nexport function isTouchCapable(): boolean {\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0\n}\n\n/**\n * Normalize coordinates from MouseEvent or Touch into a common shape.\n */\nexport function extractCoordinates(e: MouseEvent | Touch): PointerCoordinates {\n return {\n pageX: e.pageX,\n pageY: e.pageY,\n clientX: e.clientX,\n clientY: e.clientY,\n }\n}\n\n/**\n * Collect info about the exact element the user clicked/tapped.\n */\nexport function collectElementInfo(\n target: HTMLElement,\n section: HTMLElement,\n coords: PointerCoordinates,\n): ClickedElement {\n const sectionRect = section.getBoundingClientRect()\n\n const dataAttributes: Record<string, string> = {}\n for (const attr of Array.from(target.attributes)) {\n if (attr.name.startsWith('data-') && attr.name !== 'data-bug-reporter') {\n dataAttributes[attr.name] = attr.value\n }\n }\n\n return {\n tagName: target.tagName,\n textContent: (target.textContent || '').trim().slice(0, 200) || null,\n className: typeof target.className === 'string' ? target.className : '',\n id: target.id || null,\n ariaLabel: target.getAttribute('aria-label') || null,\n dataAttributes,\n selectorPath: buildSelectorPath(target, section),\n clickX: coords.pageX,\n clickY: coords.pageY,\n relativeClickX: coords.clientX - sectionRect.left,\n relativeClickY: coords.clientY - sectionRect.top,\n }\n}\n\n/**\n * Collect all metadata at the moment of capture.\n */\nexport function collectMetadata(\n sectionElement: HTMLElement,\n siteId: string,\n reporterName: string,\n reporterEmail: string,\n clickedElement?: ClickedElement,\n): CaptureMetadata {\n const { browser, os } = parseUserAgent()\n const now = new Date()\n\n return {\n pageUrl: window.location.href,\n sectionId: getSectionId(sectionElement),\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n deviceType: getDeviceType(),\n browser,\n os,\n timestamp: now.toISOString(),\n timestampUtc: now.toUTCString(),\n siteId,\n reporterName,\n reporterEmail,\n clickedElement,\n }\n}\n","import type { ConsoleError } from './types'\n\nconst MAX_ERRORS = 50\n\nlet capturedErrors: ConsoleError[] = []\nlet isCapturing = false\nlet originalConsoleError: typeof console.error | null = null\nlet errorListener: ((event: ErrorEvent) => void) | null = null\nlet rejectionListener: ((event: PromiseRejectionEvent) => void) | null = null\n\n/**\n * Start capturing console errors and unhandled exceptions.\n * Stores the last N errors for inclusion in bug reports.\n */\nexport function startCapturing(): void {\n if (isCapturing) return\n if ((console.error as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedErrors = []\n\n originalConsoleError = console.error\n const patchedConsoleError = (...args: unknown[]) => {\n const MAX_MSG_LEN = 500\n const message = args\n .map((a) => {\n if (typeof a === 'string') return a.slice(0, MAX_MSG_LEN)\n try {\n const s = JSON.stringify(a)\n return s.slice(0, MAX_MSG_LEN)\n } catch {\n return String(a).slice(0, MAX_MSG_LEN)\n }\n })\n .join(' ')\n .slice(0, MAX_MSG_LEN)\n\n capturedErrors.push({\n message,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n\n originalConsoleError!.apply(console, args)\n }\n ;(patchedConsoleError as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n console.error = patchedConsoleError\n\n errorListener = (event: ErrorEvent) => {\n capturedErrors.push({\n message: event.message,\n source: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('error', errorListener)\n\n rejectionListener = (event: PromiseRejectionEvent) => {\n capturedErrors.push({\n message: `Unhandled Promise Rejection: ${event.reason instanceof Error ? event.reason.message : String(event.reason)}`,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedErrors.length > MAX_ERRORS) {\n capturedErrors = capturedErrors.slice(-MAX_ERRORS)\n }\n }\n window.addEventListener('unhandledrejection', rejectionListener)\n}\n\n/**\n * Stop capturing and restore original console.error.\n */\nexport function stopCapturing(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalConsoleError) {\n const restoreTarget = originalConsoleError\n console.error = restoreTarget\n if (console.error !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original console.error')\n }\n originalConsoleError = null\n }\n\n if (errorListener) {\n window.removeEventListener('error', errorListener)\n errorListener = null\n }\n\n if (rejectionListener) {\n window.removeEventListener('unhandledrejection', rejectionListener)\n rejectionListener = null\n }\n}\n\n/**\n * Get a copy of all captured errors.\n */\nexport function getCapturedErrors(): ConsoleError[] {\n return [...capturedErrors]\n}\n\n/**\n * Clear the captured errors buffer.\n */\nexport function clearCapturedErrors(): void {\n capturedErrors = []\n}\n","import type { FailedNetworkRequest } from './types'\n\nconst MAX_REQUESTS = 30\n/** Skip reading bodies larger than 64 KB to avoid memory/latency cost */\nconst MAX_BODY_READ_BYTES = 64 * 1024\nconst TRUNCATE_LEN = 500\n\nlet capturedRequests: FailedNetworkRequest[] = []\nlet isCapturing = false\nlet originalFetch: typeof window.fetch | null = null\n\nfunction truncateBody(body: string | null | undefined, maxLen = TRUNCATE_LEN): string | null {\n if (!body) return null\n if (body.length <= maxLen) return body\n return body.slice(0, maxLen) + '...(truncated)'\n}\n\n/**\n * Read a bounded amount of the response body for error capture.\n * Returns truncated text for small bodies, or a size-only metadata\n * string when the body exceeds MAX_BODY_READ_BYTES.\n */\nasync function readBoundedBody(response: Response): Promise<string | null> {\n try {\n // Fast path: check Content-Length header\n const contentLength = response.headers.get('Content-Length')\n if (contentLength) {\n const size = parseInt(contentLength, 10)\n if (!isNaN(size) && size > MAX_BODY_READ_BYTES) {\n return `[body too large: ${size} bytes]`\n }\n }\n\n // Stream-based bounded read when ReadableStream is available\n const cloned = response.clone()\n if (cloned.body && typeof cloned.body.getReader === 'function') {\n const reader = cloned.body.getReader()\n const chunks: Uint8Array[] = []\n let totalBytes = 0\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n totalBytes += value.byteLength\n if (totalBytes > MAX_BODY_READ_BYTES) {\n reader.cancel().catch(() => {})\n return `[body too large: >${MAX_BODY_READ_BYTES} bytes]`\n }\n chunks.push(value)\n }\n\n const decoder = new TextDecoder()\n const text = chunks.map((c) => decoder.decode(c, { stream: true })).join('')\n return truncateBody(text)\n }\n\n // Fallback: clone().text() for environments without ReadableStream\n const text = await cloned.text()\n return truncateBody(text)\n } catch {\n // Body may not be readable\n return null\n }\n}\n\n/**\n * Start capturing failed network requests (fetch only).\n * Intercepts window.fetch to log requests that return status >= 400 or throw.\n */\nexport function startNetworkCapture(): void {\n if (isCapturing || typeof window === 'undefined') return\n if ((window.fetch as { __bugReporterPatched?: boolean }).__bugReporterPatched) return\n\n isCapturing = true\n capturedRequests = []\n\n originalFetch = window.fetch\n const patchedFetch = async function patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n const method =\n init?.method ||\n (typeof input !== 'string' && !(input instanceof URL) ? input.method : 'GET') ||\n 'GET'\n\n // Skip our own bug reporter endpoints and non-HTTP URLs\n if (\n url.includes('/api/bug-reporter/') ||\n url.includes('/bug-reporter/external/') ||\n url.startsWith('data:') ||\n url.startsWith('blob:')\n ) {\n return originalFetch!.call(window, input, init)\n }\n\n try {\n const response = await originalFetch!.call(window, input, init)\n\n if (response.status >= 400) {\n const responseBody = await readBoundedBody(response)\n\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: response.status,\n statusText: response.statusText,\n responseBody,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n }\n\n return response\n } catch (error) {\n capturedRequests.push({\n url,\n method: method.toUpperCase(),\n status: 0,\n statusText: error instanceof Error ? error.message : 'Network error',\n responseBody: null,\n timestamp: new Date().toISOString(),\n })\n\n if (capturedRequests.length > MAX_REQUESTS) {\n capturedRequests = capturedRequests.slice(-MAX_REQUESTS)\n }\n\n throw error\n }\n }\n ;(patchedFetch as { __bugReporterPatched?: boolean }).__bugReporterPatched = true\n window.fetch = patchedFetch\n}\n\n/**\n * Stop capturing and restore original fetch.\n */\nexport function stopNetworkCapture(): void {\n if (!isCapturing) return\n isCapturing = false\n\n if (originalFetch) {\n const restoreTarget = originalFetch\n window.fetch = restoreTarget\n if (window.fetch !== restoreTarget) {\n console.warn('Bug reporter: failed to restore original fetch')\n }\n originalFetch = null\n }\n}\n\n/**\n * Get a copy of all captured failed requests.\n */\nexport function getCapturedNetworkErrors(): FailedNetworkRequest[] {\n return [...capturedRequests]\n}\n\n/**\n * Clear the captured requests buffer.\n */\nexport function clearCapturedNetworkErrors(): void {\n capturedRequests = []\n}\n","import { useState, useRef, useEffect, useCallback, useMemo } from 'react'\nimport { X, Send, Loader2, CheckCircle2 } from 'lucide-react'\nimport { cn } from './cn'\nimport { isTouchCapable } from './utils'\nimport type {\n CaptureResult,\n ConversationMessage,\n BugReporterApiConfig,\n BugReporterUser,\n} from './types'\n\ninterface ReportModalProps {\n isOpen: boolean\n captureResult: CaptureResult | null\n apiConfig: BugReporterApiConfig\n siteId: string\n user: BugReporterUser\n onClose: () => void\n}\n\ntype ModalState = 'chatting' | 'submitting' | 'submitted' | 'error'\n\nexport function ReportModal({\n isOpen,\n captureResult,\n apiConfig,\n siteId,\n user,\n onClose,\n}: ReportModalProps) {\n const [messages, setMessages] = useState<ConversationMessage[]>([])\n const [input, setInput] = useState('')\n const [isLoading, setIsLoading] = useState(false)\n const [modalState, setModalState] = useState<ModalState>('chatting')\n const [reportId, setReportId] = useState<string | null>(null)\n const [screenshotUrl, setScreenshotUrl] = useState<string | null>(null)\n const [errorMessage, setErrorMessage] = useState<string | null>(null)\n const chatEndRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLTextAreaElement>(null)\n const hasInitRef = useRef(false)\n\n const apiHeaders = useMemo<Record<string, string>>(\n () => ({\n 'Content-Type': 'application/json',\n 'X-Bug-Reporter-Key': apiConfig.apiKey.trim(),\n }),\n [apiConfig.apiKey],\n )\n\n // Create screenshot preview URL — revoke previous URL on replacement and\n // reset to null when there is no valid screenshot.\n useEffect(() => {\n if (captureResult?.screenshot && captureResult.screenshot.size > 0) {\n const url = URL.createObjectURL(captureResult.screenshot)\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return url\n })\n return () => {\n URL.revokeObjectURL(url)\n setScreenshotUrl(null)\n }\n }\n\n // No valid screenshot — clear stale URL\n setScreenshotUrl((prev) => {\n if (prev) URL.revokeObjectURL(prev)\n return null\n })\n }, [captureResult])\n\n // Stable callback for sending initial AI message\n const sendInitialMessage = useCallback(async () => {\n if (!captureResult) return\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: [],\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n setMessages([{ role: 'assistant', content: data.message }])\n } catch {\n setMessages([\n {\n role: 'assistant',\n content:\n \"I can see you've captured a section of the page. What's going wrong here? Please describe the issue you're experiencing.\",\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }, [captureResult, apiConfig.apiUrl, apiHeaders])\n\n // Send initial AI message when modal opens\n useEffect(() => {\n if (isOpen && captureResult && !hasInitRef.current) {\n hasInitRef.current = true\n sendInitialMessage()\n }\n }, [isOpen, captureResult, sendInitialMessage])\n\n // Auto-scroll chat\n useEffect(() => {\n chatEndRef.current?.scrollIntoView({ behavior: 'smooth' })\n }, [messages])\n\n // Focus input\n useEffect(() => {\n if (isOpen && !isLoading) {\n inputRef.current?.focus()\n }\n }, [isOpen, isLoading, messages])\n\n const submitReport = useCallback(\n async (\n conversation: ConversationMessage[],\n structuredReport?: {\n summary: string\n expected_behavior: string\n actual_behavior: string\n reproducibility: string\n specific_data: string\n severity: string\n },\n ) => {\n if (!captureResult || modalState !== 'chatting') return\n setModalState('submitting')\n\n try {\n // Convert screenshot blob to base64\n let screenshotBase64: string | undefined\n if (captureResult.screenshot.size > 0) {\n const reader = new FileReader()\n screenshotBase64 = await new Promise<string>((resolve, reject) => {\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(captureResult.screenshot)\n })\n }\n\n // If no structured report from AI, request one via the API\n let report = structuredReport\n if (!report) {\n try {\n const summaryResponse = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: conversation,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n requestSummary: true,\n }),\n })\n if (summaryResponse.ok) {\n const data = await summaryResponse.json()\n report = data.structuredReport\n }\n } catch {\n // Fall through to default\n }\n }\n\n // Submit via the external submit endpoint\n const response = await fetch(`${apiConfig.apiUrl}/submit`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n screenshot: screenshotBase64,\n metadata: captureResult.metadata,\n conversation,\n structuredReport: report,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}))\n throw new Error(errorData.error || 'Failed to submit report')\n }\n\n const data = await response.json()\n setReportId(data.id)\n setModalState('submitted')\n } catch (err) {\n console.error('Bug reporter: failed to submit report', err)\n setErrorMessage(err instanceof Error ? err.message : 'Failed to submit report')\n setModalState('error')\n }\n },\n [captureResult, apiConfig.apiUrl, apiHeaders, modalState],\n )\n\n const handleManualSubmit = useCallback(() => {\n submitReport(messages)\n }, [submitReport, messages])\n\n async function sendMessage() {\n if (!input.trim() || isLoading || !captureResult) return\n\n const userMessage = input.trim()\n setInput('')\n\n const newMessages: ConversationMessage[] = [...messages, { role: 'user', content: userMessage }]\n setMessages(newMessages)\n setIsLoading(true)\n\n try {\n const response = await fetch(`${apiConfig.apiUrl}/chat`, {\n method: 'POST',\n headers: apiHeaders,\n body: JSON.stringify({\n messages: newMessages,\n metadata: captureResult.metadata,\n consoleErrors: captureResult.consoleErrors,\n networkErrors: captureResult.networkErrors,\n clickedElement: captureResult.metadata.clickedElement || null,\n }),\n })\n\n if (response.status === 401) {\n console.error(\n 'Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.',\n )\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content:\n \"The bug reporter service isn't configured correctly. Please let the site administrator know.\",\n },\n ])\n return\n }\n\n if (!response.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n\n setMessages([...newMessages, { role: 'assistant', content: data.message }])\n\n if (data.readyToSubmit && data.structuredReport) {\n await submitReport(\n [...newMessages, { role: 'assistant', content: data.message }],\n data.structuredReport,\n )\n }\n } catch {\n setMessages([\n ...newMessages,\n {\n role: 'assistant',\n content: 'I had trouble processing that. Could you try describing the issue again?',\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }\n\n function handleClose() {\n setMessages([])\n setInput('')\n setModalState('chatting')\n setReportId(null)\n setErrorMessage(null)\n // Revoke and clear stale screenshot URL\n if (screenshotUrl) {\n URL.revokeObjectURL(screenshotUrl)\n }\n setScreenshotUrl(null)\n hasInitRef.current = false\n onClose()\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Enter' && !e.shiftKey && !isTouchCapable()) {\n e.preventDefault()\n sendMessage()\n }\n }\n\n if (!isOpen) return null\n\n return (\n <div\n data-bug-reporter\n className=\"fixed inset-0 z-[10001] flex items-center justify-center bg-black/50 backdrop-blur-sm\"\n onClick={(e) => {\n if (e.target === e.currentTarget) handleClose()\n }}\n >\n <div\n className={cn(\n 'flex flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-gray-950',\n 'mx-4 w-full max-w-lg',\n 'max-[768px]:mx-0 max-[768px]:h-full max-[768px]:max-w-none max-[768px]:rounded-none',\n 'min-[769px]:max-h-[85vh]',\n )}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-gray-200 bg-gray-50/30 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/30\">\n <div>\n <h2 className=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">Bug Report</h2>\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">{siteId}</p>\n </div>\n <button\n onClick={handleClose}\n className=\"rounded-md p-1.5 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-gray-600 dark:text-gray-400\" />\n </button>\n </div>\n\n {/* Screenshot preview */}\n {screenshotUrl && (\n <div className=\"border-b border-gray-200 bg-gray-50/10 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/10\">\n <img\n src={screenshotUrl}\n alt=\"Captured section\"\n className=\"max-h-40 w-full rounded-md border border-gray-200 object-contain dark:border-gray-700\"\n />\n </div>\n )}\n\n {/* Chat area */}\n <div className=\"min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-3\">\n {modalState === 'submitted' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <CheckCircle2 className=\"mb-3 h-12 w-12 text-green-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Report Submitted\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n Reference:{' '}\n <code className=\"rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-800\">\n {reportId?.slice(0, 8)}\n </code>\n </p>\n <p className=\"mt-2 text-sm text-gray-500 dark:text-gray-400\">\n Thanks for the report — we&apos;ll look into it.\n </p>\n <button\n onClick={handleClose}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Done\n </button>\n </div>\n ) : modalState === 'error' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <X className=\"mb-3 h-12 w-12 text-red-500\" />\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100\">\n Submission Failed\n </h3>\n <p className=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">\n {errorMessage || 'Something went wrong. Please try again.'}\n </p>\n <button\n onClick={() => setModalState('chatting')}\n className=\"mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700\"\n >\n Try Again\n </button>\n </div>\n ) : modalState === 'submitting' ? (\n <div className=\"flex flex-col items-center justify-center py-8\">\n <Loader2 className=\"mb-3 h-8 w-8 animate-spin text-indigo-500\" />\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">Submitting your report...</p>\n </div>\n ) : (\n <>\n {captureResult?.screenshot.size === 0 && (\n <p className=\"text-xs text-amber-600\">\n Screenshot could not be captured. Please describe the visual issue in detail.\n </p>\n )}\n\n {messages.map((msg, i) => (\n <div\n key={i}\n className={cn(\n 'text-sm leading-relaxed',\n msg.role === 'assistant'\n ? 'rounded-lg bg-gray-100/50 p-3 text-gray-900 dark:bg-gray-800/50 dark:text-gray-100'\n : 'ml-8 rounded-lg bg-indigo-600 p-3 text-white',\n )}\n >\n {msg.content}\n </div>\n ))}\n\n {isLoading && (\n <div className=\"flex items-center gap-2 rounded-lg bg-gray-100/50 p-3 dark:bg-gray-800/50\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin text-gray-500\" />\n <span className=\"text-sm text-gray-500 dark:text-gray-400\">Thinking...</span>\n </div>\n )}\n\n <div ref={chatEndRef} />\n </>\n )}\n </div>\n\n {/* Input area — only show during chatting */}\n {modalState === 'chatting' && (\n <div className=\"border-t border-gray-200 px-4 py-3 dark:border-gray-800\">\n <div className=\"flex flex-col gap-2\">\n <textarea\n ref={inputRef}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe what's wrong...\"\n rows={2}\n className=\"w-full resize-none rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 focus:border-transparent focus:ring-2 focus:ring-indigo-400 focus:outline-none dark:border-gray-700 dark:bg-gray-950 dark:text-gray-100\"\n disabled={isLoading}\n />\n <div className=\"flex flex-col gap-2\">\n {captureResult &&\n (captureResult.consoleErrors.length > 0 ||\n captureResult.networkErrors.length > 0) && (\n <p className=\"flex-1 text-xs text-amber-600\">\n {[\n captureResult.consoleErrors.length > 0\n ? `${captureResult.consoleErrors.length} console error${captureResult.consoleErrors.length !== 1 ? 's' : ''}`\n : null,\n captureResult.networkErrors.length > 0\n ? `${captureResult.networkErrors.length} failed request${captureResult.networkErrors.length !== 1 ? 's' : ''}`\n : null,\n ]\n .filter(Boolean)\n .join(' + ')}{' '}\n captured — these will be included in the report.\n </p>\n )}\n <div className=\"flex justify-end gap-2\">\n <button\n onClick={sendMessage}\n disabled={!input.trim() || isLoading}\n className=\"flex items-center gap-1 rounded-md bg-indigo-600 px-3 py-2 text-nowrap text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"Send message\"\n >\n <Send className=\"h-4 w-4\" />\n <span className=\"text-sm font-medium\">Send message</span>\n </button>\n {messages.length >= 2 && (\n <button\n onClick={handleManualSubmit}\n disabled={isLoading}\n className=\"rounded-md bg-green-600 px-3 py-2 text-xs font-medium text-nowrap text-white transition-colors hover:bg-green-700 disabled:opacity-50\"\n title=\"Submit report now\"\n >\n <span className=\"text-sm font-medium\">Submit report</span>\n </button>\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","useEffect","useState","useRef","isCapturing","text","patchedFetch","jsx","jsxs","useState","isCapturing","useRef","useEffect","useState","useRef","useEffect","useCallback","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","data","Fragment","jsx","jsxs","useState","useEffect","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jarve/bug-reporter",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Bug reporter widget for Next.js apps. Reports flow to JARVE Agency dashboard.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",