ai-app-feedback 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/PRODUCT.md CHANGED
@@ -6,11 +6,11 @@ product
6
6
 
7
7
  ## Users
8
8
 
9
- Product teams and application users. Users are already inside a web app when they notice a bug, confusing workflow, broken visual state, or missing behavior. Developers and coding agents receive the report later and need enough context to reproduce and triage the issue quickly.
9
+ Teams using internal applications. Teammates are already inside a web app when they notice a bug, confusing workflow, broken visual state, or missing behavior. Developers and coding agents receive the report later and need enough context to understand the feedback and decide what to do.
10
10
 
11
11
  ## Product Purpose
12
12
 
13
- `ai-app-feedback` gives Next.js apps an embeddable feedback assistant. It helps users describe what happened, captures recent navigation and browser context, optionally attaches a screenshot, and formats a developer-ready report that can be handed to an issue tracker or coding AI agent.
13
+ `ai-app-feedback` gives Next.js apps a small feedback widget. Teammates choose Problem or Improvement and describe the request. Expected results and work impact are optional. The widget captures recent navigation and browser context, supports screenshots and image uploads, and emails a readable report with attached context for a coding agent. Replies to the notification reach the reporter, keeping follow-up in the team's existing email workflow.
14
14
 
15
15
  ## Brand Personality
16
16
 
@@ -23,10 +23,13 @@ Avoid marketing-style widgets, decorative motion, chat bubbles that pretend to b
23
23
  ## Design Principles
24
24
 
25
25
  - Capture context silently, ask users only for judgment and details.
26
+ - Use the host application's authenticated session instead of asking signed-in users to identify themselves again.
26
27
  - Make privacy boundaries explicit, especially around screenshots and interaction history.
27
28
  - Prefer developer-ready structured reports over free-form message blobs.
28
29
  - Keep the widget small enough to live in production apps without becoming part of the app's visual identity.
29
30
  - Make framework integration thin, so core reporting remains portable and testable.
31
+ - Keep one required text field. Do not require teammates to classify engineering categories, write acceptance criteria, or learn a separate tracking system.
32
+ - Supply the original feedback and captured evidence. The receiving agent decides what to do; the report does not prescribe an implementation, workflow, or reply.
30
33
 
31
34
  ## Accessibility & Inclusion
32
35
 
package/README.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # ai-app-feedback
2
2
 
3
- A Next.js-ready feedback widget that helps users send useful reports and gives developers enough context to reproduce issues. It captures recent navigation, selected interaction events, browser context, optional screenshots, and a prompt formatted for coding AI triage.
3
+ A Next.js-ready feedback widget for teams using internal applications. Teammates describe a problem or improvement; the widget captures context and emails a report that can be shared with a coding agent.
4
+
5
+ ## Team workflow
6
+
7
+ 1. Choose **Problem** or **Improvement** and describe it in one text box.
8
+ 2. Optionally expand **Add details** to describe the expected result (a concrete example helps with calculations) and the impact on your work. Attach screenshots or images as needed.
9
+ 3. Send. The confirmation includes the report's reference ID.
10
+ 4. The recipient gets an email whose subject describes the request. Share the attached `*-context.md`, `*-trace.json`, and images with the receiving agent as context.
11
+ 5. The receiving agent decides what to do using that context and its own instructions. Reply to the email to follow up with the reporter.
12
+
13
+ Only the description is required. Email remains the handoff and follow-up channel; this package does not maintain a task inbox or automatically run an agent.
14
+
15
+ Improvements use `category: "feature"`. Existing categories and severity values remain supported by the core API. The stock widget presents two choices and labels severity as work impact. `defaultCategory` still applies to problem reports; set it to `"feature"` to start with an improvement. Draft text survives closing and reopening the widget while it remains mounted, but is not persisted across reloads.
4
16
 
5
17
  ## Install
6
18
 
@@ -34,19 +46,85 @@ export default function RootLayout({ children }: { children: React.ReactNode })
34
46
  }
