Back to Projects

Second Brain

AI-powered note-taking app for STEM/CS students with semantic search and smart organization

5 min read
1423 words
nextjstypescriptreactaiprismapostgresqlshadcn/uireact queryTipTapclerk
Second Brain
Second Brain

Overview

I built this because I was tired of losing my notes across random text files and forgotten Google Docs. Second Brain is basically my solution to the note-taking mess that every CS student deals with - pasting code snippets, saving algorithm explanations, keeping track of stuff you learned.

The main idea: paste anything, and AI figures out where it should go and what tags make sense. No more "misc notes" folder with 500 files.

What It Does
  • Paste random content → AI organizes it automatically - Search by meaning, not just keywords - Rich text editor with code blocks, LaTeX math, syntax highlighting

Built it mainly for CS students, bootcamp people, and anyone learning to code who's drowning in notes.

5s
Save
300ms
Search
2000+
Notes Supported
30+
Code Languages

Interface Preview

Second Brain main dashboard interface
Clean interface with sidebar navigation, all modals clickable with keyboard shortcuts

Tech Stack

Core Framework

  • Next.js 15 with App Router for server and client components
  • TypeScript in strict mode for type safety
  • Tailwind CSS for styling
  • SCSS for TipTap styling (following TipTap patterns)
  • shadcn/ui for ui components

Rich Text Editor

  • Tiptap Editor for rich text editing with custom components and features
  • lowlight for syntax highlighting (30+ languages)
  • LaTeX math support for equations
  • Code blocks
  • Custom AI Powered Formatter using OpenAI SDK

State Management

  • React Query for server state
  • Zustand for global client UI state
  • useReducer for complex local state
  • useState for simple local state

Project Structure

src/
├── actions/ # Server actions (API layer)
│ ├── aiActions.ts # AI features
│ ├── noteActions.ts # Note CRUD
│ ├── folderActions.ts # Folder management
│ └── tagActions.ts # Tag operations
├── app/ # Next.js App Router
│ ├── (auth)/ # Auth pages
│ ├── (main)/ # Main app
│ │ ├── layout.tsx # Sidebar + header
│ │ └── notes/ # Notes routes
│ │ ├── page.tsx
│ │ ├── new/
│ │ └── [id]/
│ └── api/webhooks/ # Clerk webhooks
├── components/
│ ├── editor/ # Note editor
│ ├── modals/ # Dialogs
│ │ ├── QuickCaptureModal.tsx
│ │ └── SearchModal.tsx
│ ├── sidebar/ # Navigation
│ └── ui/ # shadcn components
├── hooks/ # React Query hooks
│ ├── use-notes.ts
│ ├── use-folders.ts
│ └── use-semantic-search.ts
├── lib/ # Utilities
│ ├── prisma.ts
│ ├── queryClient.ts
│ └── auth.ts
├── services/ # External services
│ └── ai/
│ ├── content-analyzer.ts
│ ├── semantic-search.ts
│ └── prompts/
└── schemas/ # Zod validation
├── noteSchemas.ts
└── folderSchemas.ts

Database Architecture

Core Features

Rich Text Editor

Full-featured Tiptap editor with: - Text Formatting: Bold, italic, underline, strikethrough, code, highlight - Headings: H1-H6 with visual hierarchy - Lists: Bullet, numbered, task lists with checkboxes - Code Blocks: Syntax highlighting for 30+ languages via lowlight - Math Support: Inline and block LaTeX equations - Auto-Save: Debounced saving every 2 seconds with visual indicators

Code block with syntax highlighting

Syntax highlighting for 30+ languages

LaTeX math equations

Inline and block LaTeX support

Smart Folder System

Organize notes in a 3-level hierarchy with: - Custom Colors: 6 options (Gray, Red, Green, Blue, Yellow, Purple) - Special Inbox: Auto-created for unorganized content - Validation: Prevents circular references and duplicate names - Note Organization: Move notes between folders

