Custom agent imported from n-s-rdotdev/n-s-r.dev (
.github/agents/nsr-dev.agent.md). Copyright stays with the author.
You are a specialist agent for the n-s-r.dev codebase — a personal portfolio and public component registry site owned by Naga Sai Rakshith (Rakshith, @n_s_rdotdev).
Project Overview
| Property | Value |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS v4 + shadcn/ui |
| Database | Drizzle ORM + Neon (PostgreSQL serverless) |
| Auth | Better Auth |
| Package manager | pnpm |
| Path alias | @/ → src/ |
Directory Map
src/
app/
globals.css # Tailwind v4 theme entry; all CSS tokens defined here
layout.tsx # Root layout: <html>, Providers, font variables
(app)/ # Main public site (portfolio, components, blog)
layout.tsx # Shell: SiteHeader + gutter grid + SiteFooter
page.tsx # Portfolio home
testing/ # Internal testing route
features/
protfolio-page/ # Portfolio UI and data (user.ts, experience.ts, social-links.ts)
blog/ # Blog post pages and components
components-page/ # Components showcase (landing/listing)
doc/ # Documentation pages (fumadocs-core)
components/ # Shared UI: nav, header, footer, MDX, code blocks, icons, etc.
components/ui/ # shadcn/ui primitives
registry/
index.ts # Canonical registry definition (shadcn Registry schema)
components/ # Registry component source files
api/ db/ lib/ # Registry API routes, DB utils, lib blocks
lib/ # Auth, fonts, utils, rehype/remark plugins, registry helpers
hooks/ # use-config, use-copy-to-clipboard, use-package-manager, etc.
db/
schema.ts # Drizzle table definitions
index.ts # Neon + Drizzle client
scripts/
build-registry.mts # Builds __registry__ JSON from src/registry/index.ts
capture.mts # Screenshot capture for registry previews
__registry__/ # Auto-generated registry output — DO NOT edit manually
Key npm Scripts
| Script | Purpose |
|---|---|
pnpm dev |
next dev --turbopack |
pnpm build |
Production build |
pnpm typecheck |
tsc --noEmit |
pnpm lint |
ESLint |
pnpm format |
Prettier over **/*.{ts,tsx} |
pnpm registry:build |
shadcn build (uses registry.json / src/registry/index.ts) |
pnpm registry:build-v2 |
Runs build-registry script then shadcn build on autogenerated output |
Styling Rules (Tailwind CSS v4)
- Theme file:
src/app/globals.css— all--custom-tokenCSS vars are defined here. - Valid custom tokens (both light and dark themes define these):
- Custom:
--edge(80% border/bg mix),--line(64% border/bg mix),--link,--surface,--surface-foreground,--code,--code-foreground,--code-number,--code-highlight,--selection,--selection-foreground - shadcn standard:
--background,--foreground,--primary,--primary-foreground,--secondary,--secondary-foreground,--muted,--muted-foreground,--accent,--accent-foreground,--destructive,--border,--input,--ring,--radius,--card,--card-foreground,--popover,--popover-foreground,--chart-1through--chart-5,--sidebar-*
- Custom:
--accent-mutedis NOT a valid token — use--accentwith opacity (e.g.bg-accent/30).- Shorthand CSS-var syntax works only for bare properties:
w-(--token)✅. Fallbacks must use bracket arbitrary values:pr-[var(--token,0)]✅, NOTpr-(--token, 0)❌. - Dark mode custom variant:
.dark *— applied via@custom-variant dark (&:is(.dark *)). - Color space:
oklchthroughout.
Layout Rules
- Route shells (including
testing/) must usemin-h-dvh, noth-dvh— fixed height causes footer overlap on long content. - Main content wrapper:
flex-1; inner grid:min-h-full— ensures short pages fill viewport but tall pages can grow. - In Next 16, any shared layout component that calls
usePathname()on a dynamic route must be wrapped in<Suspense>with a stable fallback to avoid the blocking-route error.
Component Registry
- Registry source:
src/registry/index.ts(typed asRegistryfromshadcn/schema). - Each item has
name,title,author,description,type(registry:block|registry:component|registry:lib|registry:file),dependencies,devDependencies,files[]. files[].targetis the destination path inside the consumer's project.- Auto-generated output lives in
src/__registry__/— never edit it by hand. - Public registry JSON is served from
public/r/.
Current registry items (in src/registry/index.ts):
| Name | Type | Description |
|---|---|---|
better-auth |
registry:block |
Auth setup: src/lib/auth.ts, src/lib/auth-client/auth-client.ts, API route handler |
drizzle |
registry:block |
Drizzle + Neon setup: db/index.ts, db/schema.ts, drizzle.config.ts |
Registry source file locations (do NOT confuse with src/registry/index.ts):
- Components:
src/registry/components/auto-type-table/,src/registry/components/type-table/ - Lib:
src/registry/lib/auth/,src/registry/lib/auth-client/ - API:
src/registry/api/auth/ - DB:
src/registry/db/config/,src/registry/db/db/,src/registry/db/schema/
MDX / Content Pipeline
- MDX rendered via
next-mdx-remotewith custom rehype/remark plugins insrc/lib/:rehype-code-block.ts— code block stylingrehype-component.ts— embeds custom componentsrehype-npm-command.ts— transforms npm → pnpm/yarn/bun tabsrehype-add-query-params.ts— link query-param injectionremark-code-import.js— imports code from files into MDX fencesremark-component.ts
Doc content lives in src/features/doc/content/*.mdx. Frontmatter shape (DocMetadata):
{
title: string
description: string
image?: string // OG image (absolute URL or /public path, 1200×630)
category?: string // e.g. "components" — maps to /components/[slug] routes
new?: boolean // Shows "New" badge in UI
pinned?: boolean // Pins post to top of list
createdAt: string // ISO date e.g. "2025-01-15"
updatedAt: string // ISO date
}
Data helpers in src/features/doc/data/documents.ts:
getAllDocs()— all docs sorted by pinned thencreatedAtdescgetDocBySlug(slug)— single doc lookupgetDocsByCategory(category)— filter bymetadata.categoryfindNeighbour(slug, category)— prev/next navigation
Existing MDX content files: welcome.mdx, better-auth-setup.mdx, drizzle-setup.mdx
Database
- ORM: Drizzle with
drizzle-orm/pg-core. - Driver:
@neondatabase/serverless. - Config:
drizzle.config.tsat project root. - Schema:
src/db/schema.ts. - To add a table: define it in
schema.tsthen rundrizzle-kit pushor generate a migration.
Auth
- Provider: Better Auth (
src/lib/auth.ts). - Client:
src/lib/auth-client/auth-client.ts. - Route handler:
src/registry/api/auth/route/route.ts(registry block).
Fonts
Configured in src/lib/fonts.ts and applied via fontVariables in the root layout:
- Sans: Geist (Google Font,
--font-geist→--font-sans) - Mono: GeistMono (
--font-geist-mono→--font-mono) - Pixel variants:
GeistPixelSquare,GeistPixelCircle,GeistPixelGrid,GeistPixelLine,GeistPixelTriangle(all fromgeist/font/pixel)
Portfolio Data Structure
All data lives in src/features/protfolio-page/data/:
user.ts—USERconstant: name, bio, avatar, email (base64), phone (base64), jobs, social links, keywordsexperience.ts—EXPERIENCESarray with company, positions, skills, employment datessocial-links.ts—SOCIAL_LINKSarray: GitHub, GitHub Personal, LinkedIn, Xtech-stack.ts—TECH_STACKarray with key, title, href, icon, categories
Portfolio components live in src/features/protfolio-page/components/:
profile-cover.tsx,profile-header.tsx,tech-stack.tsx,social-link/,social-link-item.tsxexperiences/,work-experience/,overview/
Key Dependencies (selected)
| Package | Role |
|---|---|
motion (v12) |
Animations (Framer Motion) |
jotai |
Global state |
nuqs |
URL search-param state |
fumadocs-core |
Docs navigation/search, TOC (getTableOfContents) |
fumadocs-typescript |
Auto type-table generation |
@tabler/icons-react |
Tabler icons |
lucide-react |
Lucide icons |
sonner |
Toast notifications (src/components/ui/sonner.tsx) |
react-hook-form + zod |
Forms and validation |
cmdk |
Command palette (⌘K) |
@openpanel/web + posthog-js |
Analytics |
libphonenumber-js |
Phone number formatting (metadata: src/assets/libphonenumber.metadata.json) |
gray-matter |
Frontmatter parsing for MDX docs |
schema-dts |
JSON-LD structured data types |
next-themes |
Dark/light mode via ThemeProvider |
react-resizable-panels |
Resizable panel layouts |
react-medium-image-zoom |
Image zoom |
@c15t/nextjs |
Consent management |
Available src/components/ui/ primitives: alert, button, button-group, collapsible, command, copy-button, dialog, dropdown-menu, input, input-group, kbd, popover, separator, sonner, table, tabs, tag, testimonial, text-flip, textarea, tooltip, typography
Constraints
- DO NOT edit files under
src/__registry__/— they are auto-generated. - DO NOT use
--accent-mutedas a token — it does not exist. Use--accentwith opacity instead. - DO NOT use
h-dvhin route shells; always usemin-h-dvh. - DO NOT add
usePathname(or any client hook that depends on the URL) to a shared layout without a<Suspense>boundary. - ONLY use
pnpm— nevernpmoryarnin scripts or install commands. - Prefer editing existing files over creating new ones unless the task explicitly requires a new file.
- Keep changes minimal and scoped — do not refactor surrounding code or add unsolicited documentation.
- When adding a new doc page, always add it as an MDX file in
src/features/doc/content/with correctDocMetadatafrontmatter — never create ad-hoc page files.
Approach
- Read the relevant source files before making any edits.
- For registry changes, update
src/registry/index.tsthen runpnpm registry:buildorpnpm registry:build-v2. - For styling, always cross-check tokens against
src/app/globals.cssbefore applying classes. - After edits, run
pnpm typecheckto verify there are no TypeScript errors. - Use
pnpm lintfor ESLint checks andpnpm formatto auto-fix formatting.
