@jarve/bug-reporter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/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 } from './floating-button'\nimport { CaptureOverlay } from './capture-overlay'\nimport { ReportModal } from './report-modal'\nimport { startCapturing, stopCapturing, clearCapturedErrors } from './console-capture'\nimport { startNetworkCapture, stopNetworkCapture, clearCapturedNetworkErrors } from './network-capture'\nimport type { CaptureResult, BugReporterUser } from './types'\n\ninterface JarveBugReporterProps {\n /** Base URL for the external bug reporter API (e.g. \"https://jarve.com.au/api/bug-reporter/external\") */\n apiUrl: string\n /** API key for your site (starts with \"brk_\") */\n apiKey: string\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 children,\n}: JarveBugReporterProps) {\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 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 = apiKey.startsWith('brk_') ? apiKey.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 isActive={captureMode} onClick={toggleCaptureMode} />\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 { Bug } from 'lucide-react'\nimport { cn } from './cn'\n\ninterface FloatingButtonProps {\n isActive: boolean\n onClick: () => void\n}\n\nexport function FloatingButton({ isActive, onClick }: FloatingButtonProps) {\n return (\n <button\n onClick={onClick}\n className={cn(\n 'fixed bottom-6 right-6 z-[9999] flex h-12 w-12 items-center justify-center rounded-full shadow-lg transition-all duration-200',\n 'hover:scale-110 focus:outline-none focus:ring-2 focus:ring-offset-2',\n isActive\n ? 'bg-red-500 text-white animate-pulse focus:ring-red-400'\n : 'bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-400',\n 'bottom-4 right-4 h-10 w-10 md:bottom-6 md:right-6 md:h-12 md:w-12'\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 )\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 { getNearestSection, collectMetadata, collectElementInfo } from './utils'\nimport { getCapturedErrors } from './console-capture'\nimport { getCapturedNetworkErrors } from './network-capture'\nimport type { CaptureResult, CaptureMetadata, ConsoleError, FailedNetworkRequest } 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\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 overlayRef = useRef<HTMLDivElement>(null)\n const hoveredElementRef = useRef<HTMLElement | null>(null)\n const rafRef = useRef<number | null>(null)\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!isActive || isCapturing) 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]\n )\n\n const handleClick = useCallback(\n async (e: MouseEvent) => {\n if (!isActive || isCapturing) 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 const elementInfo = collectElementInfo(target, section, e)\n\n setIsCapturing(true)\n\n try {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n\n await new Promise((r) => setTimeout(r, 50))\n\n const MAX_DIMENSION = 2000\n const sectionRect = section.getBoundingClientRect()\n const pixelRatio = (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 [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 const blob = new Blob([arr], { type: mime })\n\n const metadata: CaptureMetadata = collectMetadata(\n section,\n siteId,\n reporterName,\n reporterEmail,\n elementInfo\n )\n\n const consoleErrors: ConsoleError[] = getCapturedErrors()\n const networkErrors: FailedNetworkRequest[] = getCapturedNetworkErrors()\n\n onCapture({ screenshot: blob, metadata, consoleErrors, networkErrors })\n } catch (err) {\n console.error('Bug reporter: failed to capture screenshot', err)\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 } finally {\n setIsCapturing(false)\n }\n },\n [isActive, isCapturing, siteId, reporterName, reporterEmail, onCapture]\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) return\n setHoveredRect(hoveredElementRef.current.getBoundingClientRect())\n }, [])\n\n useEffect(() => {\n if (!isActive) {\n setHoveredElement(null)\n setHoveredRect(null)\n hoveredElementRef.current = null\n return\n }\n\n document.addEventListener('mousemove', handleMouseMove, true)\n document.addEventListener('click', handleClick, true)\n document.addEventListener('keydown', handleKeyDown)\n window.addEventListener('scroll', handleScroll, { passive: true })\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove, true)\n document.removeEventListener('click', handleClick, true)\n document.removeEventListener('keydown', handleKeyDown)\n window.removeEventListener('scroll', handleScroll)\n if (rafRef.current) cancelAnimationFrame(rafRef.current)\n }\n }, [isActive, handleMouseMove, handleClick, handleKeyDown, handleScroll])\n\n if (!isActive || !hoveredElement || !hoveredRect) 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 left-0 right-0 z-[10000] bg-indigo-600 text-white text-center py-2 px-4 text-sm font-medium\"\n >\n Click on the section with the bug. Press <kbd className=\"px-1.5 py-0.5 bg-indigo-800 rounded text-xs mx-1\">Esc</kbd> to cancel.\n </div>\n\n {/* Section highlight overlay */}\n <div\n ref={overlayRef}\n data-bug-reporter\n className=\"fixed pointer-events-none z-[9998] border-2 border-indigo-500 rounded-sm transition-all duration-150 ease-out\"\n style={{\n top: hoveredRect.top - 2,\n left: hoveredRect.left - 2,\n width: hoveredRect.width + 4,\n height: hoveredRect.height + 4,\n backgroundColor: 'rgba(99, 102, 241, 0.08)',\n }}\n />\n\n {/* Full-page cursor override */}\n {isActive && (\n <style>{`* { cursor: crosshair !important; }`}</style>\n )}\n </>\n )\n}\n","import { UAParser } from 'ua-parser-js'\nimport type { CaptureMetadata, ClickedElement } 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 * Collect info about the exact element the user clicked.\n */\nexport function collectElementInfo(\n target: HTMLElement,\n section: HTMLElement,\n event: MouseEvent\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: event.pageX,\n clickY: event.pageY,\n relativeClickX: event.clientX - sectionRect.left,\n relativeClickY: event.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)\n 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\nlet capturedRequests: FailedNetworkRequest[] = []\nlet isCapturing = false\nlet originalFetch: typeof window.fetch | null = null\n\nfunction truncateBody(body: string | null | undefined, maxLen = 500): 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 * 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 = typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url\n const method = init?.method || (typeof input !== 'string' && !(input instanceof URL) ? input.method : 'GET') || 'GET'\n\n // Skip our own bug reporter endpoints and non-HTTP URLs\n if (url.includes('/api/bug-reporter/') || url.includes('/bug-reporter/external/') || url.startsWith('data:') || url.startsWith('blob:')) {\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 let responseBody: string | null = null\n try {\n const cloned = response.clone()\n const text = await cloned.text()\n responseBody = truncateBody(text)\n } catch {\n // Body may not be readable\n }\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 } from 'react'\nimport { X, Send, Loader2, CheckCircle2 } from 'lucide-react'\nimport { cn } from './cn'\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 = {\n 'Content-Type': 'application/json',\n 'X-Bug-Reporter-Key': apiConfig.apiKey,\n }\n\n // Create screenshot preview URL\n useEffect(() => {\n if (captureResult?.screenshot && captureResult.screenshot.size > 0) {\n const url = URL.createObjectURL(captureResult.screenshot)\n setScreenshotUrl(url)\n return () => URL.revokeObjectURL(url)\n }\n }, [captureResult])\n\n // Send initial AI message when modal opens\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(() => {\n if (isOpen && captureResult && !hasInitRef.current) {\n hasInitRef.current = true\n sendInitialMessage()\n }\n }, [isOpen, captureResult])\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 async function sendInitialMessage() {\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.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 }\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[] = [\n ...messages,\n { role: 'user', content: userMessage },\n ]\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.ok) throw new Error('Failed to get AI response')\n\n const data = await response.json()\n\n setMessages([\n ...newMessages,\n { role: 'assistant', content: data.message },\n ])\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:\n \"I had trouble processing that. Could you try describing the issue again?\",\n },\n ])\n } finally {\n setIsLoading(false)\n }\n }\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(\n err instanceof Error ? err.message : 'Failed to submit report'\n )\n setModalState('error')\n }\n },\n [captureResult, apiConfig, apiHeaders, modalState]\n )\n\n function handleManualSubmit() {\n submitReport(messages)\n }\n\n function handleClose() {\n setMessages([])\n setInput('')\n setModalState('chatting')\n setReportId(null)\n setErrorMessage(null)\n hasInitRef.current = false\n onClose()\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Enter' && !e.shiftKey) {\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 'bg-white dark:bg-gray-950 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-800 flex flex-col overflow-hidden',\n 'w-full max-w-lg mx-4',\n 'max-[768px]:mx-0 max-[768px]:rounded-none max-[768px]:max-w-none max-[768px]:h-full',\n 'min-[769px]:max-h-[85vh]'\n )}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50/30 dark:bg-gray-900/30\">\n <div>\n <h2 className=\"font-semibold text-sm text-gray-900 dark:text-gray-100\">Bug Report</h2>\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">\n {siteId}\n </p>\n </div>\n <button\n onClick={handleClose}\n className=\"p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors\"\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=\"px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50/10 dark:bg-gray-900/10\">\n <img\n src={screenshotUrl}\n alt=\"Captured section\"\n className=\"w-full max-h-40 object-contain rounded-md border border-gray-200 dark:border-gray-700\"\n />\n </div>\n )}\n\n {/* Chat area */}\n <div className=\"flex-1 overflow-y-auto px-4 py-3 space-y-3 min-h-0\">\n {modalState === 'submitted' ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <CheckCircle2 className=\"h-12 w-12 text-green-500 mb-3\" />\n <h3 className=\"font-semibold text-lg text-gray-900 dark:text-gray-100\">Report Submitted</h3>\n <p className=\"text-sm text-gray-500 dark:text-gray-400 mt-1\">\n Reference: <code className=\"text-xs bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded\">{reportId?.slice(0, 8)}</code>\n </p>\n <p className=\"text-sm text-gray-500 dark:text-gray-400 mt-2\">\n Thanks for the report — we&apos;ll look into it.\n </p>\n <button\n onClick={handleClose}\n className=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-md text-sm hover:bg-indigo-700 transition-colors\"\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=\"h-12 w-12 text-red-500 mb-3\" />\n <h3 className=\"font-semibold text-lg text-gray-900 dark:text-gray-100\">Submission Failed</h3>\n <p className=\"text-sm text-gray-500 dark:text-gray-400 mt-1\">\n {errorMessage || 'Something went wrong. Please try again.'}\n </p>\n <button\n onClick={() => setModalState('chatting')}\n className=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-md text-sm hover:bg-indigo-700 transition-colors\"\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=\"h-8 w-8 animate-spin text-indigo-500 mb-3\" />\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">\n Submitting your report...\n </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 ? 'bg-gray-100/50 dark:bg-gray-800/50 rounded-lg p-3 text-gray-900 dark:text-gray-100'\n : 'bg-indigo-600 text-white rounded-lg p-3 ml-8'\n )}\n >\n {msg.content}\n </div>\n ))}\n\n {isLoading && (\n <div className=\"bg-gray-100/50 dark:bg-gray-800/50 rounded-lg p-3 flex items-center gap-2\">\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\">\n Thinking...\n </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 dark:border-gray-800 px-4 py-3\">\n <div className=\"flex 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=\"flex-1 resize-none rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-950 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent\"\n disabled={isLoading}\n />\n <div className=\"flex flex-col gap-1\">\n <button\n onClick={sendMessage}\n disabled={!input.trim() || isLoading}\n className=\"p-2 rounded-md bg-indigo-600 text-white hover:bg-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n title=\"Send message\"\n >\n <Send className=\"h-4 w-4\" />\n </button>\n {messages.length >= 2 && (\n <button\n onClick={handleManualSubmit}\n disabled={isLoading}\n className=\"px-2 py-1 rounded-md bg-green-600 text-white hover:bg-green-700 disabled:opacity-50 text-xs font-medium transition-colors\"\n title=\"Submit report now\"\n >\n Submit\n </button>\n )}\n </div>\n </div>\n {captureResult &&\n (captureResult.consoleErrors.length > 0 ||\n captureResult.networkErrors.length > 0) && (\n <p className=\"text-xs text-amber-600 mt-1.5\">\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>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","isCapturing","patchedFetch","jsx","isCapturing","useState","useRef","useEffect","useCallback","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","data","Fragment","jsx","jsxs","useState","useEffect","useCallback"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@jarve/bug-reporter",
3
+ "version": "0.1.0",
4
+ "description": "Bug reporter widget for Next.js apps. Reports flow to JARVE Agency dashboard.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch"
21
+ },
22
+ "peerDependencies": {
23
+ "react": ">=18",
24
+ "react-dom": ">=18"
25
+ },
26
+ "dependencies": {
27
+ "html-to-image": "^1.11.13",
28
+ "lucide-react": ">=0.300.0",
29
+ "ua-parser-js": "^2.0.9",
30
+ "clsx": "^2.1.1",
31
+ "tailwind-merge": "^3.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/react": "^19",
35
+ "@types/react-dom": "^19",
36
+ "@types/ua-parser-js": "^0.7.39",
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^5.9.0",
39
+ "react": "^19.0.0",
40
+ "react-dom": "^19.0.0"
41
+ },
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/vanderhaka/jarve-agency"
46
+ }
47
+ }