Skip to content

Transform the chaos of scattered thoughts into organized intelligence

rajatvijay/chat-notes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

47 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ChatNotes - AI-Powered Note-Taking Application

License: MIT TypeScript React Supabase OpenAI

An intelligent, mobile-first note-taking application that uses AI to automatically categorize your thoughts into smart categories like Tasks, Ideas, Meetings, and more. Built with React, Supabase, and OpenAI GPT-4o.

🌟 Key Highlights

  • Single Input Interface: One friction-less chat box for all types of notes
  • AI-Powered Organization: Automatic categorization using GPT-4o with 85%+ accuracy
  • Real-time Cost Tracking: Monitor AI usage costs with granular per-request tracking
  • Rich Metadata Extraction: Intelligent extraction of due dates, meeting times, links, and more
  • Mobile-First Design: Optimized for mobile with PWA capabilities
  • Full-Text Search: Instant search across all notes with live results
  • Type-Specific Views: Dedicated interfaces for tasks, ideas, meetings, journal, and reading

πŸ“š Table of Contents


🎯 Product Overview

Vision Statement

ChatNotes transforms the way people capture and organize their thoughts by providing an AI-powered, friction-less interface that automatically understands and categorizes content, eliminating the mental overhead of manual organization.

Core Value Proposition

  1. Cognitive Load Reduction: Users don't need to think about where to put notes
  2. Context Preservation: Rich metadata extraction preserves important context
  3. Rapid Retrieval: Intelligent search and categorization for instant access
  4. Universal Capture: One interface for all types of content

Target Users

Primary Personas

  1. Busy Professionals (40% of user base)

    • Use Case: Meeting notes, action items, follow-ups
    • Pain Point: Context switching between different tools
    • Value: Unified capture with automatic categorization
  2. Students & Researchers (30% of user base)

    • Use Case: Lecture notes, research findings, reading summaries
    • Pain Point: Organizing diverse content types
    • Value: Automatic metadata extraction and linking
  3. Creative Professionals (20% of user base)

    • Use Case: Ideas, inspiration, project notes
    • Pain Point: Losing creative thoughts in complex systems
    • Value: Friction-less capture with intelligent organization
  4. Knowledge Workers (10% of user base)

    • Use Case: Personal knowledge management
    • Pain Point: Information scattered across tools
    • Value: Centralized intelligent organization

Success Metrics

  • Adoption: 40% 7-day retention, 10+ notes per user per week
  • Accuracy: 85%+ correct AI categorization
  • Performance: <2s search latency, <5s note capture time
  • Cost Efficiency: <$0.10 per user per month in AI costs

πŸ—οΈ Architecture & Technical Details

System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Frontend      β”‚    β”‚   API Layer     β”‚    β”‚   Data Layer    β”‚
β”‚   (React/Vite)  β”‚    β”‚ (Vercel Edge)   β”‚    β”‚   (Supabase)    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ Chat UI       │◄──►│ β€’ Classification│◄──►│ β€’ PostgreSQL    β”‚
β”‚ β€’ Category Viewsβ”‚    β”‚ β€’ Search        β”‚    β”‚ β€’ Real-time     β”‚
β”‚ β€’ Settings      β”‚    β”‚ β€’ Metadata      β”‚    β”‚ β€’ Auth Ready    β”‚
β”‚ β€’ Search        β”‚    β”‚ β€’ Cost Tracking β”‚    β”‚ β€’ File Storage  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚   AI Services   β”‚
                    β”‚   (OpenAI)      β”‚
                    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
                    β”‚ β€’ GPT-4o        β”‚
                    β”‚ β€’ Structured    β”‚
                    β”‚   Output        β”‚
                    β”‚ β€’ Token Trackingβ”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Technology Stack

Frontend (apps/web/)

  • Framework: React 18.2.0 with TypeScript 5.0.2
  • Build Tool: Vite 4.4.5 for fast development and optimized builds
  • Styling: Tailwind CSS 3.3.3 with custom design system
  • Routing: React Router DOM 6.15.0 for client-side navigation
  • State Management: React built-in state for local state
  • Data Fetching: Native fetch with custom hooks for server state
  • UI Components: Custom library built on Radix UI primitives
  • Icons: Lucide React for consistent iconography
  • Type Safety: Strict TypeScript configuration

Backend API (api/)

  • Runtime: Vercel Edge Functions (V8 isolate, not Node.js)
  • Language: TypeScript with strict configuration
  • AI Integration: OpenAI GPT-4o with structured JSON output
  • Database: Supabase client for PostgreSQL operations
  • Architecture: Shared utilities for DRY code principles

Database (Supabase)

  • Engine: PostgreSQL 15+ with Supabase extensions
  • Real-time: Supabase real-time subscriptions
  • Auth: Supabase Auth (configured, not implemented)
  • Storage: Supabase Storage (prepared for file uploads)
  • Migrations: Version-controlled schema migrations

Development Tools

  • Package Manager: PNPM with workspace support
  • Linting: ESLint 8.45.0 with React and TypeScript rules
  • Testing: Vitest for unit tests, Playwright for E2E
  • Quality: Prettier, Lighthouse, pa11y for accessibility
  • CI/CD: GitHub Actions ready configuration
  • Deployment: Vercel with automatic deployments

Project Structure

