{"version":3,"sources":["../src/browser.ts"],"sourcesContent":["/**\n * Browser entry — `gh-issue-tracker/browser`.\n *\n * Client-side helpers for collecting a bug report (screenshot + metadata) and\n * POSTing it to your own API route, which then calls `captureBugReport` on the\n * server (so the GitHub token never reaches the browser).\n *\n * `captureScreenshot` dynamically imports `modern-screenshot`, which is an\n * OPTIONAL peer dependency — install it only if you use screenshots. The server\n * entry (`gh-issue-tracker`) stays dependency-free.\n */\n\nexport interface CaptureScreenshotOptions {\n  /**\n   * Element to capture. Defaults to `[data-screenshot-target]` if present,\n   * otherwise `document.body`.\n   */\n  target?: HTMLElement\n  /** Downscale factor (0–1). Default 0.5 keeps payloads small. */\n  scale?: number\n  /** JPEG quality (0–1). Default 0.75. */\n  quality?: number\n  /**\n   * Selectors hidden during capture (e.g. the bug widget itself) so they don't\n   * appear in the screenshot. Restored afterwards.\n   */\n  hideSelectors?: string[]\n}\n\nexport interface CapturedScreenshot {\n  /** JPEG file ready to append to FormData. */\n  file: File\n  /** Data URL for an inline preview. */\n  preview: string\n}\n\n/**\n * Capture the current page (including any open modal/dialog) as a JPEG using\n * `modern-screenshot`. Returns null if capture fails or runs outside a browser.\n */\nexport async function captureScreenshot(\n  options: CaptureScreenshotOptions = {},\n): Promise<CapturedScreenshot | null> {\n  if (typeof document === 'undefined') return null\n\n  const hideSelectors = options.hideSelectors ?? ['[data-bug-report]', '[data-pin-overlay]']\n  const hidden: HTMLElement[] = []\n\n  try {\n    const { domToJpeg } = await import('modern-screenshot')\n\n    for (const sel of hideSelectors) {\n      document.querySelectorAll<HTMLElement>(sel).forEach((el) => {\n        el.style.visibility = 'hidden'\n        hidden.push(el)\n      })\n    }\n\n    const target =\n      options.target ??\n      document.querySelector<HTMLElement>('[data-screenshot-target]') ??\n      document.body\n\n    const dataUrl = await domToJpeg(target, {\n      scale: options.scale ?? 0.5,\n      quality: options.quality ?? 0.75,\n    })\n    const blob = await (await fetch(dataUrl)).blob()\n    const file = new File([blob], 'screenshot.jpg', { type: 'image/jpeg' })\n    return { file, preview: dataUrl }\n  } catch (err) {\n    console.error('[gh-issue-tracker] screenshot capture failed:', err)\n    return null\n  } finally {\n    for (const el of hidden) el.style.visibility = ''\n  }\n}\n\nexport interface SubmitBugReportInput {\n  /** Endpoint that calls `captureBugReport` on the server. */\n  endpoint: string\n  message: string\n  screenshot?: File | null\n  /** Pin location as a percentage of the viewport (0–100). */\n  pin?: { x: number; y: number }\n  /** Extra fields appended to the multipart form. */\n  extra?: Record<string, string | number | undefined>\n  /** Passed to fetch (e.g. credentials, headers). */\n  fetchInit?: RequestInit\n}\n\nexport interface SubmitBugReportResult {\n  ok: boolean\n  status: number\n  issueNumber?: number\n  issueUrl?: string\n  error?: string\n}\n\n/**\n * Build the multipart form for a bug report, auto-collecting browser/environment\n * metadata (viewport, screen, UA, language, timezone, connection, referrer).\n */\nexport function buildBugReportFormData(input: SubmitBugReportInput): FormData {\n  const form = new FormData()\n  form.append('message', input.message)\n  form.append('pageUrl', window.location.href)\n  if (input.pin) {\n    form.append('pinX', String(input.pin.x))\n    form.append('pinY', String(input.pin.y))\n  }\n  form.append('userAgent', navigator.userAgent)\n  form.append('viewportWidth', String(window.innerWidth))\n  form.append('viewportHeight', String(window.innerHeight))\n  if (document.title) form.append('pageTitle', document.title)\n  form.append('screenWidth', String(screen.width))\n  form.append('screenHeight', String(screen.height))\n  form.append('devicePixelRatio', String(window.devicePixelRatio))\n  form.append('language', navigator.language)\n  form.append('timezone', Intl.DateTimeFormat().resolvedOptions().timeZone)\n  const connection = (navigator as Navigator & { connection?: { effectiveType?: string } }).connection\n  if (connection?.effectiveType) form.append('connectionType', connection.effectiveType)\n  if (document.referrer) form.append('referrer', document.referrer)\n  for (const [k, v] of Object.entries(input.extra ?? {})) {\n    if (v !== undefined) form.append(k, String(v))\n  }\n  if (input.screenshot) form.append('screenshot', input.screenshot, 'screenshot.jpg')\n  return form\n}\n\n/** Build the form and POST it to your bug-report endpoint. */\nexport async function submitBugReport(\n  input: SubmitBugReportInput,\n): Promise<SubmitBugReportResult> {\n  const form = buildBugReportFormData(input)\n  try {\n    const res = await fetch(input.endpoint, {\n      method: 'POST',\n      body: form,\n      ...input.fetchInit,\n    })\n    const data = (await res.json().catch(() => null)) as\n      | { issueNumber?: number; issueUrl?: string; error?: string }\n      | null\n    return {\n      ok: res.ok,\n      status: res.status,\n      issueNumber: data?.issueNumber,\n      issueUrl: data?.issueUrl,\n      error: res.ok ? undefined : (data?.error ?? `HTTP ${res.status}`),\n    }\n  } catch (err) {\n    return { ok: false, status: 0, error: err instanceof Error ? err.message : 'Network error' }\n  }\n}\n"],"mappings":"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","names":[]}