# Project export: DeepDive

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: Turn any article or video into trusted insight instantly. Our browser co‑pilot distills what matters, spots bias, and lets you quiz the source in real time, so every browsing session has reason.
- Devpost: https://devpost.com/software/deepdive-lv4nyu
- GitHub: https://github.com/n8liu/calhacks12
- Video: https://www.youtube.com/embed/C3D5Z7ZclM4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — nathanliu528@gmail.com (7 commits), Tyler Wu (5 commits)

## Devpost submission (written by the team)

### Inspiration

We’re drowning in tabs that shout headlines without context. The spark came from those midnight doomscrolls where we wished for a calm, trustworthy co‑pilot to cut through noise, surface what matters, and warn us when content felt dubious. DeepDive was born to make critical reading as effortless as streaming a playlist.

### What it does

DeepDive is a Chrome companion that lands in the corner of any article or YouTube video. One click delivers: A focused summary and key claims. A credibility check that weighs source, author, and tone. A persistent memory of what you’ve already read so it can surface related content later. A chat interface that answers questions grounded in the source—no hallucinated facts.

### How we built it

The frontend is a Manifest V3 extension: contentScript.js injects the sidebar UI, background.js brokers messages to the backend, and injectUI.css keeps the experience lightweight and unobtrusive. The backend is an Express server that orchestrates multiple LLMs: Gemini 2.5 Flash handles fast summarization and topic extraction. Claude 3.5 Sonnet performs deeper credibility reasoning and interactive chat. A Letta-hosted memory agent stores article embeddings so we can retrieve related reading later. Under the hood we approximate token limits with a simple cap ( T \leq \frac{\text{chars}}{4} ) to stay within API quotas, cache results by URL hash, and keep in-memory conversations for responsive chat.

### Challenges we ran into

LLM juggling: Balancing Gemini’s speed with Claude’s rigor required fallback logic, JSON parsing guards, and careful prompt design. Credibility sourcing: Getting Claude to cite author research consistently meant iterating on prompt structure and mapping [1]-style references back to usable URLs in the UI. Extension UX: Making the floating entry point draggable without accidental clicks took a custom pointer-state handler. Memory sync: Aligning local in-memory storage with the Letta agent’s persistent memory introduced async race conditions that we resolved with defensive checks.

### Accomplishments we're proud of

A seamless sidebar that feels native across wildly different sites. Robust caching and fallback paths so users rarely feel latency. A credibility experience that goes beyond color badges by surfacing transparent reasoning. Tight integration with Letta to keep a personalized knowledge graph of past reads.

### What we learned

Prompt architecture matters as much as model choice; small wording tweaks unlocked reliable JSON and grounded citations. Chrome extension ergonomics are equal parts CSS finesse and DOM diplomacy—especially when injecting UI into unpredictable pages. Multi-model orchestration benefits from defensive programming: Promise.allSettled, structured logging, and graceful degradation keep the product resilient.

### What's next

Expand memory with embeddings persisted in a real datastore so cross-session insights survive restarts. Add collaborative notes so teams can annotate and share credibility scores. Introduce automatic fact spot-checking against trusted datasets. Ship production-ready icons, a settings panel, and polished onboarding to turn DeepDive into a daily reading habit.

## README (from the GitHub repository)

# DeepDive 🧠

**Your AI co-pilot for reading and watching content online.**

DeepDive is a Chrome extension that provides intelligent summarization, credibility analysis, fact-checking, and interactive Q&A for any article or YouTube video you're viewing.

---

## ✨ Features

- **🎯 Smart Summaries** - Get concise TL;DR and key takeaways instantly
- **🔍 Credibility Analysis** - Trust scores, bias detection, and source evaluation
- **✅ Real-Time Fact Checking** - Verify claims with Google Search-powered verification
- **👤 Author Research** - Automatic author background and expertise analysis
- **🔗 Content Connections** - Discover links between articles you've read
- **💬 Interactive Chat** - Ask questions about any article or video
- **📚 Reading History** - Track and revisit analyzed content
- **🌙 Dark Mode** - Comfortable reading in any environment

---

## 🚀 Quick Start

### Prerequisites