chat-notes/
β”œβ”€β”€ api/                          # Vercel Edge Functions
β”‚   β”œβ”€β”€ shared/                   # Shared utilities (DRY principles)
β”‚   β”‚   β”œβ”€β”€ schemas.ts           # JSON schemas for OpenAI structured output
β”‚   β”‚   β”œβ”€β”€ openai-utils.ts      # OpenAI API calling and prompt management
β”‚   β”‚   β”œβ”€β”€ supabase-utils.ts    # Database operations and client management
β”‚   β”‚   └── cost-tracking.ts     # LLM cost calculation and logging
β”‚   β”œβ”€β”€ classify.ts              # Main classification endpoint
β”‚   β”œβ”€β”€ extract-metadata.ts      # Metadata-only extraction
β”‚   β”œβ”€β”€ search.ts                # Full-text search
β”‚   β”œβ”€β”€ task-completion.ts       # Task completion toggle
β”‚   β”œβ”€β”€ task-due-date.ts         # Due date management
β”‚   β”œβ”€β”€ update-meeting.ts        # Bulk meeting metadata updates
β”‚   β”œβ”€β”€ update-metadata.ts       # Generic metadata updates
β”‚   └── llm-costs.ts            # Cost reporting endpoint
β”œβ”€β”€ apps/
β”‚   └── web/                     # React frontend application
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ components/      # Reusable UI components
β”‚       β”‚   β”‚   β”œβ”€β”€ Composer.tsx # Note input interface
β”‚       β”‚   β”‚   β”œβ”€β”€ SearchBar.tsx# Global search component
β”‚       β”‚   β”‚   β”œβ”€β”€ NotesList.tsx# Generic notes display
β”‚       β”‚   β”‚   β”œβ”€β”€ TasksList.tsx# Task-specific view with completion
β”‚       β”‚   β”‚   β”œβ”€β”€ IdeasList.tsx# Ideas with title/summary display
β”‚       β”‚   β”‚   β”œβ”€β”€ MeetingsList.tsx # Meetings grouped by date/time
β”‚       β”‚   β”‚   β”œβ”€β”€ JournalList.tsx # Journal entries display
β”‚       β”‚   β”‚   β”œβ”€β”€ ReadingList.tsx # Reading notes with links
β”‚       β”‚   β”‚   └── DueDatePicker.tsx # Date selection component
β”‚       β”‚   β”œβ”€β”€ pages/           # Route-level components
β”‚       β”‚   β”‚   β”œβ”€β”€ ChatPage.tsx # Main chat interface with AI features
β”‚       β”‚   β”‚   β”œβ”€β”€ CategoryPage.tsx # Individual category views
β”‚       β”‚   β”‚   β”œβ”€β”€ CategoriesPage.tsx # Categories overview
β”‚       β”‚   β”‚   β”œβ”€β”€ SearchPage.tsx # Search interface and results
β”‚       β”‚   β”‚   └── SettingsPage.tsx # Settings with cost tracking
β”‚       β”‚   β”œβ”€β”€ hooks/           # Custom React hooks
β”‚       β”‚   β”‚   β”œβ”€β”€ useCategoryNotes.ts # Category-specific data fetching
β”‚       β”‚   β”‚   β”œβ”€β”€ useTaskOperations.ts # Task operations (complete, due)
β”‚       β”‚   β”‚   β”œβ”€β”€ useTaskCompletions.ts # Task completion state
β”‚       β”‚   β”‚   β”œβ”€β”€ useDebounce.ts # Search input debouncing
β”‚       β”‚   β”‚   └── useTheme.ts  # Theme management
β”‚       β”‚   └── lib/
β”‚       β”‚       └── supabase.ts  # Supabase client configuration
β”‚       β”œβ”€β”€ public/              # Static assets
β”‚       └── dist/                # Build output
β”œβ”€β”€ packages/
β”‚   └── ui/                      # Shared UI component library
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ components/ui/   # Base UI components
β”‚       β”‚   β”‚   β”œβ”€β”€ button.tsx   # Button with variants
β”‚       β”‚   β”‚   β”œβ”€β”€ card.tsx     # Card component
β”‚       β”‚   β”‚   β”œβ”€β”€ dialog.tsx   # Modal dialog
β”‚       β”‚   β”‚   └── popover.tsx  # Popover component
β”‚       β”‚   └── lib/
β”‚       β”‚       └── utils.ts     # Utility functions (cn, clsx)
β”‚       └── dist/                # Built component library
β”œβ”€β”€ supabase/                    # Database configuration
β”‚   β”œβ”€β”€ migrations/              # Version-controlled schema changes
β”‚   β”‚   β”œβ”€β”€ 001_initial_schema.sql    # Base tables and indexes
β”‚   β”‚   β”œβ”€β”€ 002_add_metadata_column.sql # JSONB metadata support
β”‚   β”‚   β”œβ”€β”€ 003_add_task_completions.sql # Task completion tracking
β”‚   β”‚   └── 004_add_llm_costs.sql     # AI cost tracking
β”‚   β”œβ”€β”€ config.toml             # Local development configuration
β”‚   └── seed.sql                # Sample data for development
β”œβ”€β”€ tests/                      # E2E tests
β”‚   └── e2e.spec.ts            # Playwright test specifications
β”œβ”€β”€ coverage/                   # Test coverage reports
β”œβ”€β”€ docs/                       # Additional documentation
β”œβ”€β”€ package.json               # Root package configuration
β”œβ”€β”€ vercel.json                # Vercel deployment configuration
β”œβ”€β”€ playwright.config.ts       # E2E testing configuration
β”œβ”€β”€ vitest.config.ts           # Unit testing configuration
β”œβ”€β”€ eslint.config.js           # Linting configuration
└── README.md                  # This documentation

✨ Features Documentation

1. AI-Powered Classification System

Overview

The core feature that automatically categorizes notes into predefined categories using OpenAI GPT-4o with structured JSON output for reliability.

Categories

  1. Tasks (βœ…) - Action items, todos, assignments with due date extraction
  2. Ideas (πŸ’‘) - Creative thoughts, innovations, brainstorming with title/summary
  3. Journal (πŸ“–) - Personal reflections, daily logs, experiences
  4. Meetings (πŸ‘₯) - Meeting notes with title, date, and time extraction
  5. Reading (πŸ“š) - Quotes, research notes, article summaries with link enhancement
  6. Misc (πŸ“) - Everything else that doesn't fit other categories

Technical Implementation

// Classification request flow
POST /api/classify
{
  "note_id": "uuid",
  "content": "Fix the login bug due tomorrow"
}

// AI Processing with structured output
const result = await openai.chat.completions.create({
  model: "gpt-4o",
  response_format: {
    type: "json_schema",
    json_schema: { schema: classificationSchema }
  }
})

// Response with metadata
{
  "category": "task",
  "metadata": {
    "due_date": "2024-12-26",
    "cleaned_content": "Fix the login bug"
  }
}

Business Logic

  • Auto Mode: Default behavior, AI classifies all content
  • Manual Override: Users can select categories explicitly
  • Confidence Threshold: Falls back to 'misc' for ambiguous content
  • Content Preservation: Original content always preserved
  • Metadata Enhancement: Category-specific data extraction

2. Intelligent Metadata Extraction

Task Metadata

  • Due Date Extraction: Natural language parsing ("due tomorrow", "by Friday")
  • Content Cleaning: Removes date references for clean task descriptions
  • Date Normalization: Converts relative dates to ISO format
  • Examples:
    • Input: "Fix bug due tomorrow" β†’ Cleaned: "Fix bug", Due: "2024-12-26"
    • Input: "Meeting prep by Friday" β†’ Cleaned: "Meeting prep", Due: "2024-12-29"

Idea Metadata

  • Title Generation: Concise, descriptive titles
  • Summary Creation: 1-2 sentence summaries
  • Examples:
    • Input: "App for sharing recipes with friends" β†’ Title: "Recipe Sharing App", Summary: "A social platform for sharing and discovering recipes among friends"

Meeting Metadata

  • Title Extraction: Meeting subject or purpose
  • Date Parsing: Meeting dates in various formats
  • Time Extraction: Meeting times in 24-hour format
  • Grouping Logic: Meetings grouped by date/time in list view

Reading Metadata

  • Link Detection: Automatic URL extraction
  • Title Enhancement: Uses LLM to generate titles for links
  • Summary Generation: Content summaries enhanced by URL analysis
  • Two-Pass Processing: Initial extraction + LLM enhancement for links

3. Real-time Cost Tracking

Cost Calculation

// GPT-4o Pricing (as of 2024)
const pricing = {
  input: 0.0025 / 1000,   // $0.0025 per 1K input tokens
  output: 0.01 / 1000     // $0.01 per 1K output tokens
}

// Token estimation (4 chars β‰ˆ 1 token)
function calculateTokens(text: string): number {
  return Math.ceil(text.length / 4)
}

Database Schema

