"use client" import { memo, useEffect } from "react" import type { GraphNode } from "@/types" import * as styles from "./node-popover.css" export interface NodePopoverProps { node: GraphNode x: number // Screen X position y: number // Screen Y position onClose: () => void containerBounds?: DOMRect // Optional container bounds to limit backdrop onBackdropClick?: () => void // Optional callback when backdrop is clicked } export const NodePopover = memo(function NodePopover({ node, x, y, onClose, containerBounds, onBackdropClick, }) { // Handle Escape key to close popover useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { onClose() } } window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) }, [onClose]) // Calculate backdrop bounds - use container bounds if provided, otherwise full viewport const backdropStyle = containerBounds ? { left: `${containerBounds.left}px`, top: `${containerBounds.top}px`, width: `${containerBounds.width}px`, height: `${containerBounds.height}px`, } : undefined const backdropClassName = containerBounds ? styles.backdrop : `${styles.backdrop} ${styles.backdropFullscreen}` const handleBackdropClick = () => { onBackdropClick?.() onClose() } return ( <> {/* Invisible backdrop to catch clicks outside */}
{/* Popover content */}
e.stopPropagation()} // Prevent closing when clicking inside className={styles.popoverContainer} style={{ left: `${x}px`, top: `${y}px`, }} > {node.type === "document" ? ( // Document popover
{/* Header */}

Document

{/* Sections */}
{/* Title */}
Title

{(node.data as any).title || "Untitled Document"}

{/* Summary - truncated to 2 lines */} {(node.data as any).summary && (
Summary

{(node.data as any).summary}

)} {/* Type */}
Type

{(node.data as any).type || "Document"}

{/* Memory Count */}
Memory Count

{(node.data as any).memoryEntries?.length || 0} memories

{/* URL */} {((node.data as any).url || (node.data as any).customId) && ( )} {/* Footer with metadata */}
{new Date( (node.data as any).createdAt, ).toLocaleDateString()}
{node.id}
) : ( // Memory popover
{/* Header */}

Memory

{/* Sections */}
{/* Memory content */}
Memory

{(node.data as any).memory || (node.data as any).content || "No content"}

{(node.data as any).isForgotten && (
Forgotten
)} {/* Expires (inline with memory if exists) */} {(node.data as any).forgetAfter && (

Expires:{" "} {new Date( (node.data as any).forgetAfter, ).toLocaleDateString()} {(node.data as any).forgetReason && ` - ${(node.data as any).forgetReason}`}

)}
{/* Space */}
Space

{(node.data as any).spaceId || "Default"}

{/* Footer with metadata */}
{new Date( (node.data as any).createdAt, ).toLocaleDateString()}
{node.id}
)}
) })