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.
- 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
- Product Overview
- Architecture & Technical Details
- Features Documentation
- API Documentation
- Database Schema
- Setup & Development
- Deployment
- Business Logic & Technical Decisions
- Performance & Quality
- Troubleshooting
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.
- Cognitive Load Reduction: Users don't need to think about where to put notes
- Context Preservation: Rich metadata extraction preserves important context
- Rapid Retrieval: Intelligent search and categorization for instant access
- Universal Capture: One interface for all types of content
-
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
-
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
-
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
-
Knowledge Workers (10% of user base)
- Use Case: Personal knowledge management
- Pain Point: Information scattered across tools
- Value: Centralized intelligent organization
- 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
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β 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β
βββββββββββββββββββ
- 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
- 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
- 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
- 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
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
The core feature that automatically categorizes notes into predefined categories using OpenAI GPT-4o with structured JSON output for reliability.
- Tasks (β ) - Action items, todos, assignments with due date extraction
- Ideas (π‘) - Creative thoughts, innovations, brainstorming with title/summary
- Journal (π) - Personal reflections, daily logs, experiences
- Meetings (π₯) - Meeting notes with title, date, and time extraction
- Reading (π) - Quotes, research notes, article summaries with link enhancement
- Misc (π) - Everything else that doesn't fit other categories
// 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"
}
}- 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
- 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"
- 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"
- 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
- 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
// 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)
}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()
);- 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
- 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
// 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;- 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
- 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
- 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
- Link Highlighting: Extracted URLs prominently displayed
- Enhanced Titles: LLM-generated titles for better organization
- Summary Preview: Quick overview of reading content
- Dropdown Selection: Change note type with automatic metadata extraction
- Loading States: Visual feedback during reclassification
- Optimistic Updates: Immediate UI updates with rollback on error
- 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
- 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
- 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
All API endpoints currently use Supabase service role key for database access. User authentication is prepared but not implemented in the current version.
- Development:
http://localhost:3001/api/ - Production:
https://your-domain.vercel.app/api/
// Success Response
{
"success": true,
"data": { ... },
"metadata": { ... }
}
// Error Response
{
"error": "Error message",
"code": "ERROR_CODE",
"details": { ... }
}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_coststable - Triggers real-time updates via Supabase subscriptions
Error Cases:
400: Missing note_id or content500: OpenAI API error or database error
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
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
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
}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
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
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
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
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
The database uses PostgreSQL with Supabase extensions for UUID generation, real-time subscriptions, and full-text search capabilities.
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.
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 modifiedcategory: AI-classified or manually selected categorysource: Tracks whether classification was automatic or manualmetadata: JSONB field for category-specific datacreated_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"
}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
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;-- 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;-- 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);- Primary Queries: Category filtering, date ordering
- Search Queries: Full-text search on content
- Metadata Queries: GIN index for JSONB operations
- Cost Queries: Date and endpoint aggregations
-- 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;- 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
- 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
- Point-in-Time Recovery: 1-second granularity
- Daily Backups: Automated daily snapshots
- Geographic Replication: Multi-region backup storage
-- 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;- Node.js: 18.0.0 or higher
- PNPM: 8.0.0 or higher (recommended package manager)
- Git: For version control
- Supabase Account: For database and real-time features
- OpenAI API Key: For GPT-4o classification
- Vercel Account: For deployment (optional for development)
# Clone the repository
git clone <repository-url>
cd chat-notes
# Install dependencies
pnpm installCreate 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=developmentapps/web/.env:
# Frontend Supabase Configuration
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_keyEnvironment Variable Details:
SUPABASE_SERVICE_ROLE_KEY: Full access key for API functionsVITE_SUPABASE_ANON_KEY: Public key for frontend with RLS policiesOPENAI_KEY: API key with access to GPT-4o model
# 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# 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:54323For 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
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 locallyFull Development Stack:
pnpm dev:fullThis 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-
Start Development Environment
pnpm dev:full
-
Open Application
- Navigate to
http://localhost:5173 - The frontend automatically proxies API calls to the Vercel dev server
- Navigate to
-
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)
-
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
// tsconfig.json (strict configuration)
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}// 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
}
}
]# Runs automatically before commits
pnpm lint # ESLint check
pnpm test # Unit tests
pnpm build # TypeScript compilation check# Run unit tests
pnpm test
# Watch mode during development
pnpm test --watch
# Coverage report
pnpm test --coverageTest 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)
})
})# 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()
})# Start Vercel dev with debug mode
vercel dev --debug
# View function logs in real-time
# Logs appear in the terminal running vercel dev-- 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;// Enable debug logging
localStorage.setItem('debug', 'true')
// Component debugging
console.log('Notes state:', notes)
console.log('Classification status:', classifyingNotes)# 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// 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' })// 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}` }
})# 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- Vercel account connected to your GitHub repository
- Environment variables configured in Vercel dashboard
vercel.json:
{
"buildCommand": "pnpm build",
"outputDirectory": "apps/web/dist",
"installCommand": "pnpm install"
}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_idAutomatic Deployment:
# Push to main branch for automatic deployment
git push origin mainManual Deployment:
# Install Vercel CLI
npm install -g vercel
# Link project (first time only)
vercel link
# Deploy to production
vercel --prod
# Deploy to preview
vercel-
Add Domain in Vercel Dashboard
- Go to Project Settings β Domains
- Add your custom domain
- Configure DNS records as instructed
-
SSL Certificate
- Automatically provisioned by Vercel
- Uses Let's Encrypt for free SSL
-
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)
# 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_keyDocker 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}// 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'
}
})// 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"
}
]
}
]
}// 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"
}
}
]
}// Enable in production
import { Analytics } from '@vercel/analytics/react'
function App() {
return (
<>
<Router />
<Analytics />
</>
)
}// 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)// 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
})// 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' }
})
}// Check database connectivity
async function checkDatabase() {
try {
const { data, error } = await supabase
.from('notes')
.select('count(*)', { count: 'exact' })
return !error
} catch {
return false
}
}// 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
}
}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
}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 JSONDecision: 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';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;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
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
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
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
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
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
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
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
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
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
}
}
)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
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
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
- 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
- Performance: > 90
- Accessibility: > 95
- Best Practices: > 90
- SEO: > 90
# 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- Code Splitting: Vendor libraries in separate chunks
- Tree Shaking: Unused code elimination
- Dynamic Imports: Route-based code splitting
- Asset Optimization: Image compression and modern formats
# 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- TypeScript: Strict mode enabled
- ESLint: Zero warnings policy
- Prettier: Consistent code formatting
- Pre-commit hooks: Quality gates before commits
// 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
}
}// 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
}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)
}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.sqlError: Authentication failed
# Check environment variables
echo $SUPABASE_URL
echo $SUPABASE_SERVICE_ROLE_KEY
# Verify in Supabase dashboard
# Settings β API β Service Role KeyError: 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"
fiError: 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// 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()
})
}-- 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;# Analyze bundle composition
npm install -g webpack-bundle-analyzer
pnpm build
npx webpack-bundle-analyzer apps/web/dist/assets/*.js// Add request timing to API functions
const startTime = performance.now()
// ... API logic
const endTime = performance.now()
console.log(`Request took ${endTime - startTime} milliseconds`)# Restore from backup (Supabase)
supabase db reset --linked
# Or restore specific migration
supabase db reset --to 003_add_task_completions# Redeploy functions
vercel --force
# Or deploy specific function
vercel --prod --force-- 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';- Supabase Auth integration
- Row-level security (RLS) policies
- User-specific data isolation
- Social login options
- Real-time collaboration
- File and image upload support
- Voice note transcription
- Advanced search with vector embeddings
- React Native mobile app
- Offline synchronization
- Push notifications
- Mobile-specific gestures
- Custom AI models fine-tuned on user data
- Smart suggestions and auto-completion
- Sentiment analysis and mood tracking
- Automated follow-up reminders
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make changes and add tests
- Run quality checks:
pnpm lint && pnpm test && pnpm build - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Submit a pull request
- 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
MIT License - see LICENSE file for details.
- 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