"use client"; import { useState, useCallback } from "react"; import { MermaidViewer } from "./MermaidViewer"; import { ReactFlowEditor } from "./ReactFlowEditor"; import type { KnowledgeNode, NodeCreateInput, EdgeCreateInput } from "./hooks/useGraphData"; import { useGraphData } from "./hooks/useGraphData"; import { NodeCreateModal } from "./controls/NodeCreateModal"; import { ExportButton } from "./controls/ExportButton"; type ViewMode = "interactive" | "mermaid"; type MermaidStyle = "flowchart" | "mindmap"; interface MindmapViewerProps { rootId?: string; maxDepth?: number; className?: string; readOnly?: boolean; } export function MindmapViewer({ rootId, maxDepth = 3, className = "", readOnly = false, }: MindmapViewerProps) { const [viewMode, setViewMode] = useState("interactive"); const [mermaidStyle, setMermaidStyle] = useState("flowchart"); const [showCreateModal, setShowCreateModal] = useState(false); const [selectedNode, setSelectedNode] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState([]); const [isSearching, setIsSearching] = useState(false); const { graph, mermaid, statistics, isLoading, error, fetchMermaid, createNode, updateNode, deleteNode, createEdge, searchNodes, } = useGraphData({ ...(rootId && { rootId }), maxDepth }); const handleViewModeChange = useCallback( async (mode: ViewMode) => { setViewMode(mode); if (mode === "mermaid") { await fetchMermaid(mermaidStyle); } }, [fetchMermaid, mermaidStyle] ); const handleMermaidStyleChange = useCallback( async (style: MermaidStyle) => { setMermaidStyle(style); if (viewMode === "mermaid") { await fetchMermaid(style); } }, [viewMode, fetchMermaid] ); const handleCreateNode = useCallback( async (nodeData: NodeCreateInput) => { await createNode(nodeData); setShowCreateModal(false); }, [createNode] ); const handleDeleteNode = useCallback( async (id: string) => { await deleteNode(id); setSelectedNode(null); }, [deleteNode] ); const handleCreateEdge = useCallback( async (edgeData: EdgeCreateInput) => { await createEdge(edgeData); }, [createEdge] ); const handleSearch = useCallback( async (query: string) => { setSearchQuery(query); if (!query.trim()) { setSearchResults([]); return; } setIsSearching(true); try { const results = await searchNodes(query); setSearchResults(results); } catch (_err) { // Search failed - results will remain empty setSearchResults([]); } finally { setIsSearching(false); } }, [searchNodes] ); const handleSelectSearchResult = useCallback((node: KnowledgeNode) => { setSelectedNode(node); setSearchResults([]); setSearchQuery(""); }, []); if (error) { return (
Error loading graph
{error}
); } return (
{/* Toolbar */}
{/* View mode toggle */}
{/* Mermaid style selector (only shown in mermaid mode) */} {viewMode === "mermaid" && ( )} {/* Search bar */}
handleSearch(e.target.value)} placeholder="Search nodes..." className="px-3 py-1.5 pl-8 text-sm rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 w-48" /> {/* Search results dropdown */} {searchResults.length > 0 && (
{searchResults.map((result) => ( ))}
)} {isSearching && (
)}
{/* Statistics */} {statistics && (
{statistics.node_count} nodes, {statistics.edge_count} edges
)}
{!readOnly && ( )}
{/* Main content */}
{isLoading && (
)} {viewMode === "interactive" && graph && ( )} {viewMode === "mermaid" && mermaid && ( )} {!graph && !isLoading && (

No nodes yet

Create your first node to get started

{!readOnly && ( )}
)}
{/* Selected node details panel */} {selectedNode && (

{selectedNode.title}

{selectedNode.node_type} {selectedNode.domain && ` • ${selectedNode.domain}`}

{selectedNode.content && (

{selectedNode.content}

)}
)} {/* Create node modal */} {showCreateModal && ( { setShowCreateModal(false); }} onCreate={handleCreateNode} /> )}
); }