This guide shows you how to integrate Gemini/Claude with full context awareness for your Excalidraw diagrams.
Your AI will be fully aware of:
- ✅ All previous conversations
- ✅ Current canvas state (all elements)
- ✅ Version history
- ✅ Project metadata
This means when a user says "add a database", the AI knows what's already on the canvas!
# In Supabase SQL Editor, run:
cat supabase-migration-ai-versions.sqlThis creates:
ai_messagestable (conversation history)project_versionstable (edit snapshots)- Auto-versioning functions
# Get all context for a project
GET /api/projects/{project-id}/context
# Returns:
{
"context": {
"project": { ... },
"messages": [...],
"latest_version": { ... },
"stats": { ... }
}
}import { completeAIWorkflow } from '@/lib/ai/mcp-integration-example';
const result = await completeAIWorkflow(
projectId,
"Create a microservices architecture"
);
// AI will:
// 1. Read all previous messages
// 2. See current canvas
// 3. Generate response
// 4. Save conversation
// 5. Update canvas
// 6. Create version snapshot# Get conversation history
GET /api/projects/{id}/messages
# Add user or AI message
POST /api/projects/{id}/messages
{
"role": "user" | "assistant" | "system",
"content": "message text",
"metadata": { optional extra data }
}# Get version history
GET /api/projects/{id}/versions?limit=10
# Create version snapshot
POST /api/projects/{id}/versions
{
"canvas_data": { elements, appState, files },
"description": "Added database service"
}# Get everything AI needs
GET /api/projects/{id}/context?messages=10
# Returns bundled:
- Project info
- Last 10 messages
- Current canvas state
- Latest version
- StatsUser: "Add a database to the microservices diagram"
↓
Frontend: GET /api/projects/123/context
↓
Response: {
messages: [
{ role: "user", content: "Create microservices architecture" },
{ role: "assistant", content: "Created API Gateway, Auth Service..." }
],
project: {
canvas_data: {
elements: [
{ type: "rectangle", text: "API Gateway", x: 100, y: 100 },
{ type: "rectangle", text: "Auth Service", x: 350, y: 100 }
]
}
}
}
↓
Build Prompt: "Previous conversation: ... Current canvas has: API Gateway, Auth Service... User now wants: Add database"
↓
Send to Gemini/Claude
↓
AI Response: "I'll add a PostgreSQL database and connect it to the services..."
{
"elements": [
{ type: "cylinder", text: "PostgreSQL", x: 225, y: 300 },
{ type: "arrow", start: "Auth Service", end: "PostgreSQL" }
]
}
↓
Save: Message → Update Canvas → Create Version
async function sendToAI(projectId: string, userMessage: string) {
// 1. Get context
const res = await fetch(`/api/projects/${projectId}/context`);
const { context } = await res.json();
// 2. Build prompt with full awareness
const prompt = `
Project: ${context.project.title}
Current elements: ${context.stats.elements_count}
Previous conversation:
${context.messages.map(m => `${m.role}: ${m.content}`).join('\n')}
User's new request: ${userMessage}
Generate Excalidraw elements to fulfill this request.
`;
// 3. Send to your AI
const aiResponse = await yourAIFunction(prompt);
// 4. Save conversation
await fetch(`/api/projects/${projectId}/messages`, {
method: 'POST',
body: JSON.stringify({ role: 'user', content: userMessage })
});
await fetch(`/api/projects/${projectId}/messages`, {
method: 'POST',
body: JSON.stringify({ role: 'assistant', content: aiResponse })
});
return aiResponse;
}import { completeAIWorkflow } from '@/lib/ai/mcp-integration-example';
// This handles everything for you:
const result = await completeAIWorkflow(projectId, "Add a Redis cache");
// Automatically:
// ✅ Fetches context
// ✅ Sends to AI with full awareness
// ✅ Saves user + AI messages
// ✅ Extracts Excalidraw elements from response
// ✅ Updates canvas
// ✅ Creates version snapshotasync function sendToGemini(projectId: string, userMessage: string) {
// Get context
const res = await fetch(`/api/projects/${projectId}/context`);
const { context } = await res.json();
// Build prompt
const prompt = buildAIPrompt(context, userMessage);
// Call Gemini
const GEMINI_KEY = process.env.NEXT_PUBLIC_GEMINI_API_KEY;
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${GEMINI_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }]
})
}
);
const data = await response.json();
return data.candidates[0].content.parts[0].text;
}'use client';
import { useState, useEffect } from 'react';
import { completeAIWorkflow } from '@/lib/ai/mcp-integration-example';
export default function AIChat({ projectId }: { projectId: string }) {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
// Load conversation history
useEffect(() => {
fetch(`/api/projects/${projectId}/messages`)
.then(res => res.json())
.then(data => setMessages(data.messages));
}, [projectId]);
const handleSend = async () => {
if (!input.trim()) return;
setLoading(true);
try {
// Send to AI with full context
const result = await completeAIWorkflow(projectId, input);
// Reload messages
const res = await fetch(`/api/projects/${projectId}/messages`);
const data = await res.json();
setMessages(data.messages);
setInput('');
} catch (error) {
console.error('AI error:', error);
} finally {
setLoading(false);
}
};
return (
<div>
{/* Chat history */}
<div>
{messages.map((msg, i) => (
<div key={i} className={msg.role === 'user' ? 'user-msg' : 'ai-msg'}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
</div>
{/* Input */}
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
placeholder="Tell AI what to create..."
disabled={loading}
/>
<button onClick={handleSend} disabled={loading}>
{loading ? 'Generating...' : 'Send'}
</button>
</div>
);
}function VersionTimeline({ projectId }: { projectId: string }) {
const [versions, setVersions] = useState([]);
useEffect(() => {
fetch(`/api/projects/${projectId}/versions?limit=20`)
.then(res => res.json())
.then(data => setVersions(data.versions));
}, [projectId]);
const restoreVersion = async (versionId: string, canvasData: any) => {
// Restore to this version
await fetch(`/api/projects/${projectId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ canvas_data: canvasData })
});
// Reload canvas
window.location.reload();
};
return (
<div>
<h3>Version History</h3>
{versions.map(v => (
<div key={v.id}>
<strong>v{v.version_number}</strong> - {v.description}
<small>{new Date(v.created_at).toLocaleString()}</small>
<button onClick={() => restoreVersion(v.id, v.canvas_data)}>
Restore
</button>
</div>
))}
</div>
);
}# .env.local
# For Gemini
NEXT_PUBLIC_GEMINI_API_KEY=your-key-here
# For Claude (if using their API directly)
ANTHROPIC_API_KEY=your-key-here
# Your Supabase creds (already set)
NEXT_PUBLIC_SUPABASE_URL=...
NEXT_PUBLIC_SUPABASE_ANON_KEY=...// Only send last 10-20 messages to AI to avoid token limits
GET /api/projects/{id}/context?messages=10// Create versions on:
// - AI updates
// - User manual edits (debounced)
// - Major milestones
// Don't create on every tiny change (too many versions!)// Periodically archive very old messages if conversation gets too long
// Keep last 50-100 messages, archive the resttry {
const result = await completeAIWorkflow(projectId, message);
} catch (error) {
// Show user-friendly error
// Log to error tracking service
// Allow retry
}- Check RLS policies in Supabase
- Verify user owns the project
- Check browser console for errors
- Verify context API returns data:
GET /api/projects/{id}/context - Check message limit parameter
- Ensure messages are being saved first
- Check if
create_version_snapshotfunction exists in Supabase - Verify RLS allows inserts
- Check for SQL errors in Supabase logs
- Run the migration SQL in Supabase
- Test the context API with a project ID
- Implement AI integration using the examples
- Add chat UI to your canvas page
- Test version history and restore functionality
Minimum viable implementation (30 mins):
- Run SQL migration ✅
- Add simple chat input next to canvas
- Use
completeAIWorkflow()function - Show conversation history below
Full implementation (2-3 hours):
- Beautiful chat UI with message bubbles
- Loading states with animations
- Version timeline with restore buttons
- Error handling & retries
- Excalidraw integration with live updates
You've got all the infrastructure - just connect it to your UI! 🚀