CREATE TABLE llm_costs (
  id UUID PRIMARY KEY,
  date DATE DEFAULT CURRENT_DATE,
  endpoint TEXT NOT NULL,           -- 'classify', 'extract-metadata', 'enhance-reading'
  model TEXT DEFAULT 'gpt-4o',
  input_tokens INTEGER DEFAULT 0,
  output_tokens INTEGER DEFAULT 0,
  cost_usd DECIMAL(10, 6) DEFAULT 0, -- 6 decimal precision
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Cost Reporting

  • Daily Breakdown: Costs grouped by date with request counts
  • Endpoint Tracking: Separate tracking for different API endpoints
  • 30-Day Window: Historical data for cost analysis
  • Real-time Updates: Automatic cost logging for all LLM calls

4. Advanced Search System

Search Capabilities

  • Full-Text Search: PostgreSQL ILIKE search across content
  • Real-time Results: Debounced search with 300ms delay
  • Result Highlighting: Visual emphasis on matching terms
  • Category Filtering: Search within specific categories
  • Metadata Search: Search within extracted metadata fields

Technical Implementation

// Search API endpoint
POST /api/search
{
  "query": "meeting tomorrow",
  "category": "meeting" // optional
}

// Database query with metadata search
SELECT * FROM notes 
WHERE (content ILIKE '%meeting%' AND content ILIKE '%tomorrow%')
   OR (metadata->>'title' ILIKE '%meeting%')
   OR (metadata->>'date' = '2024-12-26')
ORDER BY created_at DESC
LIMIT 50;

5. Type-Specific List Views

TasksList Component

  • Completion Toggle: Mark tasks as done with visual feedback
  • Due Date Display: Color-coded due dates (overdue, today, upcoming)
  • Content Display: Shows cleaned content without date references
  • Bulk Operations: Planned for future releases

IdeasList Component

  • Title/Summary View: Shows extracted title and summary
  • Full Content Modal: Click to view complete original content
  • Individual Display: Each idea shown separately for better scanning

MeetingsList Component

  • Date/Time Grouping: Meetings grouped by date and time
  • Batch Editing: Edit title, date, time for multiple related notes
  • Meeting Context: Shows all notes from same meeting together

ReadingList Component

  • Link Highlighting: Extracted URLs prominently displayed
  • Enhanced Titles: LLM-generated titles for better organization
  • Summary Preview: Quick overview of reading content

6. Interactive Message Cards

Type Changing

  • Dropdown Selection: Change note type with automatic metadata extraction
  • Loading States: Visual feedback during reclassification
  • Optimistic Updates: Immediate UI updates with rollback on error

Metadata Editing

  • Inline Editing: Edit metadata directly in the message card
  • Type-Specific Fields: Different fields based on note category
  • Event Handling: Proper event propagation to prevent modal conflicts

7. Mobile-First Design

Responsive Design

  • Breakpoints: Optimized for screens ≀ 448px (28rem)
  • Touch Targets: Minimum 44px tap targets for accessibility
  • Gesture Support: Swipe actions for mobile interactions
  • PWA Ready: Service worker configuration for offline use

Performance Optimizations

  • Lazy Loading: Components loaded on demand
  • Image Optimization: Responsive images with modern formats
  • Bundle Splitting: Code splitting for faster initial loads
  • Caching Strategy: Aggressive caching for static assets

πŸ”Œ API Documentation

Authentication

All API endpoints currently use Supabase service role key for database access. User authentication is prepared but not implemented in the current version.

Base URL

  • Development: http://localhost:3001/api/
  • Production: https://your-domain.vercel.app/api/

Common Response Format

// Success Response
{
  "success": true,
  "data": { ... },
  "metadata": { ... }
}

// Error Response
{
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": { ... }
}

Endpoints

1. Classification API

POST /api/classify

Classifies note content and extracts category-specific metadata.

Request Body:

{
  "note_id": string,    // UUID of the note to classify
  "content": string     // The note content to classify
}

Response:

{
  "category": "task" | "idea" | "journal" | "meeting" | "reading" | "misc",
  "metadata": {
    // Task metadata
    "due_date"?: string,           // ISO date string
    "cleaned_content"?: string,    // Content with dates removed
    
    // Idea metadata
    "title"?: string,              // Generated title
    "summary"?: string,            // Brief summary
    
    // Meeting metadata
    "title"?: string,              // Meeting title
    "date"?: string,               // ISO date string
    "time"?: string,               // HH:MM format
    
    // Reading metadata
    "link"?: string,               // Extracted URL
    "title"?: string,              // Enhanced title (for links)
    "summary"?: string             // Content summary
  }
}

Side Effects:

  • Updates the note record in the database
  • Logs cost information to llm_costs table
  • Triggers real-time updates via Supabase subscriptions

Error Cases:

  • 400: Missing note_id or content
  • 500: OpenAI API error or database error

2. Metadata Extraction API

POST /api/extract-metadata

Extracts metadata for a specific category without full classification.

Request Body:

{
  "note_id": string,      // UUID of the note
  "content": string,      // The note content
  "category": string      // Target category for metadata extraction
}

Response:

{
  "metadata": Record<string, unknown>  // Category-specific metadata
}

Use Cases:

  • Type changes in message cards
  • Re-processing existing notes
  • Manual metadata extraction

3. Search API

POST /api/search

Performs full-text search across note content and metadata.

Request Body:

{
  "query": string,          // Search terms
  "category"?: string,      // Optional category filter
  "limit"?: number          // Max results (default: 50)
}

Response:

{
  "results": Array<{
    "id": string,
    "content": string,
    "category": string,
    "metadata": Record<string, unknown>,
    "created_at": string,
    "highlight"?: string     // Highlighted match excerpt
  }>
}

Search Features:

  • Case-insensitive partial matching
  • Metadata field search
  • Category filtering
  • Relevance ordering

4. Task Operations API

POST /api/task-completion

Toggles task completion status.

Request Body:

{
  "note_id": string,      // UUID of the task note
  "completed": boolean    // New completion status
}

Response:

{
  "success": boolean,
  "completed": boolean,
  "completed_at": string | null  // Timestamp if completed
}

POST /api/task-due-date

Sets or updates task due date.

Request Body:

{
  "note_id": string,      // UUID of the task note
  "due_date": string      // ISO date string
}

5. Meeting Management API

POST /api/update-meeting

Bulk update meeting metadata for multiple notes.

Request Body:

{
  "note_ids": string[],   // Array of note UUIDs
  "title": string,        // Meeting title
  "date": string,         // ISO date string
  "time": string          // HH:MM format
}

Use Case:

  • Updating all notes from the same meeting
  • Batch metadata corrections

6. Cost Reporting API

GET /api/llm-costs

Retrieves LLM usage costs and statistics.

Response:

{
  "totalCost": number,              // Total cost in USD
  "totalRequests": number,          // Total number of requests
  "dailyBreakdown": Array<{
    "date": string,                 // ISO date string
    "cost": number,                 // Cost for that day
    "requests": number              // Number of requests
  }>
}

Features:

  • 30-day historical data
  • Daily aggregation
  • Per-endpoint cost tracking
  • Graceful degradation if table doesn't exist

Rate Limiting

Vercel Edge Functions have built-in rate limiting:

  • Free Plan: 100,000 function invocations per month
  • Pro Plan: 1,000,000 function invocations per month
  • Per-IP Limiting: Handled at Vercel level

Error Handling

All endpoints implement consistent error handling:

try {
  // API logic
} catch (error) {
  console.error('Endpoint error:', error)
  return new Response('Internal server error', { status: 500 })
}

Common error patterns:

  • Validation Errors: 400 status with descriptive message
  • Authentication Errors: 401 status (when auth is implemented)
  • Rate Limiting: 429 status
  • Server Errors: 500 status with generic message

OpenAI Integration

All AI-powered endpoints use structured output for reliability:

// Structured output configuration
response_format: {
  type: "json_schema",
  json_schema: {
    name: "result",
    schema: {
      type: "object",
      properties: { ... },
      required: [...],
      additionalProperties: false
    }
  }
}

This ensures:

  • Consistent JSON responses
  • No markdown code blocks in output
  • Validation of required fields
  • Type safety in processing

πŸ—„οΈ Database Schema

Overview

The database uses PostgreSQL with Supabase extensions for UUID generation, real-time subscriptions, and full-text search capabilities.

Core Tables

1. Categories Table

CREATE TABLE categories (
  name TEXT PRIMARY KEY CHECK (name IN ('task', 'idea', 'journal', 'meeting', 'reading', 'misc'))
);

-- Pre-populated with valid categories
INSERT INTO categories (name) VALUES 
  ('task'), ('idea'), ('journal'), ('meeting'), ('reading'), ('misc');

Purpose: Enforces valid category values and provides reference data.

2. Notes Table

CREATE TABLE notes (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  content TEXT NOT NULL,
  category TEXT REFERENCES categories(name) ON DELETE SET NULL,
  source TEXT DEFAULT 'auto' CHECK (source IN ('auto', 'manual')),
  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes for performance
CREATE INDEX idx_notes_category ON notes(category);
CREATE INDEX idx_notes_created_at ON notes(created_at DESC);
CREATE INDEX idx_notes_source ON notes(source);
CREATE INDEX idx_notes_metadata ON notes USING gin(metadata);

-- Full-text search index
CREATE INDEX idx_notes_content_fts ON notes USING gin(to_tsvector('english', content));

Fields Explanation:

  • content: Original user input, never modified
  • category: AI-classified or manually selected category
  • source: Tracks whether classification was automatic or manual
  • metadata: JSONB field for category-specific data
  • created_at: Timestamp for ordering and date-based queries

Metadata Examples:

// Task metadata
{
  "due_date": "2024-12-26",
  "cleaned_content": "Fix the login bug"
}

// Idea metadata
{
  "title": "Recipe Sharing App",
  "summary": "A social platform for sharing recipes among friends"
}

// Meeting metadata
{
  "title": "Sprint Planning",
  "date": "2024-12-25",
  "time": "14:00"
}

// Reading metadata
{
  "link": "https://example.com/article",
  "title": "AI in Note-Taking Apps",
  "summary": "Overview of AI applications in productivity tools"
}

3. Task Completions Table

CREATE TABLE task_completions (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  note_id UUID NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
  completed_at TIMESTAMPTZ DEFAULT NOW(),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes for performance
CREATE INDEX idx_task_completions_note_id ON task_completions(note_id);
CREATE INDEX idx_task_completions_completed_at ON task_completions(completed_at DESC);

-- Unique constraint to prevent duplicate completions
CREATE UNIQUE INDEX idx_task_completions_unique_note ON task_completions(note_id);

Purpose: Tracks task completion status separately from notes table for data integrity and audit trail.

Business Logic:

  • One completion record per task
  • Preserves completion timestamp
  • Cascade deletion when note is deleted
  • Unique constraint prevents duplicates

4. LLM Costs Table

CREATE TABLE llm_costs (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  date DATE NOT NULL DEFAULT CURRENT_DATE,
  endpoint TEXT NOT NULL,                    -- 'classify', 'extract-metadata', 'enhance-reading'
  model TEXT NOT NULL DEFAULT 'gpt-4o',
  input_tokens INTEGER NOT NULL DEFAULT 0,
  output_tokens INTEGER NOT NULL DEFAULT 0,
  cost_usd DECIMAL(10, 6) NOT NULL DEFAULT 0, -- 6 decimal precision for accuracy
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes for efficient querying
CREATE INDEX idx_llm_costs_date ON llm_costs(date DESC);
CREATE INDEX idx_llm_costs_endpoint ON llm_costs(endpoint);
CREATE INDEX idx_llm_costs_created_at ON llm_costs(created_at DESC);
CREATE INDEX idx_llm_costs_date_endpoint ON llm_costs(date, endpoint);

Purpose: Tracks AI usage costs with granular detail for cost analysis and optimization.

Cost Calculation:

-- Daily cost aggregation query
SELECT 
  date,
  endpoint,
  SUM(cost_usd) as total_cost,
  COUNT(*) as request_count,
  SUM(input_tokens) as total_input_tokens,
  SUM(output_tokens) as total_output_tokens
FROM llm_costs 
WHERE date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY date, endpoint
ORDER BY date DESC;

Database Constraints and Validation

Foreign Key Relationships

-- Category validation
ALTER TABLE notes 
  ADD CONSTRAINT fk_notes_category 
  FOREIGN KEY (category) REFERENCES categories(name) 
  ON DELETE SET NULL;

-- Task completion relationship
ALTER TABLE task_completions 
  ADD CONSTRAINT fk_task_completions_note 
  FOREIGN KEY (note_id) REFERENCES notes(id) 
  ON DELETE CASCADE;

Check Constraints

-- Source validation
ALTER TABLE notes 
  ADD CONSTRAINT check_source 
  CHECK (source IN ('auto', 'manual'));

-- Category validation
ALTER TABLE categories 
  ADD CONSTRAINT check_category_name 
  CHECK (name IN ('task', 'idea', 'journal', 'meeting', 'reading', 'misc'));

-- Cost validation
ALTER TABLE llm_costs 
  ADD CONSTRAINT check_positive_cost 
  CHECK (cost_usd >= 0);

ALTER TABLE llm_costs 
  ADD CONSTRAINT check_positive_tokens 
  CHECK (input_tokens >= 0 AND output_tokens >= 0);

Performance Optimizations

Indexing Strategy

  1. Primary Queries: Category filtering, date ordering
  2. Search Queries: Full-text search on content
  3. Metadata Queries: GIN index for JSONB operations
  4. Cost Queries: Date and endpoint aggregations

Query Patterns

-- Most common: Recent notes by category
SELECT * FROM notes 
WHERE category = 'task' 
ORDER BY created_at DESC 
LIMIT 20;

-- Search with metadata
SELECT * FROM notes 
WHERE content ILIKE '%search term%' 
   OR metadata->>'title' ILIKE '%search term%'
ORDER BY created_at DESC;

-- Task completion status
SELECT n.*, tc.completed_at 
FROM notes n
LEFT JOIN task_completions tc ON n.id = tc.note_id
WHERE n.category = 'task'
ORDER BY n.created_at DESC;

Data Migration Strategy

Version-Controlled Migrations

  1. 001_initial_schema.sql: Base tables and indexes
  2. 002_add_metadata_column.sql: JSONB metadata support
  3. 003_add_task_completions.sql: Task completion tracking
  4. 004_add_llm_costs.sql: AI cost tracking

Migration Best Practices

  • Additive Changes: New migrations never modify existing data
  • Backward Compatibility: Old schema versions continue to work
  • Data Preservation: No destructive operations in migrations
  • Testing: All migrations tested in development environment

Backup and Recovery

Supabase Automatic Backups

  • Point-in-Time Recovery: 1-second granularity
  • Daily Backups: Automated daily snapshots
  • Geographic Replication: Multi-region backup storage

Data Export Options

-- Export notes with metadata
COPY (
  SELECT 
    id, content, category, source, metadata, created_at
  FROM notes 
  ORDER BY created_at
) TO '/tmp/notes_backup.csv' WITH CSV HEADER;

-- Export cost data
COPY (
  SELECT 
    date, endpoint, model, input_tokens, output_tokens, cost_usd
  FROM llm_costs 
  WHERE date >= CURRENT_DATE - INTERVAL '90 days'
) TO '/tmp/costs_backup.csv' WITH CSV HEADER;

βš™οΈ Setup & Development

Prerequisites

System Requirements

  • Node.js: 18.0.0 or higher
  • PNPM: 8.0.0 or higher (recommended package manager)
  • Git: For version control

External Services

  • Supabase Account: For database and real-time features
  • OpenAI API Key: For GPT-4o classification
  • Vercel Account: For deployment (optional for development)

Environment Setup

1. Clone and Install

# Clone the repository
git clone <repository-url>
cd chat-notes

# Install dependencies
pnpm install

2. Environment Variables

Create environment files with your service credentials:

Root .env:

# Supabase Configuration
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

# OpenAI Configuration
OPENAI_KEY=sk-your-openai-api-key

# Development Configuration (optional)
NODE_ENV=development

apps/web/.env:

# Frontend Supabase Configuration
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_key

Environment Variable Details:

  • SUPABASE_SERVICE_ROLE_KEY: Full access key for API functions
  • VITE_SUPABASE_ANON_KEY: Public key for frontend with RLS policies
  • OPENAI_KEY: API key with access to GPT-4o model

3. Database Setup

Option A: Remote Supabase Database (Recommended)
# Install Supabase CLI
npm install -g @supabase/cli

# Link to your Supabase project
supabase login
supabase link --project-ref YOUR_PROJECT_REF

# Push schema to remote database
pnpm db:migrate

# Verify migration success
supabase db pull
Option B: Local Development Database
# Start local Supabase stack
pnpm dev:supabase

# Check status and get local credentials
pnpm supabase:status

# Apply migrations to local database
supabase db reset

# Access local dashboard
open http://localhost:54323

For local development, update your environment:

# apps/web/.env.local
VITE_SUPABASE_URL=http://localhost:54321
VITE_SUPABASE_ANON_KEY=<local_anon_key_from_status>

# Root .env.local
SUPABASE_URL=http://localhost:54321
SUPABASE_SERVICE_ROLE_KEY=<local_service_role_key>

Development Workflow

Available Scripts

# Development
pnpm dev:full          # Full-stack development (frontend + edge functions)
pnpm dev:web           # Frontend only (Vite dev server)
pnpm dev:supabase      # Local Supabase stack

# Database Operations
pnpm db:migrate        # Push migrations to remote database
pnpm db:reset          # Reset database with migrations + seed data
pnpm supabase:status   # Check local Supabase status
pnpm supabase:stop     # Stop local Supabase

# Quality Assurance
pnpm build             # Build for production
pnpm lint              # Run ESLint
pnpm test              # Run unit tests with coverage
pnpm e2e               # Run end-to-end tests
pnpm a11y              # Accessibility audit
pnpm lh                # Lighthouse performance audit

# Preview
pnpm preview           # Preview production build locally

Development Servers

Full Development Stack:

pnpm dev:full

This starts:

  • Frontend: React app on http://localhost:5173
  • API: Vercel Edge Functions on http://localhost:3001
  • Database: Connect to your Supabase instance

Individual Services:

# Terminal 1: Frontend
pnpm dev:web

# Terminal 2: Local database (optional)
pnpm dev:supabase

Development Flow

  1. Start Development Environment

    pnpm dev:full
  2. Open Application

    • Navigate to http://localhost:5173
    • The frontend automatically proxies API calls to the Vercel dev server
  3. Test Core Features

    • Type a note: "Fix the login bug due tomorrow"
    • Select "Auto" classification
    • Verify automatic categorization as "task"
    • Check metadata extraction (due date, cleaned content)
  4. Development Tools

    • React DevTools: For component debugging
    • Supabase Dashboard: Database inspection and real-time logs
    • Vercel Function Logs: API function debugging
    • Network Tab: API request/response inspection

Code Quality Standards

TypeScript Configuration

// tsconfig.json (strict configuration)
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}

ESLint Rules

// eslint.config.js
export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    rules: {
      '@typescript-eslint/no-unused-vars': 'error',
      'react-hooks/exhaustive-deps': 'warn',
      'no-console': 'warn' // Warnings for console.log in production
    }
  }
]

Pre-commit Hooks

# Runs automatically before commits
pnpm lint      # ESLint check
pnpm test      # Unit tests
pnpm build     # TypeScript compilation check

Testing Strategy

Unit Tests (Vitest)

# Run unit tests
pnpm test

# Watch mode during development
pnpm test --watch

# Coverage report
pnpm test --coverage

Test Structure:

// src/utils.test.ts
import { describe, it, expect } from 'vitest'
import { calculateTokens } from './cost-tracking'

describe('Cost Tracking', () => {
  it('should calculate tokens correctly', () => {
    expect(calculateTokens('Hello world')).toBe(3)
  })
})

E2E Tests (Playwright)

# Run end-to-end tests
pnpm e2e

# Run with UI for debugging
pnpm e2e --ui

# Run specific test
pnpm e2e --grep "note classification"

Test Examples:

// tests/e2e.spec.ts
test('should classify note automatically', async ({ page }) => {
  await page.goto('http://localhost:5173')
  await page.fill('[data-testid="note-input"]', 'Buy groceries tomorrow')
  await page.click('[data-testid="send-button"]')
  
  // Wait for classification
  await expect(page.locator('[data-category="task"]')).toBeVisible()
})

Debugging

API Function Debugging

# Start Vercel dev with debug mode
vercel dev --debug

# View function logs in real-time
# Logs appear in the terminal running vercel dev

Database Debugging

-- Check recent notes
SELECT id, content, category, metadata, created_at 
FROM notes 
ORDER BY created_at DESC 
LIMIT 10;

-- Check classification costs
SELECT date, endpoint, SUM(cost_usd) as daily_cost, COUNT(*) as requests
FROM llm_costs 
WHERE date >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY date, endpoint
ORDER BY date DESC;

-- Check task completions
SELECT n.content, n.category, tc.completed_at
FROM notes n
LEFT JOIN task_completions tc ON n.id = tc.note_id
WHERE n.category = 'task'
ORDER BY n.created_at DESC;

Frontend Debugging

// Enable debug logging
localStorage.setItem('debug', 'true')

// Component debugging
console.log('Notes state:', notes)
console.log('Classification status:', classifyingNotes)

Troubleshooting Common Issues

1. Vercel Function Errors

# Error: Function Runtimes must have a valid version
npm install -g vercel@latest
vercel dev --debug

# Solution: Update Vercel CLI and check runtime configuration in functions

2. Supabase Connection Issues

// Check environment variables
console.log('Supabase URL:', process.env.SUPABASE_URL)
console.log('Service Key exists:', !!process.env.SUPABASE_SERVICE_ROLE_KEY)

// Test connection
const { data, error } = await supabase
  .from('notes')
  .select('count(*)', { count: 'exact' })

3. OpenAI API Issues

// Check API key format
if (!process.env.OPENAI_KEY?.startsWith('sk-')) {
  console.error('Invalid OpenAI API key format')
}

// Test API connection
const response = await fetch('https://api.openai.com/v1/models', {
  headers: { 'Authorization': `Bearer ${process.env.OPENAI_KEY}` }
})

4. Frontend Build Issues

# Clear build cache
rm -rf apps/web/dist
rm -rf apps/web/node_modules/.vite

# Reinstall dependencies
pnpm install

# Check for TypeScript errors
pnpm build

πŸš€ Deployment

Vercel Deployment (Recommended)

Prerequisites

  • Vercel account connected to your GitHub repository
  • Environment variables configured in Vercel dashboard

1. Project Configuration

vercel.json:

{
  "buildCommand": "pnpm build",
  "outputDirectory": "apps/web/dist",
  "installCommand": "pnpm install"
}

2. Environment Variables Setup

Configure in Vercel Dashboard β†’ Project Settings β†’ Environment Variables:

# Production Environment Variables
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_production_service_key
OPENAI_KEY=sk-your-production-openai-key
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_production_anon_key

# Optional: Analytics and monitoring
VERCEL_ANALYTICS_ID=your_analytics_id

3. Deployment Process

Automatic Deployment:

# Push to main branch for automatic deployment
git push origin main

Manual Deployment:

# Install Vercel CLI
npm install -g vercel

# Link project (first time only)
vercel link

# Deploy to production
vercel --prod

# Deploy to preview
vercel

4. Custom Domain Setup

  1. Add Domain in Vercel Dashboard

    • Go to Project Settings β†’ Domains
    • Add your custom domain
    • Configure DNS records as instructed
  2. SSL Certificate

    • Automatically provisioned by Vercel
    • Uses Let's Encrypt for free SSL
  3. DNS Configuration Example:

    # CNAME record
    www.your-domain.com β†’ your-project.vercel.app
    
    # A record for apex domain
    your-domain.com β†’ 76.76.19.61 (Vercel's IP)
    

Alternative Deployment Options

1. Netlify Deployment

# Build configuration
[build]
  command = "pnpm build"
  publish = "apps/web/dist"

# Environment variables required
SUPABASE_URL=your_supabase_url
SUPABASE_SERVICE_ROLE_KEY=your_service_key
OPENAI_KEY=your_openai_key

2. Self-Hosted Deployment

Docker Configuration:

# Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm install -g pnpm
RUN pnpm install

COPY . .
RUN pnpm build

EXPOSE 3000
CMD ["pnpm", "preview"]

Docker Compose:

# docker-compose.yml
version: '3.8'
services:
  chat-notes:
    build: .
    ports:
      - "3000:3000"
    environment:
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY}
      - OPENAI_KEY=${OPENAI_KEY}

Production Optimizations

1. Performance Optimizations

// vite.config.ts production optimizations
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          supabase: ['@supabase/supabase-js'],
          ui: ['lucide-react']
        }
      }
    },
    sourcemap: false, // Disable in production
    minify: 'terser'
  }
})

