Get started
The form (anything else)
Every adapter is a thin layer over three functions that work anywhere:
import { captureScreenshot, pickElement, buildReport, sendReport } from "bugbottle";
import { htmlToImage } from "bugbottle/html-to-image";
const screenshot = await captureScreenshot(htmlToImage); // PNG data URL
const element = await pickElement(); // null if they pressed Escape
const report = buildReport({
type: "bug",
message,
screenshotDataUrl: screenshot,
elements: element ? [element] : [],
});
const { id } = await sendReport("/api/feedback", report);sendReport resolves on a 2xx, throws SendFailedError (with status and
the parsed body) on anything else, gives up with SendTimeoutError after
15 seconds (timeoutMs), and lets network errors through untouched.
captureScreenshot estimates from the capture area whether a full-scale render
would be larger than a server would accept, and starts at half scale when it
would — rendering is expensive, so a page that was never going to fit should
not be rendered twice. It still retries at half scale if the estimate was
wrong, and throws ScreenshotTooLargeError if that is too big as well. Treat
either as "send without the picture". Pass pixelRatio to skip the estimate
and force a scale, bytesPerPixelEstimate to tune it for pages that compress
unusually well or badly, and onCapture to see what each capture cost:
await captureScreenshot(htmlToImage, {
onCapture: ({ pixelRatio, length, attempts, ms }) => {
console.info(`screenshot: ${length} chars at ${pixelRatio}x, ${attempts} render(s), ${ms}ms`);
},
});