- Node.js 18+ ([Download](https://nodejs.org/))
- Chrome browser
- API keys (get them free):
  - **Claude API key** from [Anthropic Console](https://console.anthropic.com/)
  - **Gemini API key** from [Google AI Studio](https://makersuite.google.com/app/apikey)

### Installation (3 steps)

#### 1. Clone and configure

```bash
git clone <your-repo-url>
cd calhacks12
```

Edit `backend/.env` and add your API keys:

```env
ANTHROPIC_API_KEY=sk-ant-your-key-here
GOOGLE_API_KEY=AIzaSy-your-key-here
PORT=3000
```

#### 2. Start the backend

```bash
cd backend
npm install
npm start
```

You should see: `🚀 DeepDive backend running on http://localhost:3000`

#### 3. Load the Chrome extension

1. Open Chrome and go to `chrome://extensions/`
2. Enable **Developer mode** (toggle in top-right)
3. Click **"Load unpacked"**
4. Select the `frontend` folder from this project

Done! 🎉

### Try it out

1. Visit any article (try [nytimes.com](https://www.nytimes.com))
2. Click the **DD** button in the top-right corner of the page
3. Explore the Summary, Credibility, Connections, and Chat tabs!

---

## 📖 How It Works

### Architecture

```
┌─────────────────┐
│ Chrome Extension│
│   (Frontend)    │
└────────┬────────┘
         │
         ↓
┌─────────────────┐
│  Express Server │
│    (Backend)    │
└────────┬────────┘
         │
    ┌────┴────┐
    ↓         ↓
┌────────┐ ┌────────┐
│ Claude │ │ Gemini │
│  API   │ │  API   │
└────────┘ └────────┘
```

### Features in Detail

#### 📊 Credibility Analysis

Multi-tier credibility scoring:
- **Website Analysis** - Source reputation and editorial standards
- **Author Analysis** - Expert background research with web sources
- **Content Analysis** - Evidence quality, tone, and logical reasoning
- **Fact Checking** - Real-time claim verification with Google Search

#### 🔗 Connections Tab

Automatically finds connections between articles you've read:
- **Shared Topics** - Articles covering similar subjects
- **Same Authors** - Track content from the same writer
- **Reading History** - Browse your past 20 analyzed articles

#### 💬 Smart Chat

Ask questions about the content:
- "What's the main argument?"
- "Is this opinion or fact?"
- "What's missing from this analysis?"
- Adjustable response length (short, default, detailed, or auto)

---

## 🛠️ Technology Stack

### Frontend
- Vanilla JavaScript (no framework dependencies)
- Chrome Extension Manifest V3
- Injected UI with dark mode support

### Backend
- Node.js + Express
- Claude 3.5 Sonnet (credibility analysis, chat)
- Gemini 2.5 Flash (summaries, fact-checking, author research)
- In-memory storage (upgradeable to database)

### AI Features
- **Google Search Grounding** - Real-time web research via Gemini
- **Streaming responses** - See analysis appear in real-time
- **Parallel processing** - Fast analysis with concurrent API calls

---

## 📁 Project Structure

```
calhacks12/
├── frontend/              # Chrome extension
│   ├── manifest.json      # Extension config
│   ├── background.js      # Service worker (API communication)
│   ├── contentScript.js   # Content extraction + UI injection
│   ├── injectUI.css       # Styles (light/dark mode)
│   ├── popup.html         # Extension popup
│   └── assets/            # Icons
│
├── backend/               # Express API server
│   ├── server.js          # Main server (analyze, chat, connections)
│   ├── package.json       # Dependencies
│   └── .env              # API keys (DO NOT COMMIT!)
│
└── README.md             # This file
```

---

## 🔧 Development

### Backend development mode

```bash
cd backend
npm run dev  # Auto-restarts on file changes
```

### Making changes

**Frontend:**
- Edit files in `frontend/`
- Go to `chrome://extensions/`
- Click refresh icon on DeepDive
- Reload the webpage

**Backend:**
- Edit `backend/server.js`
- Server auto-restarts (if using `npm run dev`)

---

## 🐛 Troubleshooting

### Backend won't start

Check Node.js version:
```bash
node --version  # Should be 18.x or higher
```

Check if port 3000 is in use:
```bash
lsof -i :3000
kill -9 <PID>  # If needed
```

### Extension not working

Verify backend is running:
```bash
curl http://localhost:3000/health
# Should return: {"status":"ok"}
```

Check browser console (F12 → Console) for errors

### API key issues

- Claude keys start with `sk-ant-`
- Gemini keys start with `AIzaSy`
- Remove any extra spaces from `.env` file
- Verify keys are valid in respective consoles

---

## 📚 API Endpoints

### `POST /analyze/stream`

Analyze content with streaming response.

**Request:**
```json
{
  "url": "https://example.com",
  "content": "Article text...",
  "type": "article",
  "metadata": {
    "title": "Article Title",
    "author": "Author Name"
  }
}
```

**Response:** Server-Sent Events stream with summary, credibility, and fact-check data.

### `POST /chat`

Chat about analyzed content.

**Request:**
```json
{
  "conversation_id": "uuid",
  "user_message": "What's the main point?",
  "response_length": "auto"  // auto, short, default, detailed
}
```

**Response:**
```json
{
  "assistant_message": "The main point is..."
}
```

### `GET /history`

Get analyzed article history.

**Response:**
```json
{
  "articles": [...],
  "total": 42
}
```

### `GET /connections/:urlHash`

Get connections for an article.

**Response:**
```json
{
  "connections": [...],
  "totalArticles": 10
}
```

---

## 🎯 Future Enhancements

- [ ] Persistent database storage (PostgreSQL/MongoDB)
- [ ] User authentication and multi-user support
- [ ] YouTube transcript extraction and analysis
- [ ] Export summaries to Notion, Obsidian, etc.
- [ ] Browser extension for Firefox and Safari
- [ ] Mobile app
- [ ] Collaborative reading lists
- [ ] Advanced analytics and reading patterns

---

## 🤝 Contributing

Contributions are welcome! This project was built for Cal Hacks 12.0.

---

## 📄 License

MIT License - Feel free to use this for your own projects!

---

## 🙏 Acknowledgments

Built with:
- [Anthropic Claude](https://www.anthropic.com/) for reasoning and analysis
- [Google Gemini](https://deepmind.google/technologies/gemini/) for summaries and search
- Love for better internet literacy ❤️

---

**Happy reading! 📚✨**


## Detected evidence (automated analysis)

Indexed codebase: 13 recognized source files, 154 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- Google Gemini (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (18 of 18)

```
.gitignore
backend/.gitignore
backend/package.json
backend/server.js
docs/AUTHOR_RESEARCH.md
docs/CONNECTIONS.md
docs/FACT_CHECKING.md
docs/README.md
frontend/assets/README.md
frontend/background.js
frontend/contentScript.js
frontend/injectUI.css
frontend/manifest.json
frontend/popup.html
frontend/popup.js
package.json
README.md
start.sh
```

### Dependencies

- backend/package.json: @anthropic-ai/sdk@^0.27.0, @google/generative-ai@^0.1.0, axios@^1.12.2, cors@^2.8.5, dotenv@^16.3.1, express@^4.18.2, nodemon@^3.0.1, uuid@^9.0.1

### Recent commits (newest first)

- added how rating works
- condensed markdown files
- updated connections and added live response for a faster sumamrize
- updated connections tab
- added dark mode
- Updated UI and accessbility features to share tab
- Updated name to DeepDive
- updated frontend ui
- Added Outside Fact Checking
- added sources
- added features, loading, and links
- added sources cited and author
- added memory
- added memory
- fixed image errors
- first test

## Key source files (fetched from GitHub, selected and truncated for size)

### docs/AUTHOR_RESEARCH.md

```markdown
# Author Research Feature

## Overview

DeepDive automatically researches article authors using **Gemini 2.5 Flash with Google Search Grounding** to provide comprehensive background information and credibility assessment.

## How It Works

### 1. Automatic Author Detection
When you analyze an article, the system:
- Extracts the author name from the page metadata
- Identifies the publication/source
- Triggers real-time author research

### 2. Google Search Integration
Uses Gemini's built-in Google Search tool to research:
- Author credentials and expertise
- Professional background
- Published works and reputation
- Potential biases or conflicts of interest

### 3. Structured Analysis
Returns:
```json
{
  "expertise": "Area of specialization and qualifications",
  "background": "Professional history and education",
  "reputation_signals": "Awards, recognition, peer standing",
  "potential_bias": "Known affiliations or viewpoints"
}
```

## Example Output

### In the UI:
```
👤 Author Analysis
Jane Smith

Expertise
Pulitzer Prize-winning political correspondent with 15+ years
experience [1][3]

Background
Senior journalist at NYT, formerly at Washington Post.
Columbia University graduate in Political Science [1][2]

Reputation Signals
Highly regarded investigative journalist, two Pulitzer Prizes [3]

Potential Bias
Generally centrist reporting, no major conflicts of interest
detected [1]

📚 Sources
[1] The New York Times - Author Bio
[2] Wikipedia - Jane Smith (journalist)
[3] Pulitzer Prizes - Winners
```

## Technical Details

### API Call
```javascript
const model = genAI.getGenerativeModel({
  model: 'gemini-2.5-flash',
  tools: [{
    googleSearch: {}  // Enable Google Search grounding
  }]
});

const result = await model.generateContent({
  contents: [{
    role: 'user',
    parts: [{
      text: `Research this author: ${authorName} from ${source}...`
    }]
  }]
});
```

### Source Extraction
```javascript
const groundingMetadata = response.candidates?.[0]?.groundingMetadata;
const sources = groundingMetadata?.groundingChunks
  .filter(chunk => chunk.web)
  .map(chunk => ({
    title: chunk.web.title,
    url: chunk.web.uri,
    index: i + 1
  }));
```

## Benefits

✅ **No Extra API Keys** - Uses your existing Gemini key
✅ **Real-Time Data** - Fresh information from Google Search
✅ **Source Citations** - Every claim is backed by URLs
✅ **Automatic** - Happens during article analysis
✅ **Transparent** - See exactly where info comes from

## Fallback Behavior

If author research fails (no author, API error, etc.):
- Displays "No author information available"
- Credibility analysis continues based on content
- System remains fully functional

## Cost

Author research adds minimal cost:
- ~500 tokens for query
- ~1000-2000 tokens for response
- **~$0.01 per author search** at current Gemini pricing

## Configuration

No extra configuration needed! Just ensure your `.env` has:

```env
GOOGLE_API_KEY=AIzaSy-your-key-here
```

---

**Powered by Ge
[truncated — 49 more characters]
```

### docs/FACT_CHECKING.md

```markdown
# Fact-Checking System

## Overview

DeepDive includes real-time fact-checking powered by **Gemini 2.5 Flash with Google Search Grounding**. It verifies key factual claims from articles against live web sources.

## Features

✅ **Real-Time Web Search** - Searches the web NOW, not cached data
✅ **Multiple Claim Verification** - Checks 3-5 key claims per article
✅ **Source Attribution** - Every verification includes URLs
✅ **Transparency** - Shows search queries used
✅ **Reliability Scores** - 0-100% confidence for each claim

## How It Works

### 1. Claim Extraction
The system identifies verifiable factual claims like:
- Statistics and numbers
- Dates and timelines
- Specific events
- Attributable quotes
- Scientific facts

### 2. Real-Time Verification
For each claim:
- Generates targeted search queries
- **Searches Google in real-time** (not training data)
- Compares claim against found sources
- Assigns verification status

### 3. Verification Statuses

| Status | Icon | Meaning |
|--------|------|---------|
| Confirmed | ✅ | Supported by reliable sources |
| Partially Confirmed | ⚠️ | Mixed support or needs context |
| Uncertain | ❓ | Insufficient information |
| Contradicted | ❌ | Conflicts with reliable sources |

## Example

### Article Claim:
> "The unemployment rate decreased to 3.5% in 2024"

### Fact Check Result:
```
✅ Confirmed

Claim: The unemployment rate decreased to 3.5% in 2024

Assessment: Verified by Bureau of Labor Statistics data.
The unemployment rate did reach 3.5% in Q1 2024 according
to official government sources.

Reliability Score: 95%

Search Queries:
• unemployment rate 2024 3.5% BLS
• US unemployment rate 2024 verified

Sources:
[1] Bureau of Labor Statistics - Employment Situation
[2] US Department of Labor - Economic Data
```

## Technical Implementation

### Gemini Configuration
```javascript
const model = genAI.getGenerativeModel({
  model: 'gemini-2.5-flash',
  tools: [{
    googleSearch: {}
  }]
});

// Prompt forces real-time search
const prompt = `You MUST use the googleSearch tool to search
the web in real-time to verify claims from this article.

DO NOT rely on training data - search the web NOW...`;
```

### Response Format
```json
{
  "claims": [
    {
      "claim": "The factual claim being verified",
      "status": "Confirmed",
      "assessment": "Explanation of verification",
      "reliability": 0.95,
      "search_queries": ["query 1", "query 2"]
    }
  ],
  "sources": [
    {
      "index": 1,
      "title": "Source Title",
      "url": "https://...",
      "snippet": "Relevant excerpt"
    }
  ]
}
```

## UI Display

### Credibility Tab
Shows after "Overall Assessment":

```
📋 Fact Check Report
Verified key claims against real-time web sources using
Gemini 2.5 Flash with Google Search.

[1] ✅ Confirmed
Claim: The unemployment rate decreased to 3.5% in 2024
Verified by Bureau of Labor Statistics data...
Reliability score: 95%

[2] ⚠️ Partially Confirmed
Claim: GDP growth exceeded 4% last quarte
[truncated — 1544 more characters]
```

### package.json

```
{
  "name": "deepdive",
  "version": "0.1.0",
  "description": "AI co-pilot for whatever you're reading/watching",
  "private": true,
  "scripts": {
    "backend": "cd backend && npm start",
    "backend:dev": "cd backend && npm run dev",
    "install:backend": "cd backend && npm install",
    "install:all": "npm run install:backend"
  },
  "keywords": ["ai", "chrome-extension", "summarization", "credibility-check"],
  "author": "",
  "license": "MIT"
}


```

### backend/package.json

```
{
  "name": "deepdive-backend",
  "version": "0.1.0",
  "description": "Backend API for DeepDive Chrome extension",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "keywords": [
    "ai",
    "summarization",
    "credibility"
  ],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@anthropic-ai/sdk": "^0.27.0",
    "@google/generative-ai": "^0.1.0",
    "axios": "^1.12.2",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "uuid": "^9.0.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

```

### backend/server.js

```javascript
import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import { v4 as uuidv4 } from 'uuid';
import Anthropic from '@anthropic-ai/sdk';
import { GoogleGenerativeAI } from '@google/generative-ai';
import axios from 'axios';

dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(cors());
app.use(express.json({ limit: '10mb' }));

// Initialize AI clients
const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY);

// Letta AI Configuration
const LETTA_API_KEY = process.env.LETTA_API_KEY;
const LETTA_BASE_URL = process.env.LETTA_BASE_URL || 'https://api.letta.com';
let lettaAgentId = null;

// In-memory storage (replace with database in production)
const conversations = new Map();
const cache = new Map();

// Article memory system (enhanced with Letta)
const articleMemory = new Map(); // url -> article data
const articleConnections = new Map(); // articleId -> [related article IDs]
const topicIndex = new Map(); // topic -> [article IDs]

// Helper: Generate cache key from URL
function getCacheKey(url) {
  return Buffer.from(url).toString('base64');
}

// Helper: Clean and truncate content
function cleanContent(content, maxTokens = 8000) {
  // Simple token approximation: ~4 chars per token
  const maxChars = maxTokens * 4;
  return content.substring(0, maxChars).trim();
}

// Letta AI Helper: Initialize or get agent
async function initializeLettaAgent() {
  if (!LETTA_API_KEY || LETTA_API_KEY === 'your_letta_api_key_here') {
    return null;
  }

  try {
    // Check if we already have an agent
    if (lettaAgentId) {
      return lettaAgentId;
    }

    // Create or retrieve DeepDive agent
    const response = await axios.post(
      `${LETTA_BASE_URL}/v1/agents`,
      {
        name: 'DeepDive-Memory-Agent',
        persona: 'You are a memory system for DeepDive. You remember articles users have read and help them discover connections between content.',
        human: 'A user reading articles and seeking to understand connections between information.',
        system: 'Store and retrieve article memories. Find connections between articles based on topics, authors, and themes.'
      },
      {
        headers: {
          'Authorization': `Bearer ${LETTA_API_KEY}`,
          'Content-Type': 'application/json'
        }
      }
    );

    lettaAgentId = response.data.id;
    console.log('✅ Letta agent initialized:', lettaAgentId);
    return lettaAgentId;

  } catch (error) {
    if (error.response?.status === 409) {
      // Agent already exists, retrieve it
      try {
        const listResponse = await axios.get(`${LETTA_BASE_URL}/v1/agents`, {
          headers: { 'Authorization': `Bearer ${LETTA_API_KEY}` }
        });
        
        const agent = listResponse.data.find(a => a.name === 'DeepDive-Memory-Agent');
        if (agent) {
          lettaAgentId = agent.id;
          console.log('✅ Letta agent retrieved:', lettaAgentId);
          return lettaAgentId;
        }
      } catch (listError) {
        console.error('Error retrieving Letta agent:', listError.message);
      }
    }
    
    console.error('Letta agent initialization error:', error.message);
    return null;
  }
}

// Letta AI Helper: Store article in Letta memory
async function storeLettaMemory(articleData) {
  const agentId = await initializeLettaAgent();
  if (!agentId) return null;

  try {
    const memoryMessage = `Remember this article:
Title: "${articleData.title}"
URL: ${articleData.url}
Author: ${articleData.author}
Source: ${articleData.source}
Topics: ${articleData.topics.join(', ')}
Summary: ${articleData.summary}
Credibility: ${articleData.credibility_label} (${Math.round(articleData.credibility_score * 100)}%)
Date Read: ${articleData.analyzed_at}

Key Points:
${articleData.bullets.map((b, i) => `${i + 1}. ${b}`).join('\n')}`;

    const response = await axios.post(
      `${LETTA_BASE_URL}/v1/agents/${agentId}/messages`,
      {
        messages: [{
          role: 'user',
          content: memoryMessage
        }],
        stream: false
      },
      {
        headers: {
          'Authorization': `Bearer ${LETTA_API_KEY}`,
          'Content-Type': 'application/json'
        }
      }
    );

    console.log('✅ Article stored in Letta memory');
    return response.data;

  } catch (error) {
    console.error('Letta memory storage error:', error.message);
    return null;
  }
}

// Letta AI Helper: Query connections from Letta
async function queryLettaConnections(currentArticle) {
  const agentId = await initializeLettaAgent();
  if (!agentId) return null;

  try {
    const query = `What articles have I read that are related to this one?
Title: "${currentArticle.title}"
Topics: ${currentArticle.topics.join(', ')}
Author: ${currentArticle.author}

List up to 5 related articles with explanation of the connection. Format as JSON array.`;

    const response = await axios.post(
      `${LETTA_BASE_URL}/v1/agents/${agentId}/messages`,
      {
        messages: [{
          role: 'user',
          content: query
        }],
        stream: false
      },
      {
        headers: {
          'Authorization': `Bearer ${LETTA_API_KEY}`,
          'Content-Type': 'application/json'
        }
      }
    );

    // Parse Letta's response for connections
    const responseText = response.data.messages?.[0]?.content || '';
    console.log('✅ Retrieved connections from Letta');
    return responseText;

  } catch (error) {
    console.error('Letta query error:', error.message);
    return null;
  }
}

// POST /analyze/stream - Analyze content with streaming summary
app.post('/analyze/stream', async (req, res) => {
  try {
    const { url, content, type, metadata } = req.body;

    if (!content || !url) {
      return res.status(400).json({ error: 'Missing required fields: url, content' });
    }

    console.log(`Analyzing (streaming) ${type || 
[truncated — 41255 more characters]
```

### start.sh

```shell
#!/bin/bash

# DeepDive startup script

echo "🚀 Starting DeepDive..."
echo ""

# Check if Node.js is installed
if ! command -v node &> /dev/null; then
    echo "❌ Node.js is not installed. Please install Node.js 18+ from https://nodejs.org/"
    exit 1
fi

echo "✅ Node.js version: $(node --version)"

# Check if dependencies are installed
if [ ! -d "backend/node_modules" ]; then
    echo "📦 Installing backend dependencies..."
    cd backend && npm install && cd ..
    echo ""
fi

# Check if .env exists
if [ ! -f "backend/.env" ]; then
    echo "⚠️  Warning: backend/.env not found!"
    echo "   Please create it from backend/.env.example and add your API keys:"
    echo "   - ANTHROPIC_API_KEY (from https://console.anthropic.com/)"
    echo "   - GOOGLE_API_KEY (from https://makersuite.google.com/app/apikey)"
    echo ""
    read -p "Press Enter to continue anyway, or Ctrl+C to exit..."
fi

# Start backend
echo ""
echo "🔥 Starting backend server..."
echo "   Running on http://localhost:3000"
echo ""
echo "📝 Next steps:"
echo "   1. Load the Chrome extension from the 'frontend' folder"
echo "   2. Visit any webpage and click the 🧠 icon"
echo ""
echo "Press Ctrl+C to stop the server"
echo "───────────────────────────────────────────────────────────"
echo ""

cd backend && npm start


```

### frontend/popup.js

```javascript
// Popup script for extension icon click
console.log('DeepDive popup loaded');

// You can add analytics or settings UI here in the future


```

### frontend/popup.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>DeepDive</title>
  <style>
    body {
      width: 320px;
      padding: 20px;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
      margin: 0;
    }
    h1 {
      font-size: 20px;
      margin: 0 0 16px 0;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
    p {
      font-size: 14px;
      line-height: 1.6;
      color: #374151;
      margin: 0 0 16px 0;
    }
    .info {
      background: #f3f4f6;
      padding: 12px;
      border-radius: 8px;
      font-size: 13px;
      color: #6b7280;
    }
    .status {
      padding: 8px 12px;
      background: #dcfce7;
      color: #166534;
      border-radius: 6px;
      font-size: 13px;
      font-weight: 500;
      text-align: center;
    }
  </style>
</head>
<body>
  <h1>🧠 DeepDive</h1>
  <p>Your AI co-pilot for reading and watching content online.</p>
  
  <div class="status">✓ Extension Active</div>
  
  <div class="info" style="margin-top: 16px;">
    <strong>How to use:</strong><br>
    Click the brain icon (🧠) on any webpage to analyze and chat about the content.
  </div>
  
  <script src="popup.js"></script>
</body>
</html>


```

### frontend/background.js

```javascript
// Background service worker for DeepDive extension
// Handles communication between content scripts and backend API

const BACKEND_URL = 'http://localhost:3000';

// Handle messages from content scripts
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'analyze') {
    analyzeContent(request.data)
      .then(sendResponse)
      .catch(error => sendResponse({ error: error.message }));
    return true; // Keep message channel open for async response
  }

  if (request.action === 'analyzeStream') {
    analyzeContentStreaming(request.data, sender.tab.id)
      .then(() => sendResponse({ success: true }))
      .catch(error => sendResponse({ error: error.message }));
    return true;
  }

  if (request.action === 'chat') {
    sendChatMessage(request.data)
      .then(sendResponse)
      .catch(error => sendResponse({ error: error.message }));
    return true;
  }

  if (request.action === 'getConnections') {
    getConnections(request.data)
      .then(sendResponse)
      .catch(error => sendResponse({ error: error.message }));
    return true;
  }

  if (request.action === 'getHistory') {
    getHistory()
      .then(sendResponse)
      .catch(error => sendResponse({ error: error.message }));
    return true;
  }
});

// Call backend /analyze/stream endpoint with SSE
async function analyzeContentStreaming(data, tabId) {
  try {
    const response = await fetch(`${BACKEND_URL}/analyze/stream`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data)
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const reader = response.body.getReader();
    const decoder = new TextDecoder();

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const chunk = decoder.decode(value);
      const lines = chunk.split('\n');

      for (const line of lines) {
        if (line.startsWith('data: ')) {
          const data = JSON.parse(line.slice(6));

          // Send update to content script
          chrome.tabs.sendMessage(tabId, {
            action: 'streamUpdate',
            data: data
          });
        }
      }
    }
  } catch (error) {
    console.error('Error streaming content:', error);
    // Send error to content script
    chrome.tabs.sendMessage(tabId, {
      action: 'streamUpdate',
      data: { type: 'error', message: error.message }
    });
    throw error;
  }
}

// Call backend /analyze endpoint
async function analyzeContent(data) {
  try {
    const response = await fetch(`${BACKEND_URL}/analyze`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data)
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Error analyzing content:', error);
    throw error;
  }
}

// Call backend /chat endpoint
async function sendChatMessage(data) {
  try {
    const response = await fetch(`${BACKEND_URL}/chat`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data)
    });
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    return await response.json();
  } catch (error) {
    console.error('Error sending chat message:', error);
    throw error;
  }
}

// Get article connections
async function getConnections(data) {
  try {
    const response = await fetch(`${BACKEND_URL}/connections/${data.urlHash}`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Error getting connections:', error);
    throw error;
  }
}

// Get article history
async function getHistory() {
  try {
    const response = await fetch(`${BACKEND_URL}/history`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Error getting history:', error);
    throw error;
  }
}

console.log('DeepDive background service worker loaded');


```

### frontend/injectUI.css

```css
/* Styles for DeepDive injected UI */

/* CSS Variables for Light Mode */
#smart-summary-root {
  --bg-primary: #ffffff;
  --bg-secondary: #fafafa;
  --bg-tertiary: #f9fafb;
  --border-color: #e5e7eb;
  --border-light: #f3f4f6;
  --text-primary: #111827;
  --text-secondary: #6b7280;
  --text-tertiary: #9ca3af;
  --accent-blue: #2563eb;
  --accent-blue-hover: #1d4ed8;
  --button-bg: #111827;
  --button-bg-hover: #374151;
  --chat-user-bg: #f3f4f6;
  --loading-bar: #111827;
}

/* CSS Variables for Dark Mode */
#smart-summary-root.dark-mode {
  --bg-primary: #1f2937;
  --bg-secondary: #111827;
  --bg-tertiary: #374151;
  --border-color: #4b5563;
  --border-light: #374151;
  --text-primary: #f9fafb;
  --text-secondary: #d1d5db;
  --text-tertiary: #9ca3af;
  --accent-blue: #3b82f6;
  --accent-blue-hover: #60a5fa;
  --button-bg: #3b82f6;
  --button-bg-hover: #2563eb;
  --chat-user-bg: #374151;
  --loading-bar: #60a5fa;
}

#smart-summary-root {
  all: initial;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
  background-color: var(--bg-primary);
  color: var(--text-primary);
}

#smart-summary-toggle {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 40px;
  height: 40px;
  border-radius: 6px;
  background: var(--bg-primary);
  border: 1px solid var(--border-color);
  color: var(--text-primary);
  font-size: 16px;
  font-weight: 600;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
  cursor: grab;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  z-index: 999999;
  transition: all 0.15s;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

#smart-summary-toggle:hover {
  border-color: var(--text-tertiary);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  background: var(--bg-secondary);
}

#smart-summary-toggle:active {
  cursor: grabbing;
}

#smart-summary-sidebar {
  position: fixed;
  top: 0;
  right: 0;
  width: 400px;
  height: 100vh;
  background: var(--bg-primary);
  box-shadow: -1px 0 0 rgba(0, 0, 0, 0.1);
  z-index: 999998;
  display: flex;
  flex-direction: column;
  transition: transform 0.3s ease-in-out, width 0.3s ease-in-out, left 0.3s ease-in-out;
  transform: translateX(0);
}

#smart-summary-sidebar.smart-summary-hidden {
  transform: translateX(100%);
}

#smart-summary-sidebar.fullscreen {
  left: 0;
  right: 0;
  width: 100vw;
  box-shadow: none;
}

#smart-summary-sidebar.fullscreen .smart-summary-header,
#smart-summary-sidebar.fullscreen .smart-summary-tabs,
#smart-summary-sidebar.fullscreen .smart-summary-content {
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
}

#smart-summary-sidebar.fullscreen .smart-summary-content {
  padding-left: 24px;
  padding-right: 24px;
}