2. Security Headers

// vercel.json security configuration
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.openai.com https://*.supabase.co"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        }
      ]
    }
  ]
}

3. Caching Strategy

// Static asset caching
{
  "routes": [
    {
      "src": "/assets/(.*)",
      "headers": {
        "Cache-Control": "public, max-age=31536000, immutable"
      }
    },
    {
      "src": "/api/(.*)",
      "headers": {
        "Cache-Control": "no-cache, no-store, must-revalidate"
      }
    }
  ]
}

Monitoring and Analytics

1. Vercel Analytics

// Enable in production
import { Analytics } from '@vercel/analytics/react'

function App() {
  return (
    <>
      <Router />
      <Analytics />
    </>
  )
}

2. Performance Monitoring

// Web Vitals tracking
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'

function sendToAnalytics(metric) {
  // Send to your analytics service
  console.log(metric)
}

getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getFCP(sendToAnalytics)
getLCP(sendToAnalytics)
getTTFB(sendToAnalytics)

3. Error Tracking

// Global error handling
window.addEventListener('error', (event) => {
  console.error('Global error:', event.error)
  // Send to error tracking service
})

window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason)
  // Send to error tracking service
})

Health Checks and Monitoring

1. API Health Check

// api/health.ts
export default function handler() {
  return new Response(JSON.stringify({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    version: process.env.VERCEL_GIT_COMMIT_SHA || 'development'
  }), {
    headers: { 'Content-Type': 'application/json' }
  })
}

