goodcraft-jot
v0.0.0
Published
A drop-in feedback widget for web applications. Collects bug reports and feature requests with automatic screenshot capture, system info, console error collection, and submits them as Linear issues.
Readme
@goodcraft/feedback-widget
A drop-in feedback widget for web applications. Collects bug reports and feature requests with automatic screenshot capture, system info, console error collection, and submits them as Linear issues.
Architecture
┌─────────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
│ feedback-widget- │ │ feedback-widget- │ │ feedback-widget- │
│ core │────▶│ react │ │ nextjs │
│ Modal UI, capture, │ │ React components, │ │ API handler, │
│ system info, errors │ │ hooks, context │ │ Linear, Bunny CDN │
└─────────────────────┘ └──────────────────────┘ └─────────────────────┘
Client Client ServerCore renders a framework-agnostic modal with screenshot capture, error collection, and system info gathering. React wraps core in React components and context. Next.js provides the server-side API route handler that validates submissions, uploads screenshots to Bunny CDN, and creates Linear issues.
Quick Start
1. Install
# React / Next.js
pnpm add @goodcraft/feedback-widget-react @goodcraft/feedback-widget-nextjs2. Add the widget to your layout
// app/layout.tsx
import { FeedbackWidget } from '@goodcraft/feedback-widget-react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<FeedbackWidget
endpoint="/api/feedback"
projectSlug="my-app"
/>
</body>
</html>
);
}3. Create an API route
// app/api/feedback/route.ts
import { NextResponse } from 'next/server';
import {
createFeedbackHandler,
validateSubmission,
type FeedbackSubmission,
} from '@goodcraft/feedback-widget-nextjs';
const handleFeedback = createFeedbackHandler({
linear: {
apiKey: process.env.LINEAR_API_KEY!,
teamId: process.env.LINEAR_TEAM_ID!,
},
});
export async function POST(request: Request) {
const body = await request.json();
if (!validateSubmission(body)) {
return NextResponse.json({ success: false, message: 'Invalid submission' }, { status: 400 });
}
const result = await handleFeedback(body as FeedbackSubmission);
return NextResponse.json(result, { status: result.success ? 200 : 500 });
}Packages
| Package | Description |
|---------|-------------|
| @goodcraft/feedback-widget-core | Framework-agnostic widget: modal UI, screenshot capture, error collection, system info |
| @goodcraft/feedback-widget-react | React components (<FeedbackWidget>, <FeedbackWidgetProvider>, <FeedbackTrigger>), context, and hooks |
| @goodcraft/feedback-widget-nextjs | Server-side handler for Next.js API routes: Linear issue creation, Bunny CDN screenshot upload, validation |
Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| LINEAR_API_KEY | Yes | Linear API key for issue creation |
| LINEAR_TEAM_ID | Yes | Linear team ID to create issues in |
| LINEAR_BUG_LABEL_ID | No | Label ID to apply to bug reports |
| LINEAR_FEATURE_LABEL_ID | No | Label ID to apply to feature requests |
| LINEAR_WIDGET_LABEL_ID | No | Label ID to apply to all widget submissions |
| LINEAR_PROJECT_ID | No | Default Linear project ID |
| BUNNY_API_KEY | No | Bunny.net storage API key (enables screenshot upload) |
| BUNNY_STORAGE_ZONE | No | Bunny.net storage zone name |
| BUNNY_CDN_HOSTNAME | No | Bunny.net CDN hostname for public URLs |
Development
pnpm install
pnpm dev # Watch all packages
pnpm build # Build all packages
pnpm typecheck # Type-check all packagesRunning the example app
pnpm --filter nextjs-example devLicense
MIT
