| 1 | "use client"; |
| 2 | |
| 3 | import { MarkdownPlugin } from "@platejs/markdown"; |
| 4 | import { BlockSelectionPlugin } from "@platejs/selection/react"; |
| 5 | import { ZapIcon } from "lucide-react"; |
| 6 | import { nanoid } from "platejs"; |
| 7 | import { useEditorRef } from "platejs/react"; |
| 8 | import { useCallback } from "react"; |
| 9 | |
| 10 | import { ANTV_INFOGRAPHIC } from "@/components/notebook/presentation/editor/lib"; |
| 11 | import { type TAntvInfographicElement } from "@/components/notebook/presentation/editor/plugins/antv-infographic-plugin"; |
| 12 | import { ToolbarButton } from "./toolbar"; |
| 13 | |
| 14 | export function TextToDiagramToolbarButton() { |
| 15 | const editor = useEditorRef(); |
| 16 | |
| 17 | const handleClick = useCallback(() => { |
| 18 | // Try to get text from block selection first |
| 19 | const blockSelectionApi = |
| 20 | editor.getApi(BlockSelectionPlugin)?.blockSelection; |
| 21 | const selectedBlocks = blockSelectionApi |
| 22 | ? blockSelectionApi.getNodes() |
| 23 | : []; |
| 24 | const markdownApi = editor.getApi(MarkdownPlugin).markdown; |
| 25 | |
| 26 | let selectedText = ""; |
| 27 | |
| 28 | if (selectedBlocks.length > 0) { |
| 29 | selectedText = markdownApi.serialize({ |
| 30 | value: selectedBlocks.map(([node]) => node), |
| 31 | withBlockId: true, |
| 32 | }); |
| 33 | } else if (editor.selection) { |
| 34 | selectedText = editor.api.string(editor.selection); |
| 35 | |
| 36 | if (!selectedText.trim()) { |
| 37 | const block = editor.api.block({ at: editor.selection }); |
| 38 | |
| 39 | if (block) { |
| 40 | selectedText = markdownApi.serialize({ |
| 41 | value: [block[0]], |
| 42 | withBlockId: true, |
| 43 | }); |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | if (!selectedText || selectedText.trim().length === 0) { |
| 49 | return; |
| 50 | } |
| 51 | |
| 52 | // Create the infographic element in loading state |
| 53 | const infographicElement: TAntvInfographicElement = { |
| 54 | type: ANTV_INFOGRAPHIC, |
| 55 | id: nanoid(), |
| 56 | syntax: "", |
| 57 | isLoading: true, |
| 58 | sourceText: selectedText, |
| 59 | width: "100%", |
| 60 | align: "center", |
| 61 | children: [{ text: "" }], |
| 62 | }; |
| 63 | |
| 64 | if (selectedBlocks.length > 0) { |
| 65 | // Insert after the last selected block |
| 66 | const lastBlockPath = selectedBlocks[selectedBlocks.length - 1]![1]; |
| 67 | const insertPath = [lastBlockPath[0]! + 1]; |
| 68 | editor.tf.insertNodes(infographicElement, { |
| 69 | at: insertPath, |
| 70 | }); |
| 71 | |
| 72 | // Clear the block selection after insertion |
| 73 | editor.getApi(BlockSelectionPlugin)?.blockSelection.unselect(); |
| 74 | } else if (editor.selection) { |
| 75 | // Insert after the current block |
| 76 | const entry = editor.api.block(); |
| 77 | if (entry) { |
| 78 | const [, path] = entry; |
| 79 | editor.tf.insertNodes(infographicElement, { |
| 80 | at: [path[0]! + 1], |
| 81 | }); |
| 82 | } else { |
| 83 | // Fallback: insert at selection |
| 84 | editor.tf.insertNodes(infographicElement); |
| 85 | } |
| 86 | } else { |
| 87 | // Insert at the end if no selection |
| 88 | editor.tf.insertNodes(infographicElement); |
| 89 | } |
| 90 | }, [editor]); |
| 91 | |
| 92 | return ( |
| 93 | <ToolbarButton tooltip="Text to Diagram" onClick={handleClick}> |
| 94 | <ZapIcon /> |
| 95 | </ToolbarButton> |
| 96 | ); |
| 97 | } |
| 98 |