perf: memo GridCard, fuse save fns, fix slash tab active color
This commit is contained in:
@@ -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} />
|
||||
|
||||
Reference in New Issue
Block a user