.smart-summary-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24px 24px 20px 24px;
  background: var(--bg-primary);
  color: var(--text-primary);
  border-bottom: 1px solid var(--border-light);
}

.smart-summary-header h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: -0.01em;
  flex: 1;
}

#smart-summary-close {
  background: none;
  border: none;
  color: var(--text-tertiary);
  font-size: 24px;
  cursor: pointer;
  line-height: 1;
  padding: 0;
  width: 24px;
  height: 24px;
  transition: color 0.2s;
}

#smart-summary-close:hover {
  color: var(--text-primary);
}

#theme-toggle {
  background: none;
  border: none;
  color: var(--text-tertiary);
  font-size: 20px;
  cursor: pointer;
  padding: 0;
  width: 24px;
  height: 24px;
  margin-right: 8px;
  transition: color 0.2s, transform 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

#theme-toggle:hover {
  color: var(--text-primary);
  transform: rotate(15deg);
}

#fullscreen-toggle {
  background: none;
  border: none;
  color: var(--text-tertiary);
  font-size: 18px;
  cursor: pointer;
  padding: 4px;
  line-height: 1;
  border-radius: 4px;
  transition: all 0.2s;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
}

#fullscreen-toggle:hover {
  color: var(--text-primary);
  background: var(--bg-secondary);
}

