feat: brainstorm sessions, PDF document Q&A, embedding fixes, and UI improvements
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 7s

- Add brainstorm feature with collaborative canvas, AI idea generation, live cursors, playback, and export
- Add PDF upload/extraction/ingestion pipeline with pgvector document search (RAG)
- Add document Q&A overlay with streaming chat and PDF preview
- Add note attachments UI with status polling, grid layout, and auto-scroll
- Add task extraction AI tool and agent executor improvements
- Fix NoteEmbedding missing updatedAt column, re-index 66 notes with 1536-dim embeddings
- Fix brainstorm 'Create Note' button: add success toast and redirect to created note
- Fix memory echo notification infinite polling
- Fix chat route to always include document_search tool
- Add brainstorm i18n keys across all 14 locales
- Add socket server for real-time brainstorm collaboration
- Add hierarchical notebook selector and organize notebook dialog improvements
- Add sidebar brainstorm section with session management
- Update prisma schema with brainstorm tables, attachments, and document chunks
This commit is contained in:
Antigravity
2026-05-14 17:43:21 +00:00
parent 195e845f0a
commit 1fcea6ed7d
228 changed files with 57656 additions and 1059 deletions

View File

@@ -9,35 +9,40 @@ datasource db {
}
model User {
id String @id @default(cuid())
name String?
email String @unique
emailVerified DateTime?
password String?
role String @default("USER")
image String?
theme String @default("light")
resetToken String? @unique
resetTokenExpiry DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cardSizeMode String @default("variable")
accounts Account[]
agents Agent[]
aiFeedback AiFeedback[]
canvases Canvas[]
conversations Conversation[]
labels Label[]
memoryEchoInsights MemoryEchoInsight[]
notes Note[]
noteHistories NoteHistory[]
sentShares NoteShare[] @relation("SentShares")
receivedShares NoteShare[] @relation("ReceivedShares")
notebooks Notebook[]
sessions Session[]
aiSettings UserAISettings?
workflows Workflow[]
notifications Notification[]
id String @id @default(cuid())
name String?
email String @unique
emailVerified DateTime?
password String?
role String @default("USER")
image String?
theme String @default("light")
resetToken String? @unique
resetTokenExpiry DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cardSizeMode String @default("variable")
accounts Account[]
agents Agent[]
aiFeedback AiFeedback[]
brainstormActivities BrainstormActivity[]
brainstormParticipant BrainstormParticipant[]
brainstormSessions BrainstormSession[]
canvases Canvas[]
conversations Conversation[]
labels Label[]
memoryEchoInsights MemoryEchoInsight[]
notes Note[]
noteHistories NoteHistory[]
sentShares NoteShare[] @relation("SentShares")
receivedShares NoteShare[] @relation("ReceivedShares")
sentBrainstormShares BrainstormShare[] @relation("SentBrainstormShares")
receivedBrainstormShares BrainstormShare[] @relation("ReceivedBrainstormShares")
notebooks Notebook[]
notifications Notification[]
sessions Session[]
aiSettings UserAISettings?
workflows Workflow[]
}
model Account {
@@ -82,19 +87,19 @@ model Notebook {
icon String?
color String?
order Int
parentId String?
trashedAt DateTime?
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
parentId String?
trashedAt DateTime?
agents Agent[]
conversations Conversation[]
labels Label[]
notes Note[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
workflows Workflow[]
parent Notebook? @relation("NotebookTree", fields: [parentId], references: [id], onDelete: Cascade)
children Notebook[] @relation("NotebookTree")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
workflows Workflow[]
@@index([userId, order])
@@index([userId])
@@ -106,11 +111,11 @@ model Label {
id String @id @default(cuid())
name String
color String @default("gray")
type String @default("user") // "ai" or "user"
notebookId String?
userId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
type String @default("user")
notebook Notebook? @relation(fields: [notebookId], references: [id], onDelete: Cascade)
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
notes Note[] @relation("LabelToNote")
@@ -121,51 +126,56 @@ model Label {
}
model Note {
id String @id @default(cuid())
title String?
content String
color String @default("default")
isPinned Boolean @default(false)
isArchived Boolean @default(false)
type String @default("richtext")
dismissedFromRecent Boolean @default(false)
checkItems String?
labels String?
images String?
id String @id @default(cuid())
title String?
content String
color String @default("default")
isPinned Boolean @default(false)
isArchived Boolean @default(false)
type String @default("richtext")
dismissedFromRecent Boolean @default(false)
checkItems String?
labels String?
images String?
links String?
reminder DateTime?
isReminderDone Boolean @default(false)
reminderRecurrence String?
reminderLocation String?
isMarkdown Boolean @default(false)
size String @default("small")
sharedWith String?
userId String?
order Int @default(0)
notebookId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
contentUpdatedAt DateTime @default(now())
autoGenerated Boolean?
aiProvider String?
aiConfidence Int?
language String?
languageConfidence Float?
lastAiAnalysis DateTime?
trashedAt DateTime?
historyEnabled Boolean @default(false)
/// Illustration SVG (sanitized) for editorial feed thumbnail — optional, peut être généré par IA
illustrationSvg String?
links String?
reminder DateTime?
isReminderDone Boolean @default(false)
reminderRecurrence String?
reminderLocation String?
isMarkdown Boolean @default(false) // DEPRECATED: use type = 'markdown' instead
size String @default("small")
sharedWith String?
userId String?
order Int @default(0)
notebookId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
contentUpdatedAt DateTime @default(now())
autoGenerated Boolean?
aiProvider String?
aiConfidence Int?
language String?
languageConfidence Float?
lastAiAnalysis DateTime?
trashedAt DateTime?
tsv Unsupported("tsvector")?
aiFeedback AiFeedback[]
memoryEchoAsNote1 MemoryEchoInsight[] @relation("EchoNote1")
memoryEchoAsNote2 MemoryEchoInsight[] @relation("EchoNote2")
notebook Notebook? @relation(fields: [notebookId], references: [id])
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
noteEmbedding NoteEmbedding?
shares NoteShare[]
labelRelations Label[] @relation("LabelToNote")
historyEntries NoteHistory[]
historyEnabled Boolean @default(false)
illustrationSvg String?
tsv Unsupported("tsvector")?
aiFeedback AiFeedback[]
convertedBrainstormIdeas BrainstormIdea[] @relation("ConvertedBrainstormIdea")
exportedBrainstormSessions BrainstormSession[] @relation("ExportedBrainstorm")
sourceBrainstormSessions BrainstormSession[] @relation("SourceBrainstorm")
memoryEchoAsNote1 MemoryEchoInsight[] @relation("EchoNote1")
memoryEchoAsNote2 MemoryEchoInsight[] @relation("EchoNote2")
notebook Notebook? @relation(fields: [notebookId], references: [id])
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
noteEmbedding NoteEmbedding?
historyEntries NoteHistory[]
shares NoteShare[]
labelRelations Label[] @relation("LabelToNote")
attachments NoteAttachment[]
brainstormNoteRefs BrainstormNoteRef[]
@@index([isPinned])
@@index([isArchived])
@@ -178,26 +188,26 @@ model Note {
model NoteHistory {
id String @id @default(cuid())
noteId String
userId String
version Int
reason String?
title String?
content String
color String
isPinned Boolean
isArchived Boolean
type String
checkItems String?
labels String?
images String?
links String?
isMarkdown Boolean
size String
notebookId String?
createdAt DateTime @default(now())
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
noteId String
userId String
version Int
reason String?
title String?
content String
color String
isPinned Boolean
isArchived Boolean
type String
checkItems String?
labels String?
images String?
links String?
isMarkdown Boolean
size String
notebookId String?
createdAt DateTime @default(now())
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([noteId, version])
@@index([noteId, createdAt(sort: Desc)])
@@ -284,10 +294,10 @@ model UserAISettings {
desktopNotifications Boolean @default(false)
anonymousAnalytics Boolean @default(false)
autoLabeling Boolean @default(true)
fontFamily String @default("inter")
languageDetection Boolean @default(true)
noteHistory Boolean @default(false)
noteHistoryMode String @default("manual")
languageDetection Boolean @default(true)
fontFamily String @default("inter")
autoSave Boolean @default(true)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -300,9 +310,8 @@ model UserAISettings {
model NoteEmbedding {
id String @id @default(cuid())
noteId String @unique
embedding Unsupported("vector(1536)")
embedding String
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
@@index([noteId])
@@ -321,9 +330,6 @@ model Agent {
isEnabled Boolean @default(true)
targetNotebookId String?
sourceNotebookId String?
sourceNoteIds String?
slideTheme String?
slideStyle String?
tools String? @default("[]")
maxSteps Int @default(10)
notifyEmail Boolean @default(false)
@@ -334,6 +340,9 @@ model Agent {
scheduledTime String? @default("08:00")
scheduledDay Int?
timezone String?
sourceNoteIds String?
slideStyle String?
slideTheme String?
notebook Notebook? @relation(fields: [targetNotebookId], references: [id])
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
actions AgentAction[]
@@ -426,17 +435,198 @@ model WorkflowRun {
}
model Notification {
id String @id @default(cuid())
userId String
type String // "agent_success", "agent_failure", "share", "reminder", "system"
title String
message String?
read Boolean @default(false)
actionUrl String? // e.g. "/agents" or "/notes/xxx"
relatedId String? // e.g. agentId or noteId
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
id String @id @default(cuid())
userId String
type String
title String
message String?
read Boolean @default(false)
actionUrl String?
relatedId String?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, read])
@@index([userId, createdAt])
}
model BrainstormSession {
id String @id @default(cuid())
seedIdea String
sourceNoteId String?
contextNoteIds String?
exportedNoteId String?
userId String
inviteToken String? @unique
inviteExpiry DateTime?
liveblocksRoomId String?
isPublic Boolean @default(false)
guestCanEdit Boolean @default(false)
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ideas BrainstormIdea[]
participants BrainstormParticipant[]
activities BrainstormActivity[]
shares BrainstormShare[]
snapshots BrainstormSnapshot[]
sourceNote Note? @relation("SourceBrainstorm", fields: [sourceNoteId], references: [id])
exportedNote Note? @relation("ExportedBrainstorm", fields: [exportedNoteId], references: [id])
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([userId, createdAt])
@@index([inviteToken])
@@index([isPublic])
}
model BrainstormIdea {
id String @id @default(cuid())
sessionId String
waveNumber Int
title String
description String
connectionToSeed String?
noveltyScore Int?
parentIdeaId String?
convertedToNoteId String?
relatedNoteIds String?
status String @default("active")
positionX Float?
positionY Float?
createdAt DateTime @default(now())
createdBy String?
createdByType String? @default("ai")
convertedNote Note? @relation("ConvertedBrainstormIdea", fields: [convertedToNoteId], references: [id])
parentIdea BrainstormIdea? @relation("IdeaTree", fields: [parentIdeaId], references: [id])
children BrainstormIdea[] @relation("IdeaTree")
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
noteRefs BrainstormNoteRef[]
@@index([sessionId])
@@index([waveNumber])
@@index([status])
@@index([parentIdeaId])
@@index([sessionId, status])
@@index([sessionId, waveNumber, createdAt])
}
model BrainstormNoteRef {
id String @id @default(cuid())
ideaId String
noteId String?
relation String
explanation String
verdict String @default("unresolved")
createdAt DateTime @default(now())
visibility String @default("participants")
idea BrainstormIdea @relation(fields: [ideaId], references: [id], onDelete: Cascade)
note Note? @relation(fields: [noteId], references: [id])
@@index([ideaId])
@@index([noteId])
@@index([noteId, relation])
@@index([visibility])
}
model BrainstormParticipant {
id String @id @default(cuid())
sessionId String
userId String
role String @default("viewer")
joinedAt DateTime @default(now())
lastSeenAt DateTime @default(now())
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([sessionId, userId])
@@index([sessionId])
@@index([userId])
@@index([sessionId, userId, role])
}
model BrainstormActivity {
id String @id @default(cuid())
sessionId String
userId String?
action String
details String?
createdAt DateTime @default(now())
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([sessionId, createdAt], map: "BrainstormActivity_sessionId_createdAt_asc_idx")
@@index([sessionId, createdAt(sort: Desc)], map: "BrainstormActivity_sessionId_createdAt_desc_idx")
@@index([sessionId, createdAt])
}
model BrainstormShare {
id String @id @default(cuid())
sessionId String
userId String
sharedBy String
status String @default("pending")
permission String @default("editor")
notifiedAt DateTime?
respondedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
sharer User @relation("SentBrainstormShares", fields: [sharedBy], references: [id], onDelete: Cascade)
user User @relation("ReceivedBrainstormShares", fields: [userId], references: [id], onDelete: Cascade)
@@unique([sessionId, userId])
@@index([userId])
@@index([status])
}
model BrainstormSnapshot {
id String @id @default(cuid())
sessionId String
activityId String?
step Int
label String?
ideaGraph String
createdAt DateTime @default(now())
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
@@index([sessionId, step])
@@index([sessionId, createdAt])
@@index([activityId])
}
model NoteAttachment {
id String @id @default(cuid())
noteId String
fileName String
fileType String
fileSize Int
filePath String
mimeType String
status String @default("pending")
pageCount Int?
error String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
chunks DocumentChunk[]
@@index([noteId])
@@index([status])
}
model DocumentChunk {
id String @id @default(cuid())
attachmentId String
content String
chunkIndex Int
pageNumber Int?
startChar Int?
endChar Int?
metadata String?
createdAt DateTime @default(now())
attachment NoteAttachment @relation(fields: [attachmentId], references: [id], onDelete: Cascade)
@@index([attachmentId])
@@index([attachmentId, chunkIndex])
}