2. Database Health Check

// Check database connectivity
async function checkDatabase() {
  try {
    const { data, error } = await supabase
      .from('notes')
      .select('count(*)', { count: 'exact' })
    
    return !error
  } catch {
    return false
  }
}

3. External Service Health

// Check OpenAI API connectivity
async function checkOpenAI() {
  try {
    const response = await fetch('https://api.openai.com/v1/models', {
      headers: { 'Authorization': `Bearer ${process.env.OPENAI_KEY}` }
    })
    return response.ok
  } catch {
    return false
  }
}

🧠 Business Logic & Technical Decisions

Architecture Decisions

1. Edge Functions over Traditional Server

Decision: Use Vercel Edge Functions instead of Node.js server

Rationale:

  • Global Distribution: Functions run closer to users worldwide
  • Cold Start Performance: V8 isolates start faster than Node.js containers
  • Cost Efficiency: Pay-per-invocation vs. always-on server costs
  • Scalability: Automatic scaling without infrastructure management

Trade-offs:

  • Runtime Limitations: No Node.js APIs, limited to Web APIs
  • Execution Time: 30-second maximum execution time
  • Memory Constraints: Limited memory per function invocation

Implementation Impact:

// Edge-compatible imports only
import { createClient } from '@supabase/supabase-js' // βœ… Works
// import fs from 'fs' // ❌ Not available