.smart-summary-tabs {
  display: flex;
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-primary);
  padding: 0 24px;
}

.tab-btn {
  padding: 12px 0;
  margin-right: 24px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  font-size: 13px;
  font-weight: 400;
  color: var(--text-tertiary);
  transition: all 0.2s;
}

.tab-btn:hover {
  color: var(--text-primary);
}

.tab-btn.active {
  color: var(--text-primary);
  border-bottom-color: var(--text-primary);
  font-weight: 500;
}

.smart-summary-content {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
  background: var(--bg-primary);
}

.tab-content {
  display: none;
}

.tab-content.active {
  display: block;
}

.loading {
  text-align: center;
  padding: 60px 40px;
  color: var(--text-secondary);
}

.loading-bar-container {
  width: 100%;
  height: 4px;
  background: var(--border-color);
  border-radius: 2px;
  overflow: hidden;
  margin: 20px 0;
}

.loading-bar {
  height: 100%;
  background: var(--loading-bar);
  border-radius: 2px;
  animation: loading-progress 2s ease-in-out infinite;
  transform-origin: left;
}

@keyframes loading-progress {
  0% {
    transform: translateX(-100%) scaleX(0.3);
  }
  50% {
    transform: translateX(0%) scaleX(0.6);
  }
  100% {
    transform: translateX(100%) scaleX(0.3);
  }
}

/* Streaming content */
.summary-content.streaming {
  padding: 20px 24px;
}

.streaming-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-light);
}

.streaming-header h3 {
  font-size: 14px;
  font
[truncated — 21328 more characters]
```

[1 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]