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

@@ -482,7 +482,7 @@ export const NoteCard = memo(function NoteCard({
>
{/* Drag Handle - Only visible on mobile/touch devices */}
<div
className="muuri-drag-handle absolute top-2 left-2 z-20 cursor-grab active:cursor-grabbing p-2 md:hidden"
className="muuri-drag-handle absolute top-2 start-2 z-20 cursor-grab active:cursor-grabbing p-2 md:hidden"
aria-label={t('notes.dragToReorder') || 'Drag to reorder'}
title={t('notes.dragToReorder') || 'Drag to reorder'}
>
@@ -490,7 +490,7 @@ export const NoteCard = memo(function NoteCard({
</div>
{/* Move to Notebook Dropdown Menu — hidden in trash */}
{!isTrashView && <div onClick={(e) => e.stopPropagation()} className="absolute top-2 right-2 z-20">
{!isTrashView && <div onClick={(e) => e.stopPropagation()} className="absolute top-2 end-2 z-20">
<DropdownMenu open={showNotebookMenu} onOpenChange={setShowNotebookMenu}>
<DropdownMenuTrigger asChild>
<Button
@@ -507,7 +507,7 @@ export const NoteCard = memo(function NoteCard({
{t('notebookSuggestion.moveToNotebook')}
</div>
<DropdownMenuItem onClick={() => handleMoveToNotebook(null)}>
<StickyNote className="h-4 w-4 mr-2" />
<StickyNote className="h-4 w-4 me-2" />
{t('notebookSuggestion.generalNotes')}
</DropdownMenuItem>
{notebooks.filter(nb => !nb.parentId && !nb.trashedAt).map((notebook: any) => {
@@ -523,11 +523,11 @@ export const NoteCard = memo(function NoteCard({
<DropdownMenuSubTrigger className="gap-2">
<NotebookIcon className="h-4 w-4" />
{notebook.name}
<ChevronRight className="h-3 w-3 ml-auto opacity-50" />
<ChevronRight className="h-3 w-3 ms-auto opacity-50 rtl:rotate-180" />
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem onClick={() => handleMoveToNotebook(notebook.id)}>
<NotebookIcon className="h-4 w-4 mr-2" />
<NotebookIcon className="h-4 w-4 me-2" />
{notebook.name}
</DropdownMenuItem>
{descendants.map((child: any) => {
@@ -544,8 +544,8 @@ export const NoteCard = memo(function NoteCard({
})()
return (
<DropdownMenuItem key={child.id} onClick={() => handleMoveToNotebook(child.id)}>
<NotebookIcon className="h-4 w-4 mr-2" />
<span className="ml-{depth * 2}">{child.name}</span>
<NotebookIcon className="h-4 w-4 me-2" />
<span style={{ marginInlineStart: `${depth * 0.5}rem` }}>{child.name}</span>
</DropdownMenuItem>
)
})}
@@ -555,14 +555,14 @@ export const NoteCard = memo(function NoteCard({
}
return (
<DropdownMenuItem key={notebook.id} onClick={() => handleMoveToNotebook(notebook.id)}>
<NotebookIcon className="h-4 w-4 mr-2" />
<NotebookIcon className="h-4 w-4 me-2" />
{notebook.name}
</DropdownMenuItem>
)
})}
<DropdownMenuSeparator />
<DropdownMenuItem className="text-muted-foreground" onSelect={() => onCreateSubNotebook?.()}>
<Plus className="h-4 w-4 mr-2" />
<Plus className="h-4 w-4 me-2" />
{t('notebook.createSubNotebook') || 'Nouveau sous-carnet…'}
</DropdownMenuItem>
</DropdownMenuContent>
@@ -575,7 +575,7 @@ export const NoteCard = memo(function NoteCard({
size="sm"
data-testid="pin-button"
className={cn(
"absolute top-2 right-12 z-20 h-8 w-8 p-0 rounded-md transition-opacity",
"absolute top-2 end-12 z-20 h-8 w-8 p-0 rounded-md transition-opacity",
optimisticNote.isPinned ? "opacity-100" : "opacity-0 group-hover:opacity-100"
)}
onClick={(e) => {
@@ -593,7 +593,7 @@ export const NoteCard = memo(function NoteCard({
{/* Reminder Icon - Move slightly if pin button is there */}
{note.reminder && new Date(note.reminder) > new Date() && (
<Bell
className="absolute top-3 right-10 h-4 w-4 text-primary"
className="absolute top-3 end-10 h-4 w-4 text-primary"
/>
)}
@@ -604,7 +604,7 @@ export const NoteCard = memo(function NoteCard({
{t('memoryEcho.fused')}
<button
onClick={handleRemoveFusedBadge}
className="ml-1 opacity-0 group-hover/badge:opacity-100 hover:opacity-100 transition-opacity"
className="ms-1 opacity-0 group-hover/badge:opacity-100 hover:opacity-100 transition-opacity"
title={t('notes.remove') || 'Remove'}
>
<Trash2 className="h-2.5 w-2.5" />
@@ -614,7 +614,7 @@ export const NoteCard = memo(function NoteCard({
{/* Title */}
{note.title && (
<h3 dir="auto" className="text-lg font-heading font-semibold mb-2 pr-20 text-foreground leading-tight tracking-tight flex items-center gap-2">
<h3 dir="auto" className="text-lg font-heading font-semibold mb-2 pe-20 text-foreground leading-tight tracking-tight flex items-center gap-2">
{(() => {
const TypeIcon = NOTE_TYPE_ICONS[note.type] || AlignLeft
return <TypeIcon className="h-4 w-4 shrink-0 text-muted-foreground/50" />
@@ -653,7 +653,7 @@ export const NoteCard = memo(function NoteCard({
handleLeaveShare()
}}
>
<LogOut className="h-3 w-3 mr-1" />
<LogOut className="h-3 w-3 me-1" />
{t('notes.leaveShare')}
</Button>
</div>
@@ -727,7 +727,7 @@ export const NoteCard = memo(function NoteCard({
{owner && (
<div
className={cn(
"absolute bottom-2 left-2 z-20",
"absolute bottom-2 start-2 z-20",
"w-6 h-6 rounded-full text-white text-[10px] font-semibold flex items-center justify-center",
getAvatarColor(owner.name || owner.email || 'Unknown')
)}
@@ -758,7 +758,7 @@ export const NoteCard = memo(function NoteCard({
noteId={note.id}
currentReminder={reminderDate}
onUpdateReminder={handleUpdateReminder}
className="absolute bottom-0 left-0 right-0 p-2 opacity-100 md:opacity-0 group-hover:opacity-100 transition-opacity"
className="absolute bottom-0 start-0 end-0 p-2 opacity-100 md:opacity-0 group-hover:opacity-100 transition-opacity"
/>
)}
@@ -776,7 +776,7 @@ export const NoteCard = memo(function NoteCard({
)}
{/* Connections Badge - Bottom right (spec: amber, absolute) */}
<div className="absolute bottom-2 right-2 z-10">
<div className="absolute bottom-2 end-2 z-10">
<ConnectionsBadge
noteId={note.id}
onClick={() => {