// Web API usage
export const config = { runtime: 'edge' }
export default async function handler(request: Request): Promise<Response> {
  // Web API only, no Node.js APIs
}

2. Structured JSON Output for AI

Decision: Use OpenAI's structured output feature instead of parsing text responses

Rationale:

  • Reliability: Eliminates JSON parsing errors from markdown code blocks
  • Validation: Built-in schema validation ensures required fields
  • Type Safety: Predictable response structure for TypeScript
  • Error Reduction: No need for custom parsing logic

Implementation:

// Before: Text parsing with potential errors
const response = await openai.chat.completions.create({
  model: "gpt-4o",
  messages: [...],
})
// Parse markdown code blocks manually (error-prone)

// After: Structured output
const response = await openai.chat.completions.create({
  model: "gpt-4o",
  messages: [...],
  response_format: {
    type: "json_schema",
    json_schema: { schema: validationSchema }
  }
})
// Guaranteed valid JSON

3. JSONB Metadata Column

Decision: Use PostgreSQL JSONB for metadata storage instead of separate tables

Rationale:

  • Flexibility: Different categories require different metadata fields
  • Performance: GIN indexes on JSONB enable fast queries
  • Simplicity: Avoid complex table relationships for variable data
  • Evolution: Easy to add new metadata fields without migrations

Schema Design:

-- Single table with JSONB metadata
CREATE TABLE notes (
  id UUID PRIMARY KEY,
  content TEXT NOT NULL,
  category TEXT,
  metadata JSONB DEFAULT '{}', -- Flexible metadata storage
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Efficient indexing
CREATE INDEX idx_notes_metadata ON notes USING gin(metadata);

-- Query examples
SELECT * FROM notes WHERE metadata->>'due_date' = '2024-12-26';
SELECT * FROM notes WHERE metadata ? 'title';

4. Separate Task Completions Table

Decision: Track task completions in separate table instead of metadata field

Rationale:

  • Data Integrity: Completion status is critical business data
  • Audit Trail: Preserve completion timestamps
  • Constraints: Unique constraints prevent duplicate completions
  • Queries: Easier to query completed vs. incomplete tasks

Implementation:

CREATE TABLE task_completions (
  id UUID PRIMARY KEY,
  note_id UUID REFERENCES notes(id) ON DELETE CASCADE,
  completed_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(note_id) -- Prevent duplicate completions
);

-- Query for incomplete tasks
SELECT n.* FROM notes n
LEFT JOIN task_completions tc ON n.id = tc.note_id
WHERE n.category = 'task' AND tc.id IS NULL;

AI Classification Logic

1. Category-Specific Prompts

Decision: Use different prompts for each category instead of generic classification

Business Logic:

// Category-specific prompt building
function buildCategoryPrompt(category: string): string {
  switch (category) {
    case 'task':
      return `Extract due date from natural language. Convert relative dates to ISO format. 
              Remove date references from content to create clean task description.`
    
    case 'idea':
      return `Generate a concise title and 1-2 sentence summary that captures the essence 
              of the creative thought or innovation.`
    
    case 'meeting':
      return `Extract meeting title, date, and time. Convert relative dates to ISO format. 
              Use 24-hour time format.`
    
    case 'reading':
      return `Extract URLs if present. For content with links, generate enhanced titles 
              and summaries using additional LLM processing.`
  }
}

Rationale:

  • Accuracy: Specialized prompts improve extraction quality
  • Consistency: Standardized output format per category
  • Context: Category-specific context improves AI understanding

2. Two-Pass Processing for Reading Content

Decision: Implement two-pass processing for reading notes with links

Business Logic:

// First pass: Extract link and basic metadata
const metadata = await extractMetadata(content, 'reading')

// Second pass: Enhance with LLM if link present
if (metadata.link) {
  const enhancedData = await enhanceReadingContent(content, metadata.link)
  metadata.title = enhancedData.title
  metadata.summary = enhancedData.summary
}

Rationale:

  • Quality: LLM can generate better titles/summaries when aware of URL context
  • Cost: Only use additional LLM call when necessary (links present)
  • Accuracy: URL context improves title and summary generation

3. Content Preservation Strategy

Decision: Always preserve original content, store cleaned content in metadata

Implementation:

// Original content always preserved
const note = {
  content: "Fix the login bug due tomorrow", // Never modified
  metadata: {
    cleaned_content: "Fix the login bug",    // Cleaned version
    due_date: "2024-12-26"                   // Extracted date
  }
}

Rationale:

  • Data Integrity: Never lose user's original input
  • Reversibility: Can always revert to original content
  • Debugging: Helps debug AI extraction issues
  • User Trust: Users can see their exact input is preserved

Performance Optimizations

1. Debounced Search

Decision: Implement 300ms debounced search instead of real-time search

Implementation:

const useDebounce = (value: string, delay: number) => {
  const [debouncedValue, setDebouncedValue] = useState(value)

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value)
    }, delay)

    return () => clearTimeout(handler)
  }, [value, delay])

  return debouncedValue
}

