Skip to content

Latest commit

 

History

History
189 lines (144 loc) · 8.62 KB

File metadata and controls

189 lines (144 loc) · 8.62 KB

This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.

Reference docs

  • This file — project rules, file layout, critical conventions, recipes, quality bar. Read first.
  • docs/PLAN.md — full implementation plan: stack, structure, features, data model, state, UX flows, route skeleton, phases, deploy. Read when you need context on why something is built a certain way, or for feature scope decisions.
  • node_modules/next/dist/docs/01-app/02-guides/upgrading/version-16.md — authoritative Next.js 16 upgrade notes. **Read before touching app/**ornext.config.ts.**
  • node_modules/ai/dist/docs/ — AI SDK 7 reference (after install). Read before writing or reviewing any AI SDK code.
  • .opencode/skills/<name>/SKILL.md — project-local skills: ai-sdk (official Vercel AI SDK reference), shadcn-base-vega (matches the installed components.json). Load via the skill tool when relevant.

Lumin — AI Agent Chat

Streaming chat UI for multiple LLM providers (Groq primary). Built on Next.js 16 App Router, React 19.2, AI SDK 7, shadcn/ui, and AI Elements.

Stack (locked)

Layer Choice
Framework Next.js 16.2.10, App Router, React 19.2.4, TypeScript 5.9
Bundler Turbopack (default — no flag)
Styling Tailwind v4, cn from @/lib/utils, CSS variables for theme
UI shadcn/ui (new-york, neutral) + AI Elements registry
AI runtime ai, @ai-sdk/react, @ai-sdk/groq
LLM Groq
State Zustand for threads/settings; useChat for messages
Persistence localStorage (Zustand persist middleware)
Markdown streamdown (AI-SDK-friendly)
Icons / Toasts @phosphor-icons/react, sonner
Package manager pnpm 11+ (via corepack)
Deploy Vercel

Project structure

app/
  layout.tsx              # Inter + JetBrains Mono fonts, ThemeProvider, <Toaster />
  page.tsx                # redirect("/chat")
  chat/
    page.tsx              # main chat UI
    layout.tsx            # sidebar + topbar shell
  api/
    chat/route.ts         # POST streamText() with Groq + tools
    tools/                # optional tool routers (Phase 3+)

components/
  ui/                     # shadcn primitives (do not edit)
  ai-elements/            # AI Elements registry output (do not edit)
  app/
    sidebar.tsx
    topbar.tsx
    model-picker.tsx
    thread-list.tsx
    empty-state.tsx
    tools-panel.tsx

lib/
  utils.ts                # cn() helper
  prompts/system.ts       # versioned system prompt
  tools/                  # tool definitions (zod + tool() from 'ai')
    get-time.ts
    calculator.ts
    web-search.ts
  store/
    chat-store.ts         # zustand: threads, activeId, settings
  hooks/
    use-persisted-chat.ts # useChat + localStorage bridge
    use-auto-title.ts

types/
  chat.ts                 # UIMessage extensions, ToolPart discriminated unions

public/                   # static assets

Critical rules

Next.js 16 (read docs in node_modules/next/dist/docs/ first)

  • Async Request APIs: params, searchParams, cookies(), headers(), draftMode() must be awaited. No sync access.
  • Route Handlers: use RouteContext<'/path/[id]'> global type for typed params. Example: export async function POST(_req: Request, ctx: RouteContext<'/chat/...'>) { const { id } = await ctx.params }.
  • No next lint — use pnpm lint (already wired to eslint).
  • No runtimeConfig — use process.env + NEXT_PUBLIC_* prefix for client vars.
  • No next/legacy/imagenext/image only. Use images.remotePatterns (not images.domains).
  • Turbopack default — do NOT add --turbopack flags.
  • middleware.ts is deprecated — use proxy.ts if needed. Not used in MVP.
  • Parallel routes require default.js — avoid unless needed.
  • React 19.2: useEffectEvent, use(), <Activity>, <ViewTransition> available.
  • Type-safe props: run pnpm dlx next typegen to generate PageProps, LayoutProps, RouteContext helpers.

AI SDK 7 / @ai-sdk/react

  • useChat() from @ai-sdk/react is the source of truth for messages + streaming.
  • Server route returns streamText({...}).toUIMessageStreamResponse().
  • Convert UI messages with convertToModelMessages(messages) server-side.
  • Use stopWhen: stepCountIs(10) for multi-step agentic loops.
  • Tool definitions: tool({ description, inputSchema: z.object({...}), execute }) from 'ai'.
  • For approval-required tools: set needsApproval: true → AI SDK emits tool-input-available part; render <Confirmation> and reply with synthetic result.
  • Reasoning content lives on the message; for qwen/qwen3-32b and deepseek-r1-distill-llama-70b, surface it via the <Reasoning> AI Element.

Environment

  • Never commit .env.local (already gitignored by Next).
  • Server-only vars (e.g. GROQ_API_KEY) accessed only in app/api/** or server components.
  • Public vars must start with NEXT_PUBLIC_.
  • Create .env.example with all keys documented; check it in.

Imports & conventions

  • Use @/ alias for everything above app/ (e.g. @/lib/utils, @/components/ui/button).
  • shadcn components in components/ui/ and AI Elements in components/ai-elements/do not hand-edit. Re-run the add command to update.
  • One component per file. Co-locate small helpers; promote to lib/ when reused.
  • Tailwind v4: classes only. CSS variables via cn + tailwind-merge. No inline style for theme tokens.
  • TypeScript: no any. Discriminated unions for tool parts (type field). Zod for all tool inputs and env parsing.

State & persistence

  • useChat owns messages for the active thread.
  • Zustand useChatStore owns threads[], activeId, settings; persisted under key lumin:v1.
  • On thread switch: seed useChat with setMessages(thread.messages).
  • On first user message: generate a title (first 40 chars or via useAutoTitle).
  • Cap messages per thread at ~200 to bound context.

Commands

pnpm dev              # next dev (Turbopack)
pnpm build            # next build (Turbopack)
pnpm start            # next start
pnpm lint             # eslint
pnpm typecheck        # tsc --noEmit  (add this script)

Common tasks (recipes)

Add a shadcn primitive

pnpm dlx shadcn@latest add <name>

Add an AI Element

pnpm dlx ai-elements@latest add <name>

Add a new tool

  1. Create lib/tools/<name>.ts exporting tool({ description, inputSchema, execute }).
  2. Register in app/api/chat/route.ts under the tools object.
  3. Add a UI handler in components/app/tools-panel.tsx (status: running/done/error).
  4. Update types/chat.ts if the tool needs a new ToolPart variant.

Add a Groq model

  1. Add the id to the grouped list in components/app/model-picker.tsx.
  2. (If reasoning) wrap usage in <Reasoning> and ensure the model id matches one Groq supports.

Promote a stop/regenerate flow

  • const { stop, regenerate } = useChat() — already exposed by @ai-sdk/react.

Quality bar

  • No any; no // @ts-ignore.
  • Every tool input → typed ToolPart consumed by <Tool> via discriminated union.
  • All async route handlers await params and await req.json().
  • Stream errors surface as sonner toasts with a retry button.
  • Mobile-first: sidebar becomes a Sheet below md:; prompt input respects safe-area inset.
  • Focus rings visible (AI Elements inherit Radix focus states — verify after theming).

Out of scope (do not add unless asked)

  • Database (everything is localStorage for MVP).
  • Auth.
  • Server-side file/session storage.
  • next.config.ts webpack customizations (Turbopack-first).

When in doubt

  1. Re-read node_modules/next/dist/docs/01-app/02-guides/upgrading/version-16.md.
  2. Re-read the relevant AI SDK 7 doc for the API you're touching.
  3. If a shadcn or AI Element component doesn't render, check the registry install ran cleanly and components.json aliases match.