Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 63 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ There's no single platform that combines all of this with built-in AI assistance
- Powered by the **Sarvam AI** chat completions API via a secure server-side Next.js API route (`/api/ai-draft`).
- Code Review Copilot (UI ready, backend in progress)

### 🚪 Collaboration Rooms
- Real-time collaboration rooms with chat, shared notes, code editor, and voice/video
- Room creation with title, description, language, and private/public visibility
- **Chat**: live messages via Firestore `onSnapshot`, send-on-enter, timestamp display
- **Shared notes**: markdown notes with auto-save on debounce
- **Code editor**: syntax-highlighted collaborative editing with debounced Firestore sync
- **Voice/video**: WebRTC peer-to-peer calls with mic, camera, and screen-share controls
- **Presence**: real-time member list, cursor tracking, heartbeat-based online status
- **AI Room Summaries**: generate meeting summaries from chat and notes via Sarvam AI
- Summary modal with markdown rendering (react-markdown + remark-gfm)

---

## 📁 Project Structure
Expand All @@ -90,23 +101,43 @@ DevConnect-AI/
├── app/
│ ├── layout.js # Root layout with AuthProvider
│ ├── page.js # Landing page (features tour)
│ ├── globals.css # Global styles
│ ├── globals.css # Global styles + markdown styles
│ ├── api/
│ │ ├── ai-draft/
│ │ │ └── route.js # Sarvam AI-powered "Draft with AI" endpoint
│ │ └── code-review/
│ │ └── route.js # Code review endpoint (in progress)
│ │ ├── code-review/
│ │ │ └── route.js # Code review endpoint (in progress)
│ │ └── rooms/
│ │ └── [roomId]/
│ │ └── summary/
│ │ └── route.js # AI room summary endpoint
│ ├── dashboard/
│ │ └── page.js # Main community feed (protected)
│ ├── login/
│ │ └── page.js # Login with Google / GitHub
│ ├── rooms/
│ │ ├── page.js # Rooms hub — list/create rooms (protected)
│ │ └── [roomId]/
│ │ └── page.js # Room workspace — chat, notes, editor, call
│ ├── signup/
│ │ └── page.js # Signup with Google / GitHub
│ ├── profile/
│ │ └── page.js # User profile page
│ └── settings/
│ └── page.js # Settings page
├── components/
│ ├── rooms/
│ │ ├── RoomCallControls.js # Voice/video/screen-share controls + camera preview
│ │ ├── RoomChat.js # Real-time chat panel
│ │ ├── RoomCursorLayer.js # Remote cursor/presence overlay
│ │ ├── RoomEditor.js # Collaborative code editor
│ │ ├── RoomMembers.js # Online member list
│ │ ├── RoomNotes.js # Shared markdown notes
│ │ ├── RoomSummaryButton.js # Generate/view summary button
│ │ └── RoomSummaryModal.js # Markdown summary modal with backdrop blur
│ ├── dashboard/
│ │ ├── LeftSidebar.js # Feed tabs + rooms nav
│ │ └── RightSidebar.js # AI copilot + rooms promo widgets
│ ├── AIDraftAssistant.js # "Draft with AI" prompt box + Sarvam AI call
│ ├── CodeEditorModal.js # Modal for inserting formatted code blocks
│ ├── CodeReview.js # Code review UI component
Expand All @@ -117,7 +148,10 @@ DevConnect-AI/
├── context/
│ └── AuthContext.js # Firebase auth state (Google + GitHub login/logout)
├── lib/
│ └── firebase.js # Firebase app initialization
│ ├── firebase.js # Firebase client initialization
│ ├── firebase-admin.js # Firebase server-side initialization (for API routes)
│ ├── rooms.js # Room CRUD + presence + summary helpers
│ └── webrtc.js # WebRTC signaling + media stream management
├── .env.local # Environment variables (not committed)
├── .gitignore
├── package.json
Expand Down Expand Up @@ -259,6 +293,21 @@ All Firebase values are available in **Firebase Console → Project Settings →

---

## 🔥 Required Firestore Collections

The rooms feature uses these Firestore collections (created automatically on first use):

| Collection | Purpose |
|---|---|
| `rooms/{roomId}` | Room metadata — title, description, language, members, createdBy, createdAt |
| `rooms/{roomId}/messages/{messageId}` | Real-time chat messages |
| `rooms/{roomId}/notes/{noteId}` | Shared markdown notes (one active note per room) |
| `rooms/{roomId}/presence/{uid}` | Live presence — last heartbeat, cursor position, media state |

