-
Notifications
You must be signed in to change notification settings - Fork 1
feat(docs): add "Report a Bug" feedback widget to documentation site #145
Description
Summary
Add a persistent, user-friendly "Report a Bug" widget to the VitePress documentation site that lets users quickly report issues without leaving the page and without needing a GitHub account. Uses a Cloudflare Pages Function as backend proxy to create issues via GitHub App.
UX Goals
- Zero friction: User never leaves the docs site
- No GitHub account required: Issues created by bot on user's behalf
- Minimal fields: One required field ("What happened?"), everything else optional/auto-filled
- Instant feedback: "Thanks! We'll look into it" — no redirects, no loading pages
- Always accessible: Side tab visible on every page
Architecture
User (docs site) ---> Cloudflare Pages Function (/api/report-bug)
|
| (GitHub App installation token)
v
GitHub API (POST /repos/.../issues)
|
v
Issue created by "bug-reporter[bot]"
Why This Architecture?
| Approach | Pros | Cons |
|---|---|---|
| Simple | Requires GitHub account, leaves site, form is complex | |
| Simple | Token tied to person, security risk if leaked | |
| GitHub App | Secure, bot identity, no user auth needed | Slightly more setup |
| User identity | Over-engineered for bug reports, requires GitHub account |
Part 1: GitHub App Setup
Create a GitHub App named gitlab-mcp-bug-reporter (or similar):
Permissions:
- Repository > Issues: Read & Write
- Nothing else needed
Installation:
- Install on
structured-world/gitlab-mcprepository only
Credentials stored in Cloudflare:
GITHUB_APP_ID— App IDGITHUB_APP_PRIVATE_KEY— PEM private key (for JWT signing)GITHUB_APP_INSTALLATION_ID— Installation ID for the repo
Reference: Authenticating as a GitHub App installation
Part 2: Cloudflare Pages Function
File: docs/functions/api/report-bug.ts
Cloudflare Pages automatically deploys functions/ directory as serverless endpoints.
Reference: Pages Functions
// POST /api/report-bug
interface BugReport {
page: string; // Auto-filled from current docs path
description: string; // Required: "What happened?"
expected?: string; // Optional: "What did you expect?"
category?: string; // Optional: dropdown selection
honeypot?: string; // Anti-spam: must be empty
}
export const onRequestPost: PagesFunction<Env> = async (context) => {
// 1. Rate limit check (Cloudflare KV or simple in-memory)
// 2. Validate input (honeypot empty, description min 10 chars)
// 3. Generate GitHub App JWT
// 4. Exchange JWT for installation access token
// 5. Create issue via GitHub API
// 6. Return success/error to client
};Issue format created by bot:
## Bug Report from Documentation
**Page:** /guide/quick-start
**Category:** Tool not working as described
### What happened?
The example code on the page didn't work...
### What did you expect? (optional)
I expected the command to return a list of projects...
---
*Reported via docs feedback widget*Anti-spam measures:
- Honeypot field (hidden, must be empty)
- Rate limit: max 5 reports per IP per hour (Cloudflare KV)
- Minimum description length: 10 characters
- Cloudflare Turnstile (invisible CAPTCHA) — optional, add if spam becomes a problem
Part 3: VitePress Side Tab Widget
File: docs/.vitepress/theme/components/BugReportWidget.vue
Visual States
State 1: Collapsed (default)
+------------------------------------------+
| docs content... |
| [Bug?]| <- small side tab
| more content... |
+------------------------------------------+
State 2: Expanded (on click)
+------------------------------------------+
| docs content... +-------------+|
| | Found a bug?||
| | ||
| | What ||
| | happened? ||
| | [________] ||
| | ||
| | [Send] ||
| +-------------+|
+------------------------------------------+
State 3: Success
+-------------+
| Thanks! |
| We'll look |
| into it. |
+-------------+
Inline Form Fields
| Field | Type | Required | Pre-filled |
|---|---|---|---|
| Page | hidden | — | Auto: current path via useRoute() |
| What happened? | textarea | Yes | — |
| What did you expect? | textarea | No | — |
| Category | select | No | Options: Wrong docs, Tool broken, Missing info, Setup issue, Other |
| honeypot | hidden input | — | Must be empty (anti-bot) |
Responsive Behavior
- Desktop: Side tab (right edge, ~40% from top), expands into slide-out panel
- Tablet: Same but slightly smaller
- Mobile: Bottom-right floating circle (bug icon), expands into bottom sheet / modal
Animation
- Expand/collapse: CSS transition (200ms ease)
- Success: checkmark animation, auto-collapse after 3s
- Respects
prefers-reduced-motion
Part 4: GitHub Issue Template (Fallback)
For users who prefer GitHub directly, also create .github/ISSUE_TEMPLATE/bug.yml:
name: Bug Report
description: Something not working? Let us know!
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for reporting! Just tell us what happened — no formal structure needed.
- type: input
id: page
attributes:
label: Which page?
placeholder: e.g., /guide/quick-start
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened?
placeholder: Describe what went wrong or was confusing...
validations:
required: true
- type: textarea
id: expected
attributes:
label: What did you expect? (optional)
validations:
required: false
- type: dropdown
id: category
attributes:
label: Category (optional)
options:
- Documentation is wrong/outdated
- Tool not working as described
- Missing information
- Installation/setup issue
- Other
validations:
required: falseImplementation Checklist
GitHub App
- Create GitHub App
gitlab-mcp-bug-reporterwith Issues:Write permission - Install on
structured-world/gitlab-mcp - Store credentials in Cloudflare Pages env vars
Backend (Cloudflare Pages Function)
- Create
docs/functions/api/report-bug.ts - Implement JWT signing for GitHub App auth
- Implement installation token exchange
- Implement issue creation via GitHub REST API
- Add honeypot validation
- Add rate limiting (Cloudflare KV or IP-based)
- Add CORS headers for docs domain only
- Error handling: return user-friendly messages
Frontend (VitePress Widget)
- Create
docs/.vitepress/theme/index.ts— extend default theme - Create
docs/.vitepress/theme/components/BugReportWidget.vue - Implement collapsed state (side tab)
- Implement expanded state (inline form)
- Implement success/error states
- Add responsive styles (desktop: side panel, mobile: bottom sheet)
- Add dark mode support
- Add accessibility (aria-label, keyboard nav, focus trap in expanded)
- Add
prefers-reduced-motionsupport
Fallback
- Create
.github/ISSUE_TEMPLATE/bug.yml - Add "Or report on GitHub" link in expanded widget footer
Cloudflare Pages Environment Variables
Set via Cloudflare Dashboard > Pages > Settings > Environment Variables:
| Variable | Description |
|---|---|
GITHUB_APP_ID |
GitHub App numeric ID |
GITHUB_APP_PRIVATE_KEY |
PEM private key (base64 encoded) |
GITHUB_APP_INSTALLATION_ID |
Installation ID for the repo |
RATE_LIMIT_KV |
KV namespace binding (optional, for rate limiting) |
Security Considerations
- Private key NEVER exposed to client (only in Pages Function)
- CORS restricted to docs domain only
- Rate limiting prevents abuse
- Honeypot catches simple bots
- No user data collected beyond bug description
- Issues created by bot — no user identity leaked
- Input sanitized before creating issue (prevent markdown injection in title)
Dependencies
- Cloudflare Pages (already used for docs hosting)
- GitHub App (free for public repos)
- No external services / no third-party widgets
- No cookies / no tracking
Files to Create/Modify
| File | Action | Purpose |
|---|---|---|
.github/ISSUE_TEMPLATE/bug.yml |
Create | Fallback issue form |
docs/functions/api/report-bug.ts |
Create | Serverless backend |
docs/.vitepress/theme/index.ts |
Create | Custom theme |
docs/.vitepress/theme/components/BugReportWidget.vue |
Create | Widget component |
docs/.vitepress/config.mts |
Modify | Add any needed config |
docs/package.json |
Modify | Add jose dep for JWT signing |