Keep/docs/development-guide-keep-notes.md
sepehr 640fcb26f7 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
2026-01-09 22:13:49 +01:00

854 lines
14 KiB
Markdown

# 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.