// Usage in search component
const debouncedQuery = useDebounce(searchQuery, 300)

Rationale:

  • Performance: Reduce API calls during typing
  • User Experience: Avoid flickering search results
  • Cost: Minimize database queries and API calls

2. Optimistic Updates

Decision: Update UI immediately, rollback on error

Implementation:

const handleTypeChange = async (noteId: string, newCategory: string) => {
  // Optimistic update
  setNotes(prev => prev.map(note => 
    note.id === noteId 
      ? { ...note, category: newCategory }
      : note
  ))

  try {
    await updateNoteCategory(noteId, newCategory)
  } catch (error) {
    // Rollback on error
    setNotes(prev => prev.map(note => 
      note.id === noteId 
        ? { ...note, category: originalCategory }
        : note
    ))
  }
}

Rationale:

  • Perceived Performance: UI feels instantly responsive
  • User Experience: No waiting for server responses
  • Error Handling: Graceful degradation on failures

3. Cost-Aware Processing

Decision: Implement comprehensive cost tracking for all AI operations

Business Logic:

// Automatic cost calculation and logging
export function calculateAndLogCost(
  endpoint: string,
  inputText: string,
  outputText: string
): void {
  const inputTokens = calculateTokens(inputText)
  const outputTokens = calculateTokens(outputText)
  const cost = calculateCost(inputTokens, outputTokens)
  
  // Async logging without blocking response
  logCost(endpoint, inputTokens, outputTokens, cost)
    .catch(console.error)
}

Rationale:

  • Cost Control: Track AI usage to optimize costs
  • Transparency: Users can see their AI usage costs
  • Optimization: Identify expensive operations for optimization
  • Budgeting: Enable cost-based feature gating if needed

User Experience Decisions

1. Mobile-First Design

Decision: Design for mobile screens first, then scale up

Implementation:

/* Mobile-first Tailwind classes */
.container {
  @apply w-full max-w-md mx-auto p-4;
}

/* Desktop enhancements */
@media (min-width: 768px) {
  .container {
    @apply max-w-2xl p-6;
  }
}

Rationale:

  • Usage Patterns: Notes are often captured on mobile devices
  • Performance: Mobile-optimized bundle sizes and interactions
  • Accessibility: Touch-friendly targets and gestures

2. Auto-Classification Default

Decision: Default to automatic classification instead of manual selection

Business Logic:

// Default behavior: Auto classification
const [selectedCategory, setSelectedCategory] = useState('auto')

const handleSubmit = async (content: string) => {
  if (selectedCategory === 'auto') {
    // Save with null category, trigger AI classification
    await saveNote(content, null, 'auto')
    await classifyNote(noteId, content)
  } else {
    // Save with selected category
    await saveNote(content, selectedCategory, 'manual')
  }
}

Rationale:

  • Friction Reduction: Users don't need to think about categorization
  • AI Showcase: Demonstrates AI capabilities immediately
  • User Adoption: Lower barrier to entry for new users

3. Inline Metadata Editing

Decision: Enable editing metadata directly in message cards

Implementation:

const handleMetadataEdit = (noteId: string, field: string, value: string) => {
  // Immediate UI update
  setNotes(prev => prev.map(note => 
    note.id === noteId 
      ? { 
          ...note, 
          metadata: { ...note.metadata, [field]: value }
        }
      : note
  ))

  // Debounced API call
  debouncedUpdateMetadata(noteId, { [field]: value })
}

Rationale:

  • Efficiency: Edit without opening separate modals
  • Context: Edit while viewing the note content
  • Batch Operations: Multiple related notes can be edited quickly

Security and Privacy Decisions

1. Service Role Key Usage

Decision: Use Supabase service role key for API functions instead of user authentication

Current Implementation:

// API functions use service role key
const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY! // Full access
)

Rationale:

  • Simplicity: No authentication flow needed for MVP
  • Development Speed: Focus on core features first
  • Future Migration: Easy to add RLS policies later

Future Enhancement:

// Planned: User authentication with RLS
const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!, // User-level access
  { 
    auth: { 
      autoRefreshToken: true,
      persistSession: true 
    }
  }
)

2. Data Processing Transparency

Decision: Log all AI processing for transparency and debugging

Implementation:

console.log('Classification result:', {
  originalContent: body.content,
  finalCategory,
  cleanedContent,
  metadata,
  cost: calculatedCost
})

Rationale:

  • Debugging: Easy to trace AI decision-making
  • Transparency: Users can understand how their data is processed
  • Improvement: Logs help improve AI prompt engineering

Scalability Considerations

1. Database Indexing Strategy

Decision: Comprehensive indexing for expected query patterns

-- Primary access patterns
CREATE INDEX idx_notes_category ON notes(category);
CREATE INDEX idx_notes_created_at ON notes(created_at DESC);

-- Search patterns
CREATE INDEX idx_notes_content_fts ON notes USING gin(to_tsvector('english', content));
CREATE INDEX idx_notes_metadata ON notes USING gin(metadata);

-- Cost analysis patterns
CREATE INDEX idx_llm_costs_date_endpoint ON llm_costs(date, endpoint);

Rationale:

  • Query Performance: Sub-100ms response times for common queries
  • Scalability: Indexes support millions of notes
  • Cost Optimization: Efficient cost aggregation queries

2. API Rate Limiting Strategy

Decision: Rely on Vercel's built-in rate limiting with monitoring

Implementation:

// Monitor rate limits in API functions
export default async function handler(request: Request) {
  const startTime = Date.now()
  
  try {
    // API logic
    const result = await processRequest(request)
    
    // Log timing for monitoring
    console.log(`Request processed in ${Date.now() - startTime}ms`)
    
    return new Response(JSON.stringify(result))
  } catch (error) {
    console.error('API error:', error)
    return new Response('Internal server error', { status: 500 })
  }
}

