@olinux68/bug-report
v0.5.0
Published
Floating π bug-report widget + optional self-hosted email backend. Drop-in, independent.
Maintainers
Readme
@olinux68/bug-report
Floating π bug-report button + modal. Zero-dependency, configurable, drop-in.
Adds a small floating button to any page. Clicking it opens a modal where users write what went wrong and optionally attach a screenshot; the report is POSTed to your endpoint. No backend is bundled β you point it at your own infra.
Features
- Zero runtime dependencies β vanilla JS, styles injected automatically.
- Two integration modes β modern
importor a plain<script>tag. - fr / en built-in labels, every label overridable.
- Themable accent/background/text colors.
- Optional image β attach a file, or capture the screen (
getDisplayMedia, desktop; the button auto-hides where unsupported).
Install
npm install @olinux68/bug-reportUsage β with a bundler
import { mountBugReport } from '@olinux68/bug-report';
mountBugReport({
endpoint: 'https://your-site.example/api/bug', // your infra
lang: 'fr',
});No CSS to import β the widget injects its own styles.
Usage β static <script>
Copy bug-report.config.example.js to bug-report.config.js, fill in your
endpoint, then include both scripts (config first):
<script>
window.BUG_REPORT_CONFIG = {
endpoint: 'https://your-site.example/api/bug',
lang: 'fr',
};
</script>
<script src="https://unpkg.com/@olinux68/bug-report"></script>The widget auto-mounts when window.BUG_REPORT_CONFIG.endpoint is present.
Options
| Option | Type | Default | Purpose |
|--------------|-------------------|------------------|---------|
| endpoint | string | β (required) | URL the report is POSTed to (your infra) |
| token | string | '' | Optional; sent as Authorization: Bearer <token> |
| lang | 'fr' | 'en' | 'fr' | Built-in label language |
| position | string | 'bottom-right' | Button anchor corner |
| theme | object | dark theme | Colors: { accent, bg, text } |
| texts | object | labels for lang| Override individual labels |
| screenshot | boolean | true | Allow attaching an image file |
| screenCapture | boolean | true | Show a "capture screen" button where getDisplayMedia is supported (desktop). Hidden on unsupported browsers (most mobiles), which keep the file attach. |
| format | 'multipart'|'json' | 'multipart' | Request body format |
| fields | object | identity | Rename wire fields: { message, page, userAgent, screenshot } |
| extraFields| object | function| β | Extra fields merged at send time (function is evaluated then) |
| pageValue | 'href'|'pathname' | 'href' | What to send as page |
| uaMaxLen | number | null | null | Truncate the user agent |
| minLength | number | 1 | Minimum message length |
| imageMaxDim| number | 1000 | JSON mode: resize longest side before base64 |
| imageQuality| number | 0.7 | JSON mode: JPEG quality |
Precedence: options passed to mountBugReport() win over
window.BUG_REPORT_CONFIG, which wins over the built-in defaults.
Adapting to an existing server
By default the widget sends multipart/form-data. To plug it into a server
that already expects a specific JSON shape β without changing that server β use
format, fields and extraFields. Example matching a server that expects
POST /api/bug-report with JSON { desc, page, ua, image, pseudo }:
mountBugReport({
endpoint: '/api/bug-report',
format: 'json',
fields: { message: 'desc', userAgent: 'ua', screenshot: 'image' },
pageValue: 'pathname',
uaMaxLen: 160,
minLength: 5,
extraFields: () => {
try { return { pseudo: JSON.parse(localStorage.getItem('chat_profile') || '{}').pseudo || '' }; }
catch { return {}; }
},
});In JSON mode an attached screenshot is resized and sent as a base64 data URL
(the value of the mapped screenshot field).
Server contract
The widget sends POST {endpoint} as multipart/form-data:
| Field | Notes |
|--------------|-------|
| message | the user's text (required) |
| page | current page URL (auto) |
| userAgent | browser UA (auto) |
| screenshot | file, only when attached |
When token is set, an Authorization: Bearer <token> header is added. Any
2xx response is treated as success. The response body is not inspected β your
server does whatever it wants.
Self-hosted email backend (independent, no external service)
The same package ships an optional backend so reports land in your inbox,
sent by your own server β no third-party service, nothing to sign up for.
It emails via the machine's local MTA (sendmail/Exim) by default, or SMTP if you
set SMTP_*.
Mount it on your existing Express app in one line:
import express from 'express';
import { attachBugReport } from '@olinux68/bug-report/server';
const app = express();
app.use(express.json());
attachBugReport(app, { to: '[email protected]' }); // POST /bug-report β emails you
app.listen(3000);Point the widget at it (JSON mode):
<script>
window.BUG_REPORT_CONFIG = { endpoint: '/bug-report', format: 'json' };
</script>
<script src="https://unpkg.com/@olinux68/bug-report"></script>attachBugReport(app, opts) / createBugReportHandler(opts) options:
| Option | Default | Purpose |
|--------|---------|---------|
| to | β (required) | recipient email |
| from | bug-report@localhost | From header |
| path | /bug-report | route (attachBugReport only) |
| max | 20 | reports per minute per IP |
| maxImageBytes | 1500000 | screenshot size cap |
| smtp | β | {host,port,secure,auth}; else local sendmail |
Email delivery: local MTA first (sendmail), SMTP fallback when smtp
(or SMTP_HOST/SMTP_PORT/SMTP_SECURE/SMTP_USER/SMTP_PASS) is set.
nodemailer is the only backend dependency; the widget stays dependency-free.
License
MIT