35
47
  ```
36
48
 
37
- Create an endpoint that stores the report, forwards it to an issue tracker, or hands `developerPrompt` to your coding AI agent.
49
+ The stock widget does not ask for an email. Pass the current Better Auth user to the provider so the client report is useful immediately; the endpoint below still resolves the session again and treats the server-side identity as authoritative.
50
+
51
+ ```tsx
52
+ // components/app-feedback.tsx
53
+ "use client";
54
+
55
+ import { NextFeedbackProvider } from "ai-app-feedback/next";
56
+ import { authClient } from "@/lib/auth-client";
57
+
58
+ export function AppFeedback({ children }: { children: React.ReactNode }) {
59
+ const { data: session } = authClient.useSession();
60
+
61
+ return (
62
+ <NextFeedbackProvider
63
+ appId="vaster-agent"
64
+ endpoint="/api/feedback"
65
+ user={session?.user
66
+ ? {
67
+ id: session.user.id,
68
+ email: session.user.email,
69
+ name: session.user.name,
70
+ }
71
+ : undefined}
72
+ >
73
+ {children}
74
+ </NextFeedbackProvider>
75
+ );
76
+ }
77
+ ```
78
+
79
+ To show an email field in a public app instead, set `widgetProps={{ showEmailField: true }}`.
80
+
81
+ ## Authenticated SES notifications
82
+
83
+ Install the AWS SES v2 client in the host app:
84
+
85
+ ```bash
86
+ bun add @aws-sdk/client-sesv2
87
+ ```
88
+
89
+ Create an authenticated endpoint. This example uses Better Auth directly; if your app wraps session lookup, call that wrapper from `getUser` instead.
38
90
 
39
91
  ```ts
40
92
  // app/api/feedback/route.ts
41
- import type { FeedbackReport } from "ai-app-feedback";
93
+ import { auth } from "@/lib/auth";
94
+ import { createFeedbackRouteHandler } from "ai-app-feedback/server";
95
+
96
+ export const runtime = "nodejs";
42
97
 
