nit-app
v0.0.3
Published
Nit - Visual, Figma-style commenting for live prototypes with state-aware anchoring
Maintainers
Readme
Nit
The missing feedback layer for AI-generated UI
Figma-style comments for live prototypes. One script tag, click anywhere, leave feedback.
What is Nit?
More and more designers are leaning into live prototypes. Static mocks have found a new place in workflows. We now find ourselves prompting functional prototypes into existence using v0, Cursor, Lovable, and Bolt.
Nit is the bridge between your prompt and your final design. It's a lightweight, zero-config setup that lets you pin design feedback directly to the elements in your prototypes. If a Figma comment and a React component had a baby, this would be it.
Why Nit?
Because a screenshot can't click a button. Traditional design tools are great for static layouts, but when you're vibe-coding / prototyping, the "design" is the interaction, the state, and the flow. Nit brings the feedback into the browser.
Quick Start
Option 1: Ask your AI assistant (recommended)
Paste this into Cursor, Windsurf, or Copilot:
Add Nit visual feedback to my project.
1. Install: npm install nit
2. Add this before </body> in my root HTML (or use <Script> for Next.js):
<script src="https://usenit.dev/widget.js?project=GENERATE_A_SLUG"></script>
Generate a random slug like "swift-otter-42".
3. In my root component, add useNitState to capture UI state:
import { useNitState } from 'nit';
useNitState({
// key: [value, setter] for each piece of conditional UI state
});
Find every useState controlling modals, drawers, tabs, panels.
Add each as a [value, setter] pair.
4. Create a .nit file in the repo root with just the slug.The agent reads your codebase, finds your modals and tabs, and wires everything up. It takes 30 seconds.
Option 2: CLI
npx nit-app initDetects your framework, generates a project slug, injects the script tag, and creates a .nit file.
Then generate state hooks automatically:
npx nit-app hooksOption 3: Manual
Add before </body>:
<script src="https://usenit.dev/widget.js?project=my-project-42"></script>State Capture
Nit can capture and restore UI state (modals, tabs, drawers) so clicking a comment in the panel re-opens the exact view. Use the useNitState hook:
import { useNitState } from 'nit';
function App() {
const [modalOpen, setModalOpen] = useState(false);
const [activeTab, setActiveTab] = useState(0);
useNitState({
modalOpen: [modalOpen, setModalOpen],
activeTab: [activeTab, setActiveTab],
});
}One line per state slice. The hook handles NIT_CAPTURE_STATE, NIT_RESTORE_STATE, and NIT_DESCRIBE_STATE automatically.
Without state hooks, comments still work — you just can't click-to-restore hidden elements from the panel.
CLI
The CLI reads the project ID from .nit and queries the same Supabase backend as the widget.
npx nit-app init
Framework detection + script tag injection + .nit file creation.
npx nit-app hooks
Scans your codebase for useState calls that control UI visibility, generates a useNitState() call, and injects it.
npx nit-app status
swift-otter-42 — 5 open comments
/dashboard (3)
bug Button overflow on mobile Jason, 2h ago
design Spacing inconsistent in nav Wild Fox, 3h ago
question Should we add search? Swift Otter, 1d ago
/pricing (2)
blocker Prices not loading on Safari Jason, 4h ago
idea Add annual pricing toggle Wild Fox, 1d ago
3 resolved this weekFlags: --json for machine-readable output, --type bug to filter, --page /dashboard to scope.
How Sync Works
Same project slug = same comments everywhere.
localhost:5173 <--> Supabase <--> preview.vercel.app
You, building Real-time PM, reviewing
syncThe script tag is in your code, so every deploy (local, preview, staging) gets the widget. Comments sync in real-time.
Features
- Pin to DOM — Comments anchor to elements. If the element moves, the comment follows.
- Floating comments — Click empty space to leave feedback not tied to any element.
- Real-time sync — See feedback instantly across tabs and collaborators via Supabase.
- URL aware — Feedback is scoped to specific routes.
- State fingerprinting — Comments know about app state (modals, tabs, query params).
- Anonymous identity — Auto-generated names ("Swift Otter") with stable IDs. No login required.
- Ownership-based delete — Only the comment author can delete their own comments.
- Zero config — One script tag. No accounts.
Architecture
nit (npm library)
React hooks for state fingerprinting and state capture.
Exports:
useNitState()— Register UI state for capture/restoreuseStateFingerprint()— Framework-agnostic URL + state trackinguseReactRouterFingerprint()— React Router v6+ integrationuseNextFingerprint()— Next.js App Router integrationhashObject()— Deterministic DJB2 hashing utility- TypeScript types for
StateFingerprint,Comment, etc.
nit-widget (script tag)
Standalone browser widget. Built as a single IIFE bundle with Supabase baked in. Includes: toolbar, element selection overlay, comment composer, pins, side panel, popovers, settings, and debug panel.
nit-app CLI (npx nit-app)
Terminal client for project setup and comment management. Commands: init, hooks, status.
Development
Prerequisites
- Node.js 18+
- npm
Setup
npm install # Root (library + CLI)
cd widget && npm install
cd demo && npm installDev mode
npm run dev # Widget watch + copy + Vite demo serverBuilding
npm run build # Library → dist/
cd widget && npm run build # Widget → widget/dist/widget.global.jsTesting
npm test # All tests
npm run lint # TypeScript type checking
npm run check # Supabase connectivity checkWidget environment
The widget requires Supabase credentials at build time. Create widget/.env:
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_PUBLISHABLE_KEY=sb_...Project Structure
.
├── src/ # NPM library (React hooks)
│ ├── hooks/ # useNitState, useStateFingerprint, etc.
│ ├── utils/ # hashObject (DJB2 hashing)
│ ├── types.ts # Shared TypeScript types
│ └── index.ts # Public exports
├── cli/ # CLI (npx nit-app)
│ ├── index.mjs # Entry point + arg parsing
│ ├── utils.mjs # Slug generation, framework detection, Supabase client
│ └── commands/ # init, hooks, status
├── widget/ # Standalone browser widget (IIFE)
│ ├── src/
│ │ ├── components/ # Toolbar, Overlay, Composer, Pins, Panel, etc.
│ │ ├── utils/ # identity, DOM, animation, classifier
│ │ ├── storage/ # Supabase + localStorage adapters
│ │ ├── supabase.ts # Supabase facade + project ID resolution
│ │ ├── fingerprint.ts # URL + state fingerprinting
│ │ └── index.ts # NitWidget singleton
│ └── tsup.config.ts # IIFE build config
├── demo/ # React + Vite demo app
├── INSTALL_PROMPT.md # AI assistant install prompt
├── TESTING.md # Manual testing guide
└── tsconfig.json # TypeScript configLicense
MIT
Credits
Built for builders by Jason Calleiro