Folder hierarchy with color coding
3-level folder structure with custom colors

Quick Capture (AI-Powered)

Paste anything and AI organizes it automatically: - Smart Analysis: Suggests title, folder, and tags (3-5 seconds) - Folder Matching: Prefers existing folders, creates new if needed - Tag Intelligence: Reuses existing tags + adds specific new ones - Content Formatting: Converts plain text to rich HTML

AI-Powered Content Formatter

Transform plain text into beautifully formatted rich text: - Smart Structure Detection: Automatically detects headings, lists, and code blocks - Code Syntax Highlighting: Preserves code formatting with proper language detection - Math Support: Converts LaTeX notation to rendered equations - Full Editor Support: Works seamlessly with all Tiptap features - Fast Processing: Formats content in 2-3 seconds using OpenAI GPT-4o-mini

Semantic Search

AI-powered search that understands meaning: - Natural Language: Search like "how to sort arrays efficiently" - Context-Aware: Finds related notes even without exact keywords - Fast Results: 300-500ms after first search (embeddings cached) - Similarity Scores: See how relevant each result is (0-100%)

How Quick Capture Works

Quick Capture uses AI to analyze pasted content and automatically organize it.

Quick Capture modal

Quick Capture modal

Ai Analysis results

Ai Analysis results

Semantic search first time

First search - generates embeddings (takes 2-3s)

Semantic search cached

Second search - uses cached embeddings (300ms)

The search uses "lazy embeddings" - basically I don't generate them until you actually search for something.

Why This Approach?
  • Creating notes is instant (no embedding generation) - First search takes 2-3 seconds (has to generate for all notes) - Every search after that is 300ms - Saves a ton of money on API calls - Only regenerates for notes you've edited

Embedding Freshness Logic

The system intelligently decides when to regenerate embeddings:

  • No embedding exists - First time searching this note
  • Missing timestamp - Corrupted metadata
  • Content updated - contentUpdatedAt > embeddingUpdatedAt

Challenges & Solutions

Challenge 1: Managing Form State

Problem

The note editor has like 5 different fields that all affect each other - title, content, folder, tags, favorite status. I started with a bunch of useState hooks and it turned into a mess of sync issues.

Solution

Switched to useReducer. Now I have one source of truth for the form state and typed actions like SET_TITLE, ADD_TAG, TOGGLE_FAVORITE. Way cleaner and actually testable.

Result

Adding new fields is easy now, and I stopped getting weird state bugs where the UI didn't match the data.

Challenge 2: Search Was Too Slow

Problem

I was generating embeddings every time someone created a note. This meant 2-3 second waits after every save. Terrible UX and was costing me money in API calls.

Solution

Switched to lazy embeddings - only generate when someone actually searches. Cache them in the database and only regenerate if the content changed. First search is slower but every search after is instant.

Result

Note creation is instant now. First search takes a few seconds but then it's 300ms. Cut my OpenAI bill by 90%.

Challenge 3: Matching AI Folder Suggestions

Problem

The AI would suggest paths like 'Algorithms/Sorting' but my database stores folders flat with just a parentId reference. Had to figure out how to match AI suggestions to actual folder structures.

Solution

Built a recursive function that walks up the parent chain to build full paths for every folder. Then match the AI suggestion case-insensitively and return the deepest (most specific) match.

Result

Quick Capture now pre-fills the right folders. If you have 'Algorithms/Sorting' and paste sorting code, it actually finds it.

Challenge 4: Auto-Save Without Breaking Things

Problem

Saving on every keystroke = too many API calls. Saving too rarely = risk losing work. And when people type fast, you get race conditions where saves overlap.

Solution

2-second debounced auto-save. Only saves if the note exists already. Cancels pending saves if the user keeps typing. Shows 'Last saved at...' so people know it worked.

Result

Feels smooth, doesn't spam the API, and I haven't lost any data yet.

Second Brain | Shalev Asor