fix: improve note interactions and markdown LaTeX support
## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
This commit is contained in:
853
docs/development-guide-keep-notes.md
Normal file
853
docs/development-guide-keep-notes.md
Normal file
@@ -0,0 +1,853 @@
|
||||
# Development Guide - keep-notes (Memento Web App)
|
||||
|
||||
## Overview
|
||||
|
||||
Complete development guide for the Memento web application. Covers setup, development workflow, debugging, testing, and common tasks.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Software
|
||||
|
||||
| Tool | Version | Purpose |
|
||||
|------|---------|---------|
|
||||
| **Node.js** | 20+ | JavaScript runtime |
|
||||
| **npm** | Latest | Package manager |
|
||||
| **Git** | Latest | Version control |
|
||||
|
||||
### Recommended Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| VS Code | IDE with great TypeScript/React support |
|
||||
| Prisma Studio | Database GUI for viewing/editing data |
|
||||
| Postman/Insomnia | API testing |
|
||||
| Playwright VS Code | E2E test debugging |
|
||||
|
||||
---
|
||||
|
||||
## Initial Setup
|
||||
|
||||
### 1. Clone Repository
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd Keep
|
||||
```
|
||||
|
||||
### 2. Install Dependencies
|
||||
|
||||
```bash
|
||||
cd keep-notes
|
||||
npm install
|
||||
```
|
||||
|
||||
**Expected packages:**
|
||||
- React 19.2.3
|
||||
- Next.js 16.1.1
|
||||
- Prisma 5.22.0
|
||||
- 100+ dependencies
|
||||
|
||||
### 3. Database Setup
|
||||
|
||||
```bash
|
||||
# Generate Prisma client
|
||||
npm run db:generate
|
||||
|
||||
# Run migrations (if needed)
|
||||
npx prisma migrate dev
|
||||
|
||||
# Open Prisma Studio (optional)
|
||||
npx prisma studio
|
||||
```
|
||||
|
||||
**Prisma Studio:**
|
||||
- Opens at http://localhost:5555
|
||||
- View and edit database records
|
||||
- Visual database schema
|
||||
|
||||
### 4. Environment Configuration
|
||||
|
||||
Create `.env` file in `keep-notes/`:
|
||||
|
||||
```bash
|
||||
# Database
|
||||
DATABASE_URL="file:./prisma/dev.db"
|
||||
|
||||
# NextAuth
|
||||
NEXTAUTH_SECRET="your-secret-key-here"
|
||||
NEXTAUTH_URL="http://localhost:3000"
|
||||
|
||||
# Email (optional - for password reset)
|
||||
SMTP_HOST="smtp.example.com"
|
||||
SMTP_PORT="587"
|
||||
SMTP_USER="your-email@example.com"
|
||||
SMTP_PASS="your-password"
|
||||
SMTP_FROM="noreply@memento.app"
|
||||
|
||||
# AI Providers (optional)
|
||||
OPENAI_API_KEY="sk-..."
|
||||
OLLAMA_API_URL="http://localhost:11434"
|
||||
```
|
||||
|
||||
**Generate NEXTAUTH_SECRET:**
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Start Development Server
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**Server starts at:** http://localhost:3000
|
||||
|
||||
**Features:**
|
||||
- Hot reload (Fast Refresh)
|
||||
- TypeScript checking
|
||||
- Source maps for debugging
|
||||
- API routes available
|
||||
|
||||
### File Watching
|
||||
|
||||
Next.js automatically watches for changes in:
|
||||
- `app/` directory
|
||||
- `components/` directory
|
||||
- `lib/` directory
|
||||
- `public/` directory
|
||||
|
||||
**Automatic Actions:**
|
||||
- Recompile changed files
|
||||
- Refresh browser (HMR)
|
||||
- Regenerate Prisma client if schema changes
|
||||
|
||||
---
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### 1. Create a New Component
|
||||
|
||||
```bash
|
||||
# Create component file
|
||||
touch components/my-component.tsx
|
||||
```
|
||||
|
||||
```typescript
|
||||
// components/my-component.tsx
|
||||
export default function MyComponent() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello from Memento!</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```tsx
|
||||
import MyComponent from '@/components/my-component'
|
||||
|
||||
export default function Page() {
|
||||
return <MyComponent />
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Add a New API Route
|
||||
|
||||
```bash
|
||||
# Create API route directory
|
||||
mkdir -p app/api/my-resource
|
||||
touch app/api/my-resource/route.ts
|
||||
```
|
||||
|
||||
```typescript
|
||||
// app/api/my-resource/route.ts
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ message: 'Hello' })
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const body = await request.json()
|
||||
return NextResponse.json({ received: body })
|
||||
}
|
||||
```
|
||||
|
||||
**Access:** http://localhost:3000/api/my-resource
|
||||
|
||||
### 3. Add a New Server Action
|
||||
|
||||
```bash
|
||||
# Create action file
|
||||
touch app/actions/my-action.ts
|
||||
```
|
||||
|
||||
```typescript
|
||||
'use server'
|
||||
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
export async function myAction(id: string) {
|
||||
const result = await prisma.note.update({
|
||||
where: { id },
|
||||
data: { /* updates */ }
|
||||
})
|
||||
|
||||
revalidatePath('/')
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in Component:**
|
||||
```tsx
|
||||
import { myAction } from '@/app/actions/my-action'
|
||||
|
||||
export default function MyComponent() {
|
||||
async function handleSubmit() {
|
||||
await myAction('note-id')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Modify Database Schema
|
||||
|
||||
```bash
|
||||
# 1. Edit schema
|
||||
nano prisma/schema.prisma
|
||||
|
||||
# 2. Create migration
|
||||
npx prisma migrate dev --name my_changes
|
||||
|
||||
# 3. Update client
|
||||
npm run db:generate
|
||||
```
|
||||
|
||||
**Example Schema Change:**
|
||||
```prisma
|
||||
model Note {
|
||||
// ... existing fields
|
||||
newField String? // Add new optional field
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Run Tests
|
||||
|
||||
```bash
|
||||
# Run all E2E tests
|
||||
npm test
|
||||
|
||||
# Run tests with UI
|
||||
npm run test:ui
|
||||
|
||||
# Run tests in headed mode (see browser)
|
||||
npm run test:headed
|
||||
```
|
||||
|
||||
**Test Reports:**
|
||||
- HTML: `playwright-report/index.html`
|
||||
- Results: `test-results/.last-run.json`
|
||||
|
||||
---
|
||||
|
||||
## Database Operations
|
||||
|
||||
### View Data with Prisma Studio
|
||||
|
||||
```bash
|
||||
npx prisma studio
|
||||
# Opens at http://localhost:5555
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Browse tables
|
||||
- Edit records
|
||||
- Add records
|
||||
- Filter and query
|
||||
|
||||
### Manual Database Queries
|
||||
|
||||
```bash
|
||||
# Open SQLite CLI
|
||||
sqlite3 keep-notes/prisma/dev.db
|
||||
|
||||
# Query notes
|
||||
SELECT * FROM Note LIMIT 10;
|
||||
|
||||
# Query users
|
||||
SELECT * FROM User;
|
||||
|
||||
# Exit
|
||||
.quit
|
||||
```
|
||||
|
||||
### Reset Database
|
||||
|
||||
```bash
|
||||
# Delete database file
|
||||
rm keep-notes/prisma/dev.db
|
||||
|
||||
# Re-run migrations
|
||||
npx prisma migrate dev
|
||||
|
||||
# Seed data (if you have a seed script)
|
||||
npx prisma db seed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Server-Side Debugging
|
||||
|
||||
**VS Code Launch Config:**
|
||||
|
||||
Create `.vscode/launch.json`:
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Next.js: debug server-side",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "npm run dev"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug client-side",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Console Logging
|
||||
|
||||
**Server Components:**
|
||||
```typescript
|
||||
console.log('Server log', data) // Appears in terminal
|
||||
```
|
||||
|
||||
**Client Components:**
|
||||
```typescript
|
||||
console.log('Client log', data) // Appears in browser console
|
||||
```
|
||||
|
||||
### Debug API Routes
|
||||
|
||||
Add logging to API routes:
|
||||
```typescript
|
||||
export async function GET(request: NextRequest) {
|
||||
console.log('GET /api/notes called')
|
||||
console.log('Query params:', request.nextUrl.searchParams)
|
||||
|
||||
const notes = await prisma.note.findMany()
|
||||
console.log('Found notes:', notes.length)
|
||||
|
||||
return NextResponse.json({ data: notes })
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TypeScript Configuration
|
||||
|
||||
### Type Checking
|
||||
|
||||
```bash
|
||||
# Type check all files
|
||||
npx tsc --noEmit
|
||||
|
||||
# Type check with watch mode
|
||||
npx tsc --noEmit --watch
|
||||
```
|
||||
|
||||
### Common Type Issues
|
||||
|
||||
**1. Prisma Client Types:**
|
||||
```typescript
|
||||
import prisma from '@/lib/prisma'
|
||||
|
||||
// Use Prisma types
|
||||
type Note = Prisma.NoteGetPayload<{ include: {} }>
|
||||
```
|
||||
|
||||
**2. Server Actions:**
|
||||
```typescript
|
||||
'use server'
|
||||
|
||||
// Server actions must be async
|
||||
export async function myAction() {
|
||||
// Action logic
|
||||
}
|
||||
```
|
||||
|
||||
**3. Component Props:**
|
||||
```typescript
|
||||
interface MyComponentProps {
|
||||
title: string
|
||||
count?: number // Optional
|
||||
}
|
||||
|
||||
export default function MyComponent({ title, count = 0 }: MyComponentProps) {
|
||||
return <div>{title}: {count}</div>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Styling Guide
|
||||
|
||||
### Tailwind CSS Classes
|
||||
|
||||
**Documentation:** https://tailwindcss.com/docs
|
||||
|
||||
**Common Patterns:**
|
||||
```tsx
|
||||
// Spacing
|
||||
<div className="p-4 m-2"> // Padding 4, margin 2
|
||||
<div className="gap-4"> // Gap between children
|
||||
|
||||
// Colors
|
||||
<div className="bg-blue-500 text-white">
|
||||
<div className="text-gray-700 dark:text-gray-300">
|
||||
|
||||
// Typography
|
||||
<h1 className="text-2xl font-bold">
|
||||
<p className="text-sm leading-relaxed">
|
||||
|
||||
// Layout
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
|
||||
// Responsive
|
||||
<div className="hidden md:block">
|
||||
<div className="w-full md:w-1/2">
|
||||
```
|
||||
|
||||
### Custom CSS
|
||||
|
||||
**Global Styles:** `app/globals.css`
|
||||
|
||||
**Component-Specific:**
|
||||
```tsx
|
||||
// Use Tailwind @apply or inline styles
|
||||
<div style={{ customProperty: 'value' }} />
|
||||
|
||||
// Or CSS modules
|
||||
import styles from './MyComponent.module.css'
|
||||
<div className={styles.container}>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Working with Prisma
|
||||
|
||||
### Common Queries
|
||||
|
||||
**Find all notes for a user:**
|
||||
```typescript
|
||||
const notes = await prisma.note.findMany({
|
||||
where: { userId: session.user.id },
|
||||
orderBy: { updatedAt: 'desc' }
|
||||
})
|
||||
```
|
||||
|
||||
**Create a note:**
|
||||
```typescript
|
||||
const note = await prisma.note.create({
|
||||
data: {
|
||||
title: 'My Note',
|
||||
content: 'Note content',
|
||||
color: 'blue',
|
||||
userId: session.user.id
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Update a note:**
|
||||
```typescript
|
||||
const note = await prisma.note.update({
|
||||
where: { id: noteId },
|
||||
data: {
|
||||
title: 'Updated Title',
|
||||
content: 'Updated content'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Delete a note:**
|
||||
```typescript
|
||||
await prisma.note.delete({
|
||||
where: { id: noteId }
|
||||
})
|
||||
```
|
||||
|
||||
**Search notes:**
|
||||
```typescript
|
||||
const notes = await prisma.note.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ title: { contains: query, mode: 'insensitive' } },
|
||||
{ content: { contains: query, mode: 'insensitive' } }
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Transaction Support
|
||||
|
||||
```typescript
|
||||
await prisma.$transaction(async (tx) => {
|
||||
// Multiple operations
|
||||
await tx.note.create({ data: note1 })
|
||||
await tx.note.create({ data: note2 })
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication Development
|
||||
|
||||
### NextAuth Configuration
|
||||
|
||||
**Config File:** `auth.config.ts`
|
||||
|
||||
**Add OAuth Provider:**
|
||||
```typescript
|
||||
export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
providers: [
|
||||
Google({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
}),
|
||||
// ... other providers
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
### Protected Routes
|
||||
|
||||
**Server Component:**
|
||||
```tsx
|
||||
import { auth } from '@/auth'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export default async function ProtectedPage() {
|
||||
const session = await auth()
|
||||
|
||||
if (!session) {
|
||||
redirect('/login')
|
||||
}
|
||||
|
||||
return <div>Welcome {session.user.name}</div>
|
||||
}
|
||||
```
|
||||
|
||||
**API Route:**
|
||||
```typescript
|
||||
import { auth } from '@/auth'
|
||||
|
||||
export async function GET() {
|
||||
const session = await auth()
|
||||
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
return NextResponse.json({ data: 'secret' })
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI Integration Development
|
||||
|
||||
### Add New AI Provider
|
||||
|
||||
**1. Create Provider File:**
|
||||
```bash
|
||||
touch lib/ai/providers/my-provider.ts
|
||||
```
|
||||
|
||||
**2. Implement Provider:**
|
||||
```typescript
|
||||
// lib/ai/providers/my-provider.ts
|
||||
export function createMyProvider() {
|
||||
return {
|
||||
generateText: async (prompt) => {
|
||||
// Call AI API
|
||||
return response
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. Register in Factory:**
|
||||
```typescript
|
||||
// lib/ai/factory.ts
|
||||
export function getProvider(provider: string) {
|
||||
switch (provider) {
|
||||
case 'my-provider':
|
||||
return createMyProvider()
|
||||
// ... existing providers
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use AI SDK
|
||||
|
||||
```typescript
|
||||
import { generateText } from 'ai'
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
|
||||
const response = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Generate tags for this note...',
|
||||
})
|
||||
|
||||
console.log(response.text)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### 1. Image Optimization
|
||||
|
||||
```tsx
|
||||
import Image from 'next/image'
|
||||
|
||||
<Image
|
||||
src="/image.png"
|
||||
alt="Description"
|
||||
width={500}
|
||||
height={300}
|
||||
priority // For above-fold images
|
||||
/>
|
||||
```
|
||||
|
||||
### 2. Code Splitting
|
||||
|
||||
```tsx
|
||||
// Dynamic import for heavy components
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
|
||||
loading: () => <div>Loading...</div>,
|
||||
ssr: false // Client-only
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Server Components (Default)
|
||||
|
||||
```tsx
|
||||
// Server components are default (no 'use client')
|
||||
export default async function Page() {
|
||||
const data = await fetch('https://api.example.com/data')
|
||||
return <div>{data}</div>
|
||||
}
|
||||
```
|
||||
|
||||
**Client Component:**
|
||||
```tsx
|
||||
'use client' // Required for interactivity
|
||||
|
||||
export default function InteractiveComponent() {
|
||||
const [count, setCount] = useState(0)
|
||||
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Guide
|
||||
|
||||
### Write E2E Test
|
||||
|
||||
**File:** `tests/my-feature.spec.ts`
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('my feature test', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000')
|
||||
|
||||
await page.fill('input[name="email"]', 'test@example.com')
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
await expect(page).toHaveURL('/dashboard')
|
||||
})
|
||||
```
|
||||
|
||||
### Run Specific Test
|
||||
|
||||
```bash
|
||||
npx playwright test tests/my-feature.spec.ts
|
||||
```
|
||||
|
||||
### Debug Tests
|
||||
|
||||
```bash
|
||||
# Run with UI
|
||||
npm run test:ui
|
||||
|
||||
# Run headed mode
|
||||
npm run test:headed
|
||||
|
||||
# Debug mode
|
||||
npx playwright test --debug
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
### Issue: Prisma Client Not Generated
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
npm run db:generate
|
||||
```
|
||||
|
||||
### Issue: Port Already in Use
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Kill process on port 3000
|
||||
npx kill-port 3000
|
||||
|
||||
# Or find and kill manually
|
||||
lsof -ti:3000 | xargs kill -9
|
||||
```
|
||||
|
||||
### Issue: Module Not Found
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Clear cache and reinstall
|
||||
rm -rf node_modules .next
|
||||
npm install
|
||||
```
|
||||
|
||||
### Issue: Database Locked
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Close Prisma Studio or other DB connections
|
||||
# Or wait for SQLite to release lock
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build & Production
|
||||
|
||||
### Build for Production
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
**Output:** `.next/` directory
|
||||
|
||||
### Start Production Server
|
||||
|
||||
```bash
|
||||
npm start
|
||||
# Runs on port 3000 (or PORT env var)
|
||||
```
|
||||
|
||||
### Environment Variables for Production
|
||||
|
||||
```bash
|
||||
# .env.production
|
||||
DATABASE_URL="file:./prisma/dev.db"
|
||||
NEXTAUTH_SECRET="production-secret"
|
||||
NEXTAUTH_URL="https://your-domain.com"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Tips
|
||||
|
||||
### 1. Use TypeScript Strict Mode
|
||||
|
||||
Already enabled in `tsconfig.json`:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. ESLint & Prettier
|
||||
|
||||
```bash
|
||||
# Lint code
|
||||
npm run lint
|
||||
|
||||
# Fix linting issues
|
||||
npm run lint -- --fix
|
||||
```
|
||||
|
||||
### 3. Git Hooks (Optional)
|
||||
|
||||
Install husky for pre-commit hooks:
|
||||
```bash
|
||||
npm install -D husky lint-staged
|
||||
npx husky install
|
||||
```
|
||||
|
||||
### 4. VS Code Extensions
|
||||
|
||||
Recommended:
|
||||
- Prisma
|
||||
- ESLint
|
||||
- Prettier
|
||||
- Tailwind CSS IntelliSense
|
||||
- TypeScript Vue Plugin (Volar)
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
- Next.js: https://nextjs.org/docs
|
||||
- Prisma: https://www.prisma.io/docs
|
||||
- React: https://react.dev
|
||||
- Tailwind CSS: https://tailwindcss.com/docs
|
||||
- Radix UI: https://www.radix-ui.com/primitives
|
||||
|
||||
### Community
|
||||
- Next.js GitHub Discussions
|
||||
- Prisma Slack
|
||||
- Stack Overflow
|
||||
|
||||
### Project-Specific
|
||||
- API Contracts: `docs/api-contracts-keep-notes.md`
|
||||
- Data Models: `docs/data-models.md`
|
||||
- Component Inventory: `docs/component-inventory.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The Memento web application uses a modern Next.js 16 stack with:
|
||||
- **App Router** for routing
|
||||
- **Server Components** by default
|
||||
- **Prisma ORM** for database access
|
||||
- **NextAuth** for authentication
|
||||
- **Tailwind CSS** for styling
|
||||
- **Playwright** for E2E testing
|
||||
|
||||
This guide provides everything needed for local development, debugging, and testing.
|
||||
Reference in New Issue
Block a user