43
- export async function POST(request: Request) {
44
- const report = (await request.json()) as FeedbackReport;
98
+ export const POST = createFeedbackRouteHandler({
99
+ getUser: async (request) => {
100
+ const session = await auth.api.getSession({ headers: request.headers });
101
+ return session?.user ?? null;
102
+ },
103
+ });
104
+ ```
45
105
 
46
- console.log(report.developerPrompt);
106
+ Configure SES with the host's normal AWS credentials:
47
107
 
48
- return Response.json({ ok: true });
49
- }
108
+ ```dotenv
109
+ AWS_REGION=us-east-1
110
+ FEEDBACK_EMAIL_FROM="Vaster Feedback <feedback@vaster.com>"
111
+ # Optional. Defaults to dzuloaga@vaster.com.
112
+ FEEDBACK_EMAIL_TO=dzuloaga@vaster.com
113
+ ```
114
+
115
+ The route bounds the actual request body even when `Content-Length` is absent or inaccurate, validates nested report fields, rejects signed-out requests, overwrites client-supplied identity with the Better Auth session user, rebuilds the readable context, and sends the report through SES. The email body starts with the request and expected result. A Markdown context report includes the original feedback, browser context, metadata, and recent activity; the JSON attachment retains the structured trace. The captured screenshot and user-uploaded images are attached as viewable image files. Pass the image files alongside the context report: its text does not embed the image contents. Missing SES configuration or a delivery failure returns a non-success response, so the widget never says “Feedback sent” when no notification was delivered.
116
+
117
+ Email subjects now use the request text rather than the route and severity. Update any mailbox rules that depend on the old subject format. Custom integrations that render their own email must adopt the updated formatter or attachment builder to get this handoff.
118
+
119
+ Use `onReport` to persist the authenticated report or forward it to another system in addition to email:
120
+
121
+ ```ts
122
+ export const POST = createFeedbackRouteHandler({
123
+ getUser: async (request) => (await getRequestSession(request))?.user,
124
+ onReport: async (report) => {
125
+ await saveFeedback(report);
126
+ },
127
+ });
50
128
  ```
51
129
 
52
130
  ## Screenshot capture
@@ -66,6 +144,22 @@ import { createHtml2CanvasCapture } from "ai-app-feedback";
66
144
  </NextFeedbackProvider>;
67
145
  ```
68
146
 
147
+ The widget also accepts up to three PNG, JPEG, WebP, or GIF uploads by default, with a 2 MB limit per image and across all uploads. Configure this through `widgetProps`:
148
+
149
+ ```tsx
150
+ <NextFeedbackProvider
151
+ endpoint="/api/feedback"
152
+ widgetProps={{
153
+ allowImageUploads: true,
154
+ maxImageAttachments: 3,
155
+ maxImageBytes: 2_000_000,
156
+ maxTotalImageBytes: 2_000_000,
157
+ }}
158
+ >
159
+ {children}
160
+ </NextFeedbackProvider>
161
+ ```
162
+
69
163
  ## Core API
70
164
 
71
165
  Use the core session outside Next.js or with a custom UI.
@@ -94,7 +188,7 @@ await session.submit({
94
188
  - Click tracking stores a small element summary, not full DOM snapshots.
95
189
  - Screenshot capture is user-visible and permission-gated unless you provide a custom capture function.
96
190
  - URLs are captured in full by default; pass `scrubUrl` to strip query strings or tokens before they are recorded.
97
- - Reports include `developerPrompt`, a concise repro bundle intended for issue triage and coding agents.
191
+ - Reports include `developerPrompt`, readable context with the original feedback and captured evidence. The existing `developerPrompt` field and `formatFeedbackForAgent` function names are retained for compatibility; their output contains no implementation plan, agent workflow, or suggested reply. User-controlled report fields are kept inside the untrusted-content block.
98
192
 
99
193
  ```tsx
100
194
  <NextFeedbackProvider
@@ -112,3 +206,31 @@ bun install
112
206
  bun test
113
207
  bun run build
114
208
  ```
209
+
210
+
211
+ ## Publishing
212
+
213
+ The **Publish npm package** GitHub Actions workflow runs from `main`, using the
214
+ `npm` environment restricted to that branch. npm trusts
215
+ `Vastermortgage/ai-app-feedback`, workflow `publish.yml`, environment `npm`.
216
+ It uses OIDC instead of a stored npm token; local npm login is not needed.
217
+
218
+ Validate a release without publishing (the default):
219
+
220
+ ```bash
221
+ gh workflow run publish.yml --ref main -f version=0.2.1 -F dry_run=true
222
+ ```
223
+
224
+ After committing and pushing a version bump, release that exact version:
225
+
226
+ ```bash
227
+ gh workflow run publish.yml --ref main -f version=0.2.1 -F dry_run=false
228
+ ```
229
+
230
+ The workflow checks the requested version against `package.json`, installs the
231
+ frozen lockfile, runs type checks and tests, builds the package, and validates
232
+ its exports and React client boundaries before publishing. GitHub CLI access
233
+ is required to trigger it, or use **Run workflow** in GitHub Actions.
234
+ A dry run verifies the build and package contents; OIDC publishing authentication
235
+ is exercised only by an actual release. This repository is private, so npm does
236
+ not generate public build provenance for its releases.
@@ -0,0 +1,7 @@
1
+ import type { FeedbackImageAttachment } from "./types";
2
+ export declare const DEFAULT_IMAGE_MEDIA_TYPES: readonly ["image/jpeg", "image/png", "image/webp", "image/gif"];
3
+ export interface ReadFeedbackImageOptions {
4
+ maxBytes?: number;
5
+ acceptedMediaTypes?: readonly string[];
6
+ }
7
+ export declare function readFeedbackImage(file: File, options?: ReadFeedbackImageOptions): Promise<FeedbackImageAttachment>;
@@ -3,7 +3,7 @@ export type JsonValue = JsonPrimitive | JsonValue[] | {
3
3
  [key: string]: JsonValue;
4
4
  };
5
5
  export type FeedbackSeverity = "low" | "medium" | "high" | "blocking";
6
- export type FeedbackCategory = "bug" | "ux" | "content" | "performance" | "accessibility" | "other";
6
+ export type FeedbackCategory = "bug" | "feature" | "ux" | "content" | "performance" | "accessibility" | "other";
7
7
  export interface FeedbackUser {
8
8
  id?: string;
9
9
  email?: string;
@@ -66,6 +66,15 @@ export interface FeedbackScreenshot {
66
66
  capturedAt: string;
67
67
  source: "display-media" | "html2canvas" | "custom" | string;
68
68
  }
69
+ export interface FeedbackImageAttachment {
70
+ id: string;
71
+ fileName: string;
72
+ mediaType: string;
73
+ size: number;
74
+ dataUrl: string;
75
+ attachedAt: string;
76
+ source: "upload" | string;
77
+ }
69
78
  export interface FeedbackDraft {
70
79
  message: string;
71
80
  expected?: string;
@@ -73,6 +82,7 @@ export interface FeedbackDraft {
73
82
  category: FeedbackCategory;
74
83
  userEmail?: string;
75
84
  includeScreenshot?: boolean;
85
+ attachments?: FeedbackImageAttachment[];
76
86
  tags?: string[];
77
87
  metadata?: Record<string, JsonValue>;
78
88
  }
@@ -95,7 +105,9 @@ export interface FeedbackReport {
95
105
  navigation: NavigationEvent[];
96
106
  recentActivity: FeedbackTimelineEvent[];
97
107
  screenshot?: FeedbackScreenshot;
108
+ attachments?: FeedbackImageAttachment[];
98
109
  metadata: Record<string, JsonValue>;
110
+ /** Readable feedback context, without agent task instructions. Legacy field name. */
99
111
  developerPrompt: string;
100
112
  }
101
113
  export interface ScreenshotCaptureContext {
@@ -1,9 +1,13 @@
1
1
  import { formatFeedbackForAgent as formatFeedbackForAgentImpl } from "./core/agent-prompt";
2
+ import { readFeedbackImage as readFeedbackImageImpl } from "./core/attachments";
2
3
  import { captureVisibleTabWithDisplayMedia as captureVisibleTabWithDisplayMediaImpl, createHtml2CanvasCapture as createHtml2CanvasCaptureImpl } from "./core/screenshot";
3
4
  import { createFeedbackSession as createFeedbackSessionImpl } from "./core/session";
4
5
  export declare const captureVisibleTabWithDisplayMedia: typeof captureVisibleTabWithDisplayMediaImpl;
5
6
  export declare const createFeedbackSession: typeof createFeedbackSessionImpl;
6
7
  export declare const createHtml2CanvasCapture: typeof createHtml2CanvasCaptureImpl;
7
8
  export declare const formatFeedbackForAgent: typeof formatFeedbackForAgentImpl;
9
+ export declare const DEFAULT_IMAGE_MEDIA_TYPES: readonly ["image/jpeg", "image/png", "image/webp", "image/gif"];
10
+ export declare const readFeedbackImage: typeof readFeedbackImageImpl;
8
11
  export type { FeedbackSession } from "./core/session";
9
- export type { BrowserContext, ElementSummary, FeedbackCategory, FeedbackDraft, FeedbackReport, FeedbackScreenshot, FeedbackSessionOptions, FeedbackSeverity, FeedbackSubmitHandler, FeedbackTimelineEvent, FeedbackUser, JsonValue, NavigationEvent, RuntimeErrorEvent, ScreenshotCapture, ScreenshotCaptureContext, } from "./core/types";
12
+ export type { BrowserContext, ElementSummary, FeedbackCategory, FeedbackDraft, FeedbackImageAttachment, FeedbackReport, FeedbackScreenshot, FeedbackSessionOptions, FeedbackSeverity, FeedbackSubmitHandler, FeedbackTimelineEvent, FeedbackUser, JsonValue, NavigationEvent, RuntimeErrorEvent, ScreenshotCapture, ScreenshotCaptureContext, } from "./core/types";
13
+ export type { ReadFeedbackImageOptions } from "./core/attachments";