Future Enhancements:

  • Custom rate limiting per user
  • Cost-based rate limiting
  • Request queuing for high load

πŸ“Š Performance & Quality

Performance Metrics

Current Performance Targets

  • Initial Load: < 2 seconds to interactive
  • Note Capture: < 5 seconds from input to classified display
  • Search Latency: < 2 seconds for search results
  • AI Classification: < 10 seconds for complex content

Lighthouse Scores (Target)

  • Performance: > 90
  • Accessibility: > 95
  • Best Practices: > 90
  • SEO: > 90

Bundle Analysis

Current Bundle Sizes

# Production build analysis
npm run build

# Bundle sizes (approximate)
dist/assets/index-[hash].js    449.66 kB β”‚ gzip: 131.00 kB
dist/assets/index-[hash].css    52.40 kB β”‚ gzip:   8.06 kB

Optimization Strategies

  1. Code Splitting: Vendor libraries in separate chunks
  2. Tree Shaking: Unused code elimination
  3. Dynamic Imports: Route-based code splitting
  4. Asset Optimization: Image compression and modern formats

Quality Assurance

Automated Testing

# Unit tests with coverage
pnpm test
# Coverage target: > 80%

# E2E tests
pnpm e2e
# Critical user flows covered

# Accessibility audit
pnpm a11y
# WCAG 2.1 AA compliance

# Performance audit
pnpm lh
# Lighthouse scores validation

Code Quality Standards

  • TypeScript: Strict mode enabled
  • ESLint: Zero warnings policy
  • Prettier: Consistent code formatting
  • Pre-commit hooks: Quality gates before commits

Monitoring and Observability

Cost Monitoring

// Daily cost aggregation
const costMetrics = {
  totalCost: number,        // Total AI usage cost
  requestCount: number,     // Number of AI requests
  averageCost: number,      // Cost per request
  endpointBreakdown: {      // Cost by endpoint
    classify: number,
    'extract-metadata': number,
    'enhance-reading': number
  }
}

Performance Monitoring

// Web Vitals tracking
const performanceMetrics = {
  CLS: number,    // Cumulative Layout Shift
  FID: number,    // First Input Delay
  FCP: number,    // First Contentful Paint
  LCP: number,    // Largest Contentful Paint
  TTFB: number    // Time to First Byte
}

πŸ”§ Troubleshooting

Common Issues and Solutions

1. Vercel Function Errors

Error: Function Runtimes must have a valid version

# Solution: Update Vercel CLI and check runtime config
npm install -g vercel@latest
vercel dev --debug

# Verify function configuration
export const config = { runtime: 'edge' }

Error: Function timeout after 30 seconds

// Solution: Optimize OpenAI calls and add timeout handling
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 25000) // 25s timeout

try {
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    signal: controller.signal,
    // ... other options
  })
} finally {
  clearTimeout(timeoutId)
}

2. Database Connection Issues

Error: relation "llm_costs" does not exist

# Solution: Run database migrations
pnpm db:migrate

# Or create the table manually
supabase sql --file supabase/migrations/004_add_llm_costs.sql

Error: Authentication failed

# Check environment variables
echo $SUPABASE_URL
echo $SUPABASE_SERVICE_ROLE_KEY

# Verify in Supabase dashboard
# Settings β†’ API β†’ Service Role Key

3. OpenAI API Issues

Error: OpenAI API error: 429 Rate Limit Exceeded

// Solution: Implement exponential backoff
async function callOpenAIWithRetry(messages, schema, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await callOpenAI(messages, schema)
    } catch (error) {
      if (error.status === 429 && i < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000))
        continue
      }
      throw error
    }
  }
}

Error: Invalid API key

# Check API key format
if [[ $OPENAI_KEY == sk-* ]]; then
  echo "API key format is correct"
else
  echo "Invalid API key format"
fi

4. Frontend Issues

Error: Module not found: @supabase/supabase-js

# Solution: Check package installation
pnpm install @supabase/supabase-js

# Verify import paths
import { createClient } from '@supabase/supabase-js'

Error: Environment variable VITE_SUPABASE_URL is not defined

# Solution: Check environment file
cat apps/web/.env

# Ensure variables have VITE_ prefix for frontend
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_key

Debug Mode Setup

Enable Comprehensive Logging

// Add to your environment
DEBUG=true

// In API functions
if (process.env.DEBUG) {
  console.log('Debug info:', {
    request: request.url,
    body: await request.clone().json(),
    timestamp: new Date().toISOString()
  })
}

Database Query Debugging

-- Enable query logging in Supabase
-- Dashboard β†’ Settings β†’ Database β†’ Query Performance

-- Check slow queries
SELECT query, mean_exec_time, calls 
FROM pg_stat_statements 
ORDER BY mean_exec_time DESC 
LIMIT 10;

-- Monitor active connections
SELECT state, count(*) 
FROM pg_stat_activity 
GROUP BY state;

Performance Debugging

Bundle Analysis

# Analyze bundle composition
npm install -g webpack-bundle-analyzer
pnpm build
npx webpack-bundle-analyzer apps/web/dist/assets/*.js

Network Debugging

// Add request timing to API functions
const startTime = performance.now()
// ... API logic
const endTime = performance.now()
console.log(`Request took ${endTime - startTime} milliseconds`)

Recovery Procedures

Database Recovery

# Restore from backup (Supabase)
supabase db reset --linked

# Or restore specific migration
supabase db reset --to 003_add_task_completions

Function Recovery

# Redeploy functions
vercel --force

# Or deploy specific function
vercel --prod --force

Data Recovery

-- Recover accidentally deleted notes
SELECT * FROM notes 
WHERE created_at > NOW() - INTERVAL '1 day'
ORDER BY created_at DESC;

-- Restore task completions
INSERT INTO task_completions (note_id, completed_at)
SELECT id, NOW() 
FROM notes 
WHERE category = 'task' 
AND metadata->>'completed' = 'true';

πŸ“ˆ Future Roadmap

Phase 2: User Authentication & Multi-tenancy

  • Supabase Auth integration
  • Row-level security (RLS) policies
  • User-specific data isolation
  • Social login options

Phase 3: Advanced Features

  • Real-time collaboration
  • File and image upload support
  • Voice note transcription
  • Advanced search with vector embeddings

Phase 4: Mobile Applications

  • React Native mobile app
  • Offline synchronization
  • Push notifications
  • Mobile-specific gestures

Phase 5: AI Enhancements

  • Custom AI models fine-tuned on user data
  • Smart suggestions and auto-completion
  • Sentiment analysis and mood tracking
  • Automated follow-up reminders

🀝 Contributing

Development Setup

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make changes and add tests
  4. Run quality checks: pnpm lint && pnpm test && pnpm build
  5. Commit changes: git commit -m 'Add amazing feature'
  6. Push to branch: git push origin feature/amazing-feature
  7. Submit a pull request

Code Style Guidelines

  • Use TypeScript for type safety
  • Follow ESLint and Prettier configurations
  • Add JSDoc comments for complex functions
  • Write tests for new features
  • Update documentation for API changes

πŸ“„ License

MIT License - see LICENSE file for details.


πŸ™ Acknowledgments

  • OpenAI for GPT-4o API and structured output capabilities
  • Supabase for backend-as-a-service platform
  • Vercel for edge function hosting and deployment
  • React Team for the excellent frontend framework
  • Tailwind CSS for utility-first styling
  • Lucide for beautiful, consistent icons

Last updated: December 2024 Version: 1.0.0 Documentation Status: Complete

About

Transform the chaos of scattered thoughts into organized intelligence

Resources

Stars

Watchers

Forks