'use client'; import { useState, useCallback } from 'react'; import { MermaidViewer } from './MermaidViewer'; import { ReactFlowEditor } from './ReactFlowEditor'; import { useGraphData, KnowledgeNode, NodeCreateInput, EdgeCreateInput } 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} /> )}
); }