feat: add slides generation tool with multiple slide types
Some checks failed
CI / Lint, Test & Build (push) Failing after 17s
CI / Deploy production (on server) (push) Has been skipped

- Add slides.tool.ts with support for title, bullets, chart, stats, table, cards, timeline, quote, comparison, equation, image, summary slide types
- Chart types: bar, horizontal-bar, line, donut, radar
- Integrate with agent executor and canvas system
- Add multilingual support (en/fr)
- Various UI improvements and bug fixes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Antigravity
2026-05-22 17:18:48 +00:00
parent 0f6b9509da
commit 5728452b4a
68 changed files with 6990 additions and 2584 deletions

View File

@@ -29,6 +29,7 @@ import {
PinOff,
Sparkles,
Home,
Network,
} from 'lucide-react'
import { useLanguage } from '@/lib/i18n'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
@@ -182,6 +183,7 @@ function SidebarCarnetItem({
isExpanded,
toggleExpand,
hasChildNotebooks,
hasActiveDescendant,
}: {
carnet: { id: string; name: string; initial: string; isPrivate?: boolean }
isActive: boolean
@@ -201,6 +203,7 @@ function SidebarCarnetItem({
isExpanded: boolean
toggleExpand: () => void
hasChildNotebooks?: boolean
hasActiveDescendant?: boolean
}) {
const { t, language } = useLanguage()
const isRtl = language === 'fa' || language === 'ar'
@@ -287,6 +290,9 @@ function SidebarCarnetItem({
{carnet.name}
</span>
{carnet.isPrivate && <Lock size={10} className="text-concrete/60 shrink-0" />}
{!isActive && hasActiveDescendant && (
<span className="w-1.5 h-1.5 rounded-full bg-brand-accent/70 shrink-0" />
)}
</div>
<div className="flex items-center gap-1 opacity-0 group-hover/item:opacity-100 transition-opacity shrink-0">
@@ -424,6 +430,19 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
const [renamingNotebook, setRenamingNotebook] = useState<Notebook | null>(null)
const [renameValue, setRenameValue] = useState('')
const [isDark, setIsDark] = useState(false)
const [isMobileOpen, setIsMobileOpen] = useState(false)
// Écoute l'événement d'ouverture du menu mobile
useEffect(() => {
const handler = () => setIsMobileOpen(true)
window.addEventListener('open-mobile-sidebar', handler)
return () => window.removeEventListener('open-mobile-sidebar', handler)
}, [])
// Ferme la sidebar mobile lors d'une navigation
useEffect(() => {
setIsMobileOpen(false)
}, [pathname, searchParams])
useEffect(() => {
setIsDark(document.documentElement.classList.contains('dark'))
@@ -631,22 +650,6 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
} catch {}
}, [])
// Auto-expand all parent notebooks on first load
useEffect(() => {
if (orderedNotebooks.length === 0) return
const parentIds = new Set<string>()
for (const nb of orderedNotebooks) {
if (nb.parentId) parentIds.add(nb.parentId)
}
if (parentIds.size > 0) {
setExpandedIds(prev => {
const next = new Set(prev)
for (const id of parentIds) next.add(id)
return next
})
}
}, [orderedNotebooks])
const togglePin = useCallback((id: string) => {
setPinnedIds(prev => {
const next = new Set(prev)
@@ -730,8 +733,8 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
return desc.some(c => currentNotebookId === c.id || hasDescendant(c.id))
}
const hasActiveDescendant = hasDescendant(notebook.id)
// A notebook stays expanded if: manually expanded, has active descendant, OR is pinned
const isExpanded = expandedIds.has(notebook.id) || hasActiveDescendant || pinnedIds.has(notebook.id)
// A notebook stays expanded if: manually expanded OR is pinned
const isExpanded = expandedIds.has(notebook.id) || pinnedIds.has(notebook.id)
return (
<motion.div key={notebook.id}>
@@ -784,7 +787,6 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
toggleExpand(notebook.id)
} else {
handleCarnetClick(notebook.id)
if (!expandedIds.has(notebook.id)) toggleExpand(notebook.id)
}
}}
onNoteClick={handleNoteClick}
@@ -802,6 +804,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
isExpanded={isExpanded}
toggleExpand={() => toggleExpand(notebook.id)}
hasChildNotebooks={children.length > 0}
hasActiveDescendant={hasActiveDescendant}
/>
</div>
{isExpanded && renderCarnetTree(notebook.id, level + 1)}
@@ -812,10 +815,29 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
return (
<>
{/* Overlay mobile */}
<AnimatePresence>
{isMobileOpen && (
<motion.div
key="sidebar-backdrop"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className="fixed inset-0 bg-black/40 backdrop-blur-sm z-[60] md:hidden"
onClick={() => setIsMobileOpen(false)}
/>
)}
</AnimatePresence>
<aside
className={cn(
'hidden h-full min-h-0 w-72 shrink-0 flex-col lg:w-80 md:flex',
'border-e border-border/40 bg-white/30 backdrop-blur-md sidebar-shadow dark:border-white/6 dark:bg-[#151515] dark:backdrop-blur-none',
// Mobile: fixed overlay, slide in/out
'fixed inset-y-0 start-0 z-[70] md:relative md:z-auto',
'h-full min-h-0 w-72 lg:w-80 shrink-0 flex flex-col',
'transition-transform duration-300 ease-in-out',
isMobileOpen ? 'translate-x-0 shadow-2xl' : '-translate-x-full md:translate-x-0',
'border-e border-border/40 bg-white/95 md:bg-white/30 backdrop-blur-md sidebar-shadow dark:border-white/6 dark:bg-[#151515] dark:backdrop-blur-none',
className
)}
>
@@ -1145,7 +1167,25 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
)}
</Link>
<div className="my-2 h-px bg-border/20 mx-2" />
{/* ── Intelligence section ── */}
<div className="pt-3 border-t border-border/20 mx-2 mt-1 space-y-0.5">
<p className="text-[9px] font-bold text-muted-ink tracking-[0.2em] uppercase px-1 mb-1 opacity-60">Intelligence</p>
<Link
href="/graph"
className={cn(
'w-full flex items-center gap-3 px-3 py-2 text-[12px] transition-all font-medium group rounded-xl',
pathname === '/graph'
? 'bg-indigo-500/10 text-indigo-500'
: 'text-muted-ink hover:text-indigo-500 hover:bg-indigo-500/5'
)}
>
<Network
size={14}
className={pathname === '/graph' ? 'text-indigo-500' : 'text-muted-ink group-hover:text-indigo-500'}
/>
<span className="flex-1">Vue en graphe</span>
</Link>
</div>
</div>
</div>
</aside>