npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ai-app-feedback

v0.2.1

Published

A Next.js-ready feedback widget that captures team feedback, browser context, and screenshots to share with coding agents.

Readme

ai-app-feedback

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.

Team workflow

  1. Choose Problem or Improvement and describe it in one text box.
  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.
  3. Send. The confirmation includes the report's reference ID.
  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.
  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.

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.

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.

Install

bun add ai-app-feedback

Next.js App Router

Import the stylesheet once, then wrap the app in NextFeedbackProvider.

// app/layout.tsx
import "ai-app-feedback/style.css";
import { NextFeedbackProvider } from "ai-app-feedback/next";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <NextFeedbackProvider
          appId="acme-web"
          endpoint="/api/feedback"
          metadata={{ environment: process.env.NEXT_PUBLIC_VERCEL_ENV ?? "local" }}
        >
          {children}
        </NextFeedbackProvider>
      </body>
    </html>
  );
}

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.

// components/app-feedback.tsx
"use client";

import { NextFeedbackProvider } from "ai-app-feedback/next";
import { authClient } from "@/lib/auth-client";

export function AppFeedback({ children }: { children: React.ReactNode }) {
  const { data: session } = authClient.useSession();

  return (
    <NextFeedbackProvider
      appId="vaster-agent"
      endpoint="/api/feedback"
      user={session?.user
        ? {
            id: session.user.id,
            email: session.user.email,
            name: session.user.name,
          }
        : undefined}
    >
      {children}
    </NextFeedbackProvider>
  );
}

To show an email field in a public app instead, set widgetProps={{ showEmailField: true }}.

Authenticated SES notifications

Install the AWS SES v2 client in the host app:

bun add @aws-sdk/client-sesv2

Create an authenticated endpoint. This example uses Better Auth directly; if your app wraps session lookup, call that wrapper from getUser instead.

// app/api/feedback/route.ts
import { auth } from "@/lib/auth";
import { createFeedbackRouteHandler } from "ai-app-feedback/server";

export const runtime = "nodejs";

export const POST = createFeedbackRouteHandler({
  getUser: async (request) => {
    const session = await auth.api.getSession({ headers: request.headers });
    return session?.user ?? null;
  },
});

Configure SES with the host's normal AWS credentials:

AWS_REGION=us-east-1
FEEDBACK_EMAIL_FROM="Vaster Feedback <[email protected]>"
# Optional. Defaults to [email protected].
[email protected]

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.

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.

Use onReport to persist the authenticated report or forward it to another system in addition to email:

export const POST = createFeedbackRouteHandler({
  getUser: async (request) => (await getRequestSession(request))?.user,
  onReport: async (report) => {
    await saveFeedback(report);
  },
});

Screenshot capture

By default, screenshots use the browser getDisplayMedia permission flow when a user submits with screenshots enabled. Browsers do not allow silent page screenshots from arbitrary client code.

For DOM-rendered screenshots, install html2canvas in the host app and pass a custom capture function:

import { createHtml2CanvasCapture } from "ai-app-feedback";

<NextFeedbackProvider
  endpoint="/api/feedback"
  captureScreenshot={createHtml2CanvasCapture(() => import("html2canvas"))}
>
  {children}
</NextFeedbackProvider>;

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:

<NextFeedbackProvider
  endpoint="/api/feedback"
  widgetProps={{
    allowImageUploads: true,
    maxImageAttachments: 3,
    maxImageBytes: 2_000_000,
    maxTotalImageBytes: 2_000_000,
  }}
>
  {children}
</NextFeedbackProvider>

Core API

Use the core session outside Next.js or with a custom UI.

import { createFeedbackSession } from "ai-app-feedback/core";

const session = createFeedbackSession({
  appId: "admin",
  endpoint: "/api/feedback",
});

session.recordNavigation({ url: window.location.href, title: document.title });

await session.submit({
  message: "The export button does nothing.",
  expected: "A CSV file should download.",
  severity: "high",
  category: "bug",
});

Privacy defaults

  • Input values are not recorded.
  • Click tracking stores a small element summary, not full DOM snapshots.
  • Screenshot capture is user-visible and permission-gated unless you provide a custom capture function.
  • URLs are captured in full by default; pass scrubUrl to strip query strings or tokens before they are recorded.
  • 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.
<NextFeedbackProvider
  endpoint="/api/feedback"
  scrubUrl={(url) => url.split("?")[0] ?? url}
>

developerPrompt contains end-user-controlled text fenced inside <untrusted_user_content> tags. If you hand it to a coding agent, run that agent with restricted permissions and treat the fenced content as data, not instructions.

Development

bun install
bun test
bun run build

Publishing

The Publish npm package GitHub Actions workflow runs from main, using the npm environment restricted to that branch. npm trusts Vastermortgage/ai-app-feedback, workflow publish.yml, environment npm. It uses OIDC instead of a stored npm token; local npm login is not needed.

Validate a release without publishing (the default):

gh workflow run publish.yml --ref main -f version=0.2.1 -F dry_run=true

After committing and pushing a version bump, release that exact version:

gh workflow run publish.yml --ref main -f version=0.2.1 -F dry_run=false

The workflow checks the requested version against package.json, installs the frozen lockfile, runs type checks and tests, builds the package, and validates its exports and React client boundaries before publishing. GitHub CLI access is required to trigger it, or use Run workflow in GitHub Actions. A dry run verifies the build and package contents; OIDC publishing authentication is exercised only by an actual release. This repository is private, so npm does not generate public build provenance for its releases.