perf: memo GridCard, fuse save fns, fix slash tab active color
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m32s
CI / Deploy production (on server) (push) Has been skipped

This commit is contained in:
Antigravity
2026-06-14 14:06:05 +00:00
parent a8785ed4f1
commit a623454347
120 changed files with 12301 additions and 785 deletions

View File

@@ -13,7 +13,7 @@ import {
Trash2, Copy, Repeat, Link, ChevronRight,
Heading1, Heading2, Heading3, List, ListOrdered,
CheckSquare, Quote, CodeXml, Database,
ArrowUp, ArrowDown, AlignLeft, ClipboardCopy,
ArrowUp, ArrowDown, AlignLeft, ClipboardCopy, Sparkles,
} from 'lucide-react'
import { replaceBlockWithStructuredView } from '@/components/tiptap-structured-view-block-extension'
@@ -80,7 +80,7 @@ function getBlockPlainContent(editor: Editor, blockPos: number, blockNode: PMNod
const from = blockPos + 1
const to = blockPos + node.nodeSize - 1
if (to > from) {
return editor.state.doc.textBetween(from, to, '\n', '\0').trim()
return editor.state.doc.textBetween(from, to, '\n', '').trim()
}
return node.textContent?.trim() ?? ''
}
@@ -208,6 +208,63 @@ export function BlockActionMenu({
onClose()
}, [blockNode, blockPos, editor, noteId, onBlockReferenceCopied, onClose, sourceNoteTitle, t])
const handleCreateDiagram = useCallback(async () => {
const text = getBlockPlainContent(editor, blockPos, blockNode)
if (!text || text.trim().length < 5) {
toast.error(t('blockAction.createDiagramEmpty') || "Le texte est trop court pour générer un diagramme.")
onClose()
return
}
onClose()
const toastId = toast.loading(t('blockAction.createDiagramLoading') || "Génération du diagramme Excalidraw par l'IA...")
try {
const { generateDiagramFromText } = await import('@/app/actions/diagram')
const res = await generateDiagramFromText(text)
if (!res.success || !res.canvasId) {
throw new Error(res.error || "La génération du diagramme a échoué.")
}
const canvasId = res.canvasId
const canvasRes = await fetch(`/api/canvas?id=${encodeURIComponent(canvasId)}`)
const canvasData = await canvasRes.json()
if (!canvasRes.ok || !canvasData.canvas?.data) {
throw new Error("Impossible de charger les données du diagramme généré.")
}
const { exportExcalidrawSceneToPngBlob } = await import('@/lib/client/excalidraw-export-image')
const blob = await exportExcalidrawSceneToPngBlob(canvasData.canvas.data)
if (!blob) {
throw new Error("Échec du rendu du diagramme en image PNG.")
}
const fd = new FormData()
fd.append('file', blob, `diagram-${canvasId.slice(-8)}.png`)
const up = await fetch('/api/upload', { method: 'POST', body: fd })
const upJson = await up.json()
if (!up.ok || !upJson.url) {
throw new Error("Échec du téléversement du diagramme généré.")
}
const imageUrl = upJson.url
const insertPos = blockPos + (blockNode ? blockNode.nodeSize : 0)
const htmlToInsert = `<p><a href="/lab?id=${canvasId}" target="_blank"><img src="${imageUrl}" alt="Diagramme Excalidraw" style="max-width: 100%; border-radius: 8px; border: 1px solid rgba(0,0,0,0.1);" /></a></p><p>🎨 <a href="/lab?id=${canvasId}" target="_blank"><strong>${t('blockAction.createDiagramSuccess') || "Éditer le diagramme dans le Lab Excalidraw"}</strong></a></p>`.trim();
editor.chain().focus().insertContentAt(insertPos, htmlToInsert).run()
toast.success(t('blockAction.createDiagramSuccess') || "Diagramme généré et inséré avec succès !", { id: toastId })
} catch (err: any) {
console.error('[handleCreateDiagram] Error:', err)
toast.error(err.message || "Une erreur est survenue lors de la génération.", { id: toastId })
}
}, [editor, blockPos, blockNode, onClose, t])
const handleTurnInto = useCallback((option: TurnIntoOption) => {
if (blockPos >= 0 && blockNode) {
if (option.isDatabase) {
@@ -312,6 +369,14 @@ export function BlockActionMenu({
<div className="block-action-separator" />
{/* Création de diagramme */}
<button type="button" className="block-action-item" onClick={() => { void handleCreateDiagram() }}>
<Sparkles size={16} className="text-amber-500 transition-all duration-200" />
<span className="font-medium text-amber-700 dark:text-amber-400">{t('blockAction.createDiagram')}</span>
</button>
<div className="block-action-separator" />
{/* Copier */}
<button type="button" className="block-action-item" onClick={() => { void handleCopyContent() }}>
<ClipboardCopy size={16} />