No manual collection setup is required — Firestore creates these on first write.

---

## ▶️ Running the App

| Command | Description |
Expand All @@ -278,9 +327,12 @@ All Firebase values are available in **Firebase Console → Project Settings →
| `/login` | Login with Google or GitHub | No |
| `/signup` | Signup with Google or GitHub | No |
| `/dashboard` | Main community feed | ✅ Yes |
| `/rooms` | Collaboration rooms hub — list/create rooms | ✅ Yes |
| `/rooms/[roomId]` | Room workspace — chat, notes, editor, voice/video | ✅ Yes |
| `/profile` | User profile page | ✅ Yes |
| `/settings` | Settings page | ✅ Yes |
| `/api/ai-draft` | Server-side Sarvam AI draft endpoint | N/A (API route) |
| `/api/rooms/[roomId]/summary` | AI room summary generation endpoint | N/A (API route) |

> Protected routes automatically redirect unauthenticated users to `/login`.

Expand All @@ -305,9 +357,14 @@ This project is under active development. Here's what's done and what's still in
| Markdown rendering for posts (headings, lists, code blocks) | ✅ Done |
| In-post code editor / inserter | ✅ Done |
| **Draft with AI Assistant** (Sarvam AI, question-only mode) | ✅ Done |
| Collaboration Rooms — hub + workspace shell | ✅ Done |
| Room chat (real-time, Firestore onSnapshot) | ✅ Done |
| Room shared notes (markdown, auto-save) | ✅ Done |
| Room collaborative code editor | ✅ Done |
| Room voice/video/screen-share (WebRTC) | ✅ Done |
| Room presence (members, cursors, heartbeat) | ✅ Done |
| AI Room Summaries (Sarvam AI + markdown modal) | ✅ Done |
| AI Code Review (Copilot) | 🔄 In Progress |
| User profile page | 🔜 Planned |
| Trending / Questions / Collaborations / Saved Posts (functional) | 🔜 Planned |

---

Expand Down
151 changes: 151 additions & 0 deletions app/api/rooms/[roomId]/summary/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { collection, query, orderBy, getDocs, doc, getDoc } from "firebase/firestore";
import { getServerDb } from "../../../../../lib/firebase-admin";

export async function POST(req) {
try {
const { roomId } = await req.json();
const db = getServerDb();

if (!roomId) {
return Response.json(
{ error: "roomId is required" },
{ status: 400 }
);
}

// Fetch room data
const roomDoc = await getDoc(doc(db, "rooms", roomId));
const roomData = roomDoc.exists ? roomDoc.data() : {};
const title = roomData.title || "Collaboration Room";
const language = roomData.language || "Not specified";

// Fetch messages
const messagesRef = collection(db, "rooms", roomId, "messages");
const messagesQuery = query(messagesRef, orderBy("createdAt", "asc"));
const messagesSnap = await getDocs(messagesQuery);
const messages = messagesSnap.docs.map(d => ({ id: d.id, ...d.data() }));

// Fetch notes
const noteContent = roomData.noteContent || "";

const messagesText = messages.length > 0
? messages.map(m => `- ${m.senderName || "Anonymous"} (${m.createdAt ? new Date(m.createdAt.toDate()).toLocaleDateString() : "recent"}): ${m.text}`).join("\n")
: "No messages yet.";

const notesText = noteContent ? noteContent : "No notes yet.";

const sarvamApiKey = process.env.SARVAM_API_KEY;

if (!sarvamApiKey) {
const fallbackSummary = [
`## ${title || "Collaboration Room"} Summary`,
"",
`**Room ID**: ${roomId || "N/A"}`,
`**Language**: ${language || "Not specified"}`,
`**Generated**: ${new Date().toLocaleDateString()} at ${new Date().toLocaleTimeString()}`,
"",
"---",
"",
"### Recent Messages",
messagesText,
"",
"### Recent Notes",
notesText,
"",
"---",
"",
`_Generated by DevConnect AI • ${new Date().toLocaleDateString()}_`,
].join("\n");

return Response.json({
summary: fallbackSummary,
hasApiKey: false,
suggestion: "Configure SARVAM_API_KEY for AI-powered summaries",
messageCount: messages.length,
noteCount: noteContent ? 1 : 0,
});
}

const systemPrompt = [
"You are an AI assistant that creates concise collaboration room summaries.",
"Analyze the conversation and notes provided and generate a summary that:",
"- Captures key discussion points and decisions",
"- Highlights important code snippets or technical discoveries",
"- Lists action items or next steps",
"- Uses markdown formatting for readability",
"Keep the summary focused and actionable. Do not add fluff.",
].join(" ");

const userPrompt = [
`Room: ${title || "Untitled"}`,
`Language: ${language || "Not specified"}`,
"",
"Recent Messages:",
messagesText,
"",
"Recent Notes:",
notesText,
"",
"Please provide a comprehensive room summary.",
].join("\n");

const sarvamResponse = await fetch("https://api.sarvam.ai/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"api-subscription-key": sarvamApiKey,
},
body: JSON.stringify({
model: "sarvam-30b",
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt },
],
temperature: 0.3,
max_tokens: 1500,
}),
});

