From 5009ff4c7f3da81811d472953efe1efa83d0aa29 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 19 May 2026 00:18:27 +0000 Subject: [PATCH 1/3] docs: sync documentation from bugdrop repo --- src/content/docs/configuration.mdx | 109 +++++++++++++++++++++++++---- src/content/docs/faq.mdx | 10 +-- src/content/docs/installation.mdx | 15 ++-- src/content/docs/security.mdx | 38 ++++++---- src/content/docs/self-hosting.mdx | 7 +- src/content/docs/styling.mdx | 2 + 6 files changed, 140 insertions(+), 41 deletions(-) diff --git a/src/content/docs/configuration.mdx b/src/content/docs/configuration.mdx index 7c8244b..d5bf4ea 100644 --- a/src/content/docs/configuration.mdx +++ b/src/content/docs/configuration.mdx @@ -9,14 +9,15 @@ These attributes control the fundamental behavior of the widget. | Attribute | Default | Description | |-----------|---------|-------------| | `data-repo` | **(required)** | GitHub repository in `owner/repo` format | -| `data-theme` | `"light"` | Widget theme: `"light"` or `"dark"` | +| `data-theme` | `"auto"` | Widget theme: `"light"`, `"dark"`, or `"auto"` | | `data-position` | `"bottom-right"` | Button position: `"bottom-right"` or `"bottom-left"` | -| `data-color` | `"#7c3aed"` | Primary accent color (any CSS color value) | +| `data-color` | `"#14b8a6"` | Primary accent color (any CSS color value) | | `data-icon` | *(default bug icon)* | Custom icon: URL to an image, `"none"` to hide, or omit for default | -| `data-label` | `""` | Text label displayed next to the button icon | +| `data-label` | `"Feedback"` | Text label displayed next to the button icon | | `data-category-labels` | built-in labels | Self-hosted JSON mapping from built-in categories to GitHub labels | | `data-button` | `"true"` | Show the floating button: `"true"` or `"false"` | | `data-welcome` | `"Report a bug or request a feature"` | Welcome message displayed at the top of the form | +| `data-show-issue-link` | `"public"` | Show the GitHub issue link after submission: `"public"`, `"always"`, or `"never"` | ### Basic Example @@ -35,11 +36,16 @@ These attributes control the fundamental behavior of the widget. The `data-theme` attribute controls the overall color scheme of the widget: -- **`light`** (default) -- White background with dark text. Suitable for most sites. +- **`auto`** (default) -- Follows the user's system color scheme. +- **`light`** -- White background with dark text. Suitable for most sites. - **`dark`** -- Dark background with light text. Ideal for sites with dark color schemes. ```html - + + + + @@ -52,8 +58,31 @@ The `data-theme` attribute controls the overall color scheme of the widget: The `data-position` attribute controls where the floating button appears: -- **`bottom-right`** (default) -- Fixed to the bottom-right corner -- **`bottom-left`** -- Fixed to the bottom-left corner +- **`bottom-right`** (default) -- Anchored to the right edge +- **`bottom-left`** -- Anchored to the left edge + +The button is shown as an edge label with a small drag handle. Users can drag it vertically +when it covers page content, and BugDrop remembers that position in the browser for the same +repository and side. + +## Issue Link Visibility + +Control whether the success confirmation links to the GitHub Issue that was created. + +| Value | Behavior | +|-------|----------| +| `"public"` | Default. Show the issue number and GitHub link for public repositories only. Private repository submissions use a generic success message. | +| `"always"` | Show the issue number and GitHub link whenever the server returns an issue URL, including private repositories. Use this only for trusted internal users who already have GitHub access to the target repository. | +| `"never"` | Show the issue number for public repositories, but do not show a GitHub link. Private repository submissions use a generic success message. | + +```html + + +``` ## Submitter Information @@ -88,18 +117,18 @@ Allow users to dismiss the floating button so it does not obstruct their view. T | Attribute | Default | Description | |-----------|---------|-------------| | `data-button-dismissible` | `"false"` | Allow users to dismiss (hide) the floating button | -| `data-dismiss-duration` | `"session"` | How long the button stays dismissed: `"session"` (until tab close) or a number in minutes (e.g., `"60"`) | +| `data-dismiss-duration` | Forever | How long the button stays dismissed: omit for forever, or provide a number of days (e.g., `"7"`) | | `data-show-restore` | `"true"` | Show a small restore link after dismissing | ### Dismissible Button Example ```html - + ``` @@ -109,7 +138,7 @@ When a user dismisses the button: 1. The floating button hides 2. If `data-show-restore` is `"true"`, a small "Restore feedback button" link appears in the corner 3. The button stays hidden for the duration specified by `data-dismiss-duration` -4. After the duration expires (or the session ends), the button reappears automatically +4. After the duration expires, the button reappears automatically ## Feedback Categories @@ -211,6 +240,7 @@ Control how screenshots are collected during the feedback flow. |-----------|---------|-------------| | `data-screenshot` | `"optional"` | Screenshot mode: `"optional"`, `"auto"`, or `"required"` | | `data-screenshot-scale` | `"2"` | Minimum pixel ratio for screenshot captures | +| `data-element-context-max-area` | `"0"` | Maximum surrounding container size for Select Element captures, expressed as a multiple of the visible viewport area | ### Screenshot Modes @@ -218,7 +248,58 @@ Control how screenshots are collected during the feedback flow. - **`auto`** -- Automatically captures a full-page screenshot after the form is submitted, without showing the manual screenshot picker or redaction step. - **`required`** -- Requires a screenshot before submission. Users can choose full page, element, or area, then annotate or redact before submitting. -Manual redaction is controlled by the person submitting feedback. Use developer-configured masking for fields that should never appear in screenshots, especially with automatic screenshots. +Manual redaction is controlled by the person submitting feedback. Use developer-configured masking for fields that should be visually covered in supported screenshot modes, especially with automatic screenshots. Auto mode warns users that a full-page screenshot will be attached, but it does not show the manual picker or annotation review step. + +### Select Element screenshots + +When a user chooses **Select Element**, BugDrop captures the selected element and draws a rounded border around it. The border is drawn with a small 2px inner gap so it does not sit directly on top of the selected element. This keeps the default behavior tight and predictable. + +The highlight uses the same styling controls as the widget: + +- `data-color` controls the highlight color. +- `data-radius` controls the rounded corners. +- `data-border-width` controls the highlight border thickness. + +GitHub issues include a screenshot caption that identifies the selected element border and includes the exact resolved highlight color. + +Selected-element reports also include CSS selector metadata for the chosen element, including a full CSS path. Avoid putting secrets, customer identifiers, or other private data in DOM IDs or class names if issue visibility is broader than your internal team. + +The amount of surrounding context is controlled by one attribute: + +```html + +``` + +- Omit `data-element-context-max-area`, or set it to `"0"`, to capture only the selected element. +- Increase `data-element-context-max-area` to allow larger surrounding containers. This can provide more context for triage and AI-assisted review, but may create larger images and slower captures. + +Select Element captures are capped at 1x pixel ratio even when `data-screenshot-scale` is higher. This keeps context screenshots small enough for practical issue uploads. + +The screenshot review step also links to this configuration section when a Select Element screenshot is being reviewed, so teams can discover the context setting during testing. + +### Developer-configured screenshot masking + +BugDrop can visually mask fields that your app marks as private before a screenshot is uploaded: + +```html + +
Private account details
+``` + +Supported attributes: + +- `data-bugdrop-redact` +- `data-bd-redact` +- `data-bugdrop-redacted` +- `data-bugdrop-mask` + +This is best-effort visual masking, not a data-loss-prevention or security boundary. BugDrop can only mask content it can measure in the page DOM. Unmarked sensitive information can still appear, and browser rendering limits can apply to canvas, image, video, iframe, SVG, shadow DOM, pseudo-element, and highly custom control content. + +Selected-area screenshots are rendered to the selected dimensions and masks are translated into that crop, but the capture still runs client-side against the page DOM. Avoid screenshots entirely for pages where client-side screenshot rendering is unacceptable. ```html @@ -312,13 +393,13 @@ Here is a script tag using many configuration options together: data-show-email="true" data-require-email="true" data-button-dismissible="true" - data-dismiss-duration="120" + data-dismiss-duration="7" data-show-restore="true" data-label="Feedback" > ``` -This configuration creates a dark-themed widget in the bottom-left corner with an indigo accent color, collects the submitter's name and email (email required), and shows a "Feedback" label on the button. The button is dismissible for 2 hours with a restore link. +This configuration creates a dark-themed widget in the bottom-left corner with an indigo accent color, collects the submitter's name and email (email required), and shows a "Feedback" label on the button. The button is dismissible for 7 days with a restore link. ## Next Steps diff --git a/src/content/docs/faq.mdx b/src/content/docs/faq.mdx index 014b622..94ae350 100644 --- a/src/content/docs/faq.mdx +++ b/src/content/docs/faq.mdx @@ -42,9 +42,9 @@ Since BugDrop uses Shadow DOM for isolation, it does not conflict with any frame ### How does BugDrop handle screenshots? -BugDrop uses [html2canvas](https://html2canvas.hertzen.com/) to capture screenshots entirely on the client side. When a user clicks the screenshot button: +BugDrop uses [html-to-image](https://github.com/bubkoo/html-to-image) to capture screenshots entirely on the client side. When a user clicks the screenshot button: -1. html2canvas renders the current page to an HTML Canvas element in the user's browser +1. html-to-image renders the current page to an HTML Canvas element in the user's browser 2. In optional and required manual screenshot flows, the user can annotate the screenshot and cover sensitive regions with opaque blocks 3. The canvas is converted to a PNG image 4. The image is sent to the BugDrop API along with the form submission @@ -198,7 +198,7 @@ Check the following: 1. **Verify the script tag** -- Make sure it does not have `async` or `defer` attributes 2. **Check the console** -- Open your browser's developer console and look for `[BugDrop]` messages or errors -3. **Check CSP** -- If your site uses a Content Security Policy, make sure `https://bugdrop.neonwatty.workers.dev` and `https://cdn.jsdelivr.net` are allowed in `script-src` +3. **Check CSP** -- If your site uses a Content Security Policy, make sure `https://bugdrop.neonwatty.workers.dev` is allowed in `script-src` 4. **Check the repo** -- Ensure `data-repo` matches your GitHub repository path exactly (case-sensitive) 5. **Check app installation** -- Verify the BugDrop GitHub App is installed on the repository @@ -216,8 +216,8 @@ If the widget appears but submissions fail: If issues are created but without screenshots: 1. **Check branch protection** -- The GitHub App needs permission to push to the `bugdrop-screenshots` branch. See the [Installation](/docs/installation) page for details on branch protection configuration. -2. **Check CSP** -- The html2canvas library is loaded from `cdn.jsdelivr.net`. Make sure it is allowed by your Content Security Policy. -3. **Cross-origin content** -- html2canvas cannot capture cross-origin iframes or images loaded without CORS headers. These elements may appear blank in the screenshot. +2. **Check CSP** -- Make sure your Content Security Policy allows the BugDrop widget script. +3. **Cross-origin content** -- Browser screenshot rendering cannot capture cross-origin iframes or images loaded without CORS headers. These elements may appear blank in the screenshot. ### Can I use BugDrop on a static site? diff --git a/src/content/docs/installation.mdx b/src/content/docs/installation.mdx index 81b8828..57f49a4 100644 --- a/src/content/docs/installation.mdx +++ b/src/content/docs/installation.mdx @@ -73,8 +73,9 @@ See the [Configuration](/docs/configuration) and [Styling](/docs/styling) docs f ## Protecting sensitive data -If your page renders customer data, billing details, or any other content you do not -want to appear in submitted screenshots, mark those elements with `data-bugdrop-mask`: +If your page renders customer data, billing details, or any other content you want +BugDrop to visually cover in supported screenshot modes, mark those elements with +`data-bugdrop-mask`: ```html
@@ -82,9 +83,9 @@ want to appear in submitted screenshots, mark those elements with `data-bugdrop-
``` -BugDrop covers each marked element with an opaque rectangle on the captured screenshot. +BugDrop covers each marked element with an opaque rectangle on supported captured screenshots. Password inputs and credit-card autocomplete fields are masked automatically. See -[Screenshot masking](/docs/security#screenshot-masking) on the Security page for details. +[Screenshot masking](/security#screenshot-masking) on the Security page for details. ## Step 3: You Are Done @@ -107,13 +108,13 @@ The BugDrop script tag should **not** include the `async` or `defer` attributes. ### Content Security Policy (CSP) -If your site uses a Content Security Policy, you will need to allow `cdn.jsdelivr.net` in your CSP directives. BugDrop loads the html2canvas library from jsDelivr for screenshot functionality: +If your site uses a Content Security Policy, allow the BugDrop worker domain in your CSP directives: ``` -Content-Security-Policy: script-src 'self' https://bugdrop.neonwatty.workers.dev https://cdn.jsdelivr.net; +Content-Security-Policy: script-src 'self' https://bugdrop.neonwatty.workers.dev; ``` -If you have a strict CSP, make sure both the BugDrop worker domain and jsDelivr are included in your `script-src` directive. +If you have a strict CSP, make sure the BugDrop worker domain is included in your `script-src` directive. ### Branch Protection and the Screenshots Branch diff --git a/src/content/docs/security.mdx b/src/content/docs/security.mdx index 34692c8..c6c202f 100644 --- a/src/content/docs/security.mdx +++ b/src/content/docs/security.mdx @@ -53,14 +53,14 @@ Treat screenshots as unauthenticated user-generated content. The hosted service ### Screenshot Format -Screenshots are captured client-side using [html2canvas](https://html2canvas.hertzen.com/), which renders the current page to a canvas element in the user's browser. The canvas is then converted to a PNG image and uploaded. This means: +Screenshots are captured client-side using [html-to-image](https://github.com/bubkoo/html-to-image), which renders the current page to a canvas element in the user's browser. The canvas is then converted to a PNG image and uploaded. This means: - The initial screenshot capture is rendered from what the user actually sees - No server-side rendering or page access is required - The screenshot is generated entirely in the user's browser before being sent to the API - Users can redact additional screenshot regions before submitting when using the manual screenshot flow -Manual redaction is user-driven and does not automatically detect sensitive content. It complements, but does not replace, developer-configured masking for fields that should never appear in screenshots, especially when using automatic screenshots. +Manual redaction is user-driven and does not automatically detect sensitive content. It complements, but does not replace, developer-configured masking for fields that should be visually covered in supported screenshot modes, especially when using automatic screenshots. Because clients are untrusted, the API validates screenshot uploads server-side before storing them. BugDrop currently accepts PNG data URLs only and rejects SVG, malformed base64, oversized payloads, and data that does not have a PNG file signature. @@ -78,11 +78,10 @@ BugDrop is built with a privacy-first approach: ### Screenshot masking -You can mark sensitive elements so they never appear in submitted screenshots. Add the -`data-bugdrop-mask` attribute to any element you want covered: +You can mark sensitive elements so BugDrop visually covers them in supported screenshot modes. Add `data-bugdrop-redact` or `data-bugdrop-mask` to any element you want covered: ```html - +
Customer name @@ -90,9 +89,19 @@ You can mark sensitive elements so they never appear in submitted screenshots. A
``` -When a user submits feedback, BugDrop paints an opaque rectangle over each tagged -element's bounding box on the captured PNG before showing the user the annotator -preview. The user sees what is masked and can audit it before submitting. +Supported explicit attributes are `data-bugdrop-redact`, `data-bd-redact`, +`data-bugdrop-redacted`, and `data-bugdrop-mask`. + +When a user submits feedback, BugDrop plans redactions from matching DOM +elements, then paints an opaque rectangle over each target's measured bounding +box on supported captured PNGs. In manual screenshot flows, the masked image is +shown in the annotator preview so the user can audit it before submitting. In +automatic screenshot mode, BugDrop applies supported masks but submits without +showing the preview step. + +Masking is best-effort visual coverage, not a data-loss-prevention or security +boundary. Users should still review screenshots before submitting when the manual +screenshot flow is enabled. **Inheritance.** When an ancestor has `data-bugdrop-mask`, the entire ancestor box is masked as a single rectangle. Descendants do not get individual rectangles — this @@ -105,8 +114,14 @@ prevents gaps from CSS `gap` or non-masked siblings inside a masked container. **Known limitations:** -- Elements inside Shadow DOM and cross-origin iframes are not traversed in this - iteration. +- Elements inside open Shadow DOM are traversed when the browser exposes the + shadow root. Closed Shadow DOM cannot be traversed; mark the host element if + the whole custom control should be covered. +- Iframe contents are not traversed. Mark the iframe element itself if the whole + embedded frame should be visually covered. +- BugDrop does not inspect pixels or text inside canvas, image, video, plugin, + or iframe content. Mark the containing DOM element if that entire region should + be visually covered. - Mask rectangles are collected at the start of capture. If the page reflows or reveals sensitive elements between collection and the moment `html-to-image` finishes rendering, the mask may not cover the final pixels. Keep masked content stable during @@ -121,8 +136,7 @@ prevents gaps from CSS `gap` or non-masked siblings inside a masked container. The only network requests BugDrop makes are: 1. Loading the widget script from Cloudflare Workers -2. Loading html2canvas from cdn.jsdelivr.net (for screenshot functionality) -3. Submitting the feedback form to the BugDrop Cloudflare Worker API +2. Submitting the feedback form to the BugDrop Cloudflare Worker API The API acts as a pass-through to the GitHub API -- it receives the form data, creates the issue and uploads the screenshot, and discards the data. Nothing is persisted on the Cloudflare Worker. diff --git a/src/content/docs/self-hosting.mdx b/src/content/docs/self-hosting.mdx index b256a5a..fae2730 100644 --- a/src/content/docs/self-hosting.mdx +++ b/src/content/docs/self-hosting.mdx @@ -35,7 +35,8 @@ You need to create your own GitHub App to replace the hosted BugDrop app. The ap | **Webhook** | Not required (optional) | | **Installation scope** | Your repositories | -You will need the App ID, private key, and installation ID to configure the Worker. +You will need the App ID and private key to configure the Worker. BugDrop discovers the +installation for each repository at request time. ## Architecture Overview @@ -60,7 +61,7 @@ The Cloudflare Worker acts as a secure proxy between the user's browser and the ## Setup Instructions -Full self-hosting instructions, including environment variables, Wrangler configuration, and deployment steps, are available in the repository: +Full self-hosting instructions, including environment variables, ignored local configuration, and deployment steps, are available in the repository: **[SELF_HOSTING.md on GitHub](https://github.com/mean-weasel/bugdrop/blob/main/SELF_HOSTING.md)** @@ -68,7 +69,7 @@ The guide covers: 1. **Forking the repository** -- Get your own copy of the BugDrop codebase 2. **Creating a GitHub App** -- Step-by-step instructions for creating and configuring the app -3. **Configuring Wrangler** -- Setting up `wrangler.toml` with your account details +3. **Configuring local overrides** -- Using ignored `.dev.vars` and `public/test/local-config.js` files so your fork stays clean 4. **Setting secrets** -- Storing your GitHub App credentials as Worker secrets 5. **Deploying** -- Publishing the Worker to your Cloudflare account 6. **Testing** -- Verifying the deployment works end-to-end diff --git a/src/content/docs/styling.mdx b/src/content/docs/styling.mdx index a51b2b6..a5f7895 100644 --- a/src/content/docs/styling.mdx +++ b/src/content/docs/styling.mdx @@ -16,6 +16,8 @@ All styling is done through data attributes on the script tag. No CSS files to i | `data-border-color` | Theme-dependent | Border color of the form container | | `data-shadow` | `"0 8px 30px rgba(0,0,0,0.12)"` | Box shadow of the form container | +These same visual settings are also used by Select Element screenshots. When a user selects an element, BugDrop draws a border-only rounded highlight around that element using `data-color`, `data-radius`, and `data-border-width`. The captured highlight includes a small 2px inner gap so the border does not sit directly on top of the selected element. The GitHub issue caption mentions the exact resolved highlight color. + ### Defaults by Theme The `data-theme` attribute sets sensible defaults for colors, which you can then override individually: From 800f7f4783c238f74fe0befb286b50e3d7c3c290 Mon Sep 17 00:00:00 2001 From: neonwatty Date: Tue, 19 May 2026 08:07:40 -0700 Subject: [PATCH 2/3] Fix docs security link --- src/content/docs/installation.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/installation.mdx b/src/content/docs/installation.mdx index 57f49a4..afc4f0f 100644 --- a/src/content/docs/installation.mdx +++ b/src/content/docs/installation.mdx @@ -85,7 +85,7 @@ BugDrop to visually cover in supported screenshot modes, mark those elements wit BugDrop covers each marked element with an opaque rectangle on supported captured screenshots. Password inputs and credit-card autocomplete fields are masked automatically. See -[Screenshot masking](/security#screenshot-masking) on the Security page for details. +[Screenshot masking](/docs/security#screenshot-masking) on the Security page for details. ## Step 3: You Are Done From 5ceef119529e29f172baa70a2f2046cec2f19f9e Mon Sep 17 00:00:00 2001 From: neonwatty Date: Tue, 19 May 2026 08:35:47 -0700 Subject: [PATCH 3/3] Fix docs sync CSP and dismiss duration guidance --- src/components/sandbox/widget-sandbox.tsx | 11 ++++++++++- src/content/docs/faq.mdx | 7 ++++--- src/content/docs/installation.mdx | 6 ++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/sandbox/widget-sandbox.tsx b/src/components/sandbox/widget-sandbox.tsx index 378b019..cbb1137 100644 --- a/src/components/sandbox/widget-sandbox.tsx +++ b/src/components/sandbox/widget-sandbox.tsx @@ -64,7 +64,7 @@ const initialConfig: SandboxConfig = { showEmail: false, requireEmail: false, buttonDismissible: false, - dismissDuration: "session", + dismissDuration: "7", showRestore: true, showButton: true, screenshotScale: "2", @@ -866,6 +866,15 @@ export function WidgetSandbox() { checked={config.buttonDismissible} onChange={(value) => update("buttonDismissible", value)} /> + {config.buttonDismissible ? ( + + update("dismissDuration", value)} + placeholder="Leave blank for forever" + /> + + ) : null}