if (!sarvamResponse.ok) {
const errText = await sarvamResponse.text();
console.error("Sarvam AI API error:", errText);
return Response.json(
{ error: "Failed to generate AI summary", details: errText },
{ status: 500 }
);
}

const data = await sarvamResponse.json();
const summaryBody = data.choices?.[0]?.message?.content || "";

const formattedSummary = [
`## ${title || "Collaboration Room"} Summary`,
"",
`**Room ID**: ${roomId || "N/A"}`,
`**Language**: ${language || "Not specified"}`,
`**Generated**: ${new Date().toLocaleDateString()} at ${new Date().toLocaleTimeString()}`,
"",
"---",
"",
summaryBody,
"",
"---",
"",
`_Generated by DevConnect AI • ${new Date().toLocaleDateString()}_`,
].join("\n");

return Response.json({
summary: formattedSummary,
hasApiKey: true,
messageCount: messages.length,
noteCount: noteContent ? 1 : 0,
});

} catch (error) {
console.error("Error generating room summary:", error);
return Response.json(
{ error: "Failed to generate room summary", details: error.message },
{ status: 500 }
);
}
}
93 changes: 93 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,93 @@ a {
transition: color var(--transition-fast);
}

.markdown {
line-height: 1.7;
color: var(--text-primary);
}

/* Markdown content styles */
.markdown h1, .markdown h2, .markdown h3, .markdown h4 {
color: var(--text-primary);
margin-top: 1rem;
margin-bottom: 0.5rem;
font-weight: 600;
}

.markdown h1 { font-size: 1.25rem; }
.markdown h2 { font-size: 1.1rem; }
.markdown h3 { font-size: 1rem; }

.markdown p {
margin-bottom: 0.75rem;
line-height: 1.6;
}

.markdown ul, .markdown ol {
padding-left: 1.5rem;
margin-bottom: 0.75rem;
}

.markdown li {
margin-bottom: 0.25rem;
line-height: 1.5;
}

.markdown code {
background: var(--bg-primary);
padding: 0.15rem 0.4rem;
border-radius: 4px;
font-size: 0.85rem;
font-family: monospace;
}

.markdown pre {
background: var(--bg-primary);
padding: 0.75rem;
border-radius: 6px;
overflow-x: auto;
margin-bottom: 0.75rem;
}

.markdown pre code {
padding: 0;
background: transparent;
}

.markdown blockquote {
border-left: 3px solid var(--accent-primary);
padding-left: 0.75rem;
margin: 0.75rem 0;
color: var(--text-muted);
}

.markdown table {
border-collapse: collapse;
margin-bottom: 0.75rem;
width: 100%;
}

.markdown th, .markdown td {
border: 1px solid var(--border-color);
padding: 0.5rem 0.75rem;
text-align: left;
}

.markdown th {
background: var(--bg-primary);
font-weight: 600;
}

.markdown strong {
color: var(--text-primary);
}

.markdown hr {
border: none;
border-top: 1px solid var(--border-color);
margin: 1rem 0;
}

a:hover {
color: var(--accent-primary-hover);
}
Expand Down Expand Up @@ -154,3 +241,9 @@ input, textarea, button, div, a, span, p, h1, h2, h3, h4, h5, h6 {
33% { transform: translate(30px, -20px) scale(1.05); }
66% { transform: translate(-20px, 10px) scale(0.95); }
}

/* Spin animation for loader */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
3 changes: 3 additions & 0 deletions app/rooms/[roomId]/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function RoomLayout({ children }) {
return <>{children}</>;
}
3 changes: 3 additions & 0 deletions app/rooms/[roomId]/messages/[messageId]/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function MessageLayout({ children }) {
return <>{children}</>;
}
Loading
Loading