/* Copyright 2026 Marimo. All rights reserved. */ import { useAtomValue } from "jotai"; import React, { memo, use } from "react"; import { Handle, Position } from "reactflow"; import { TinyCode } from "@/components/editor/cell/TinyCode"; import { useCellIds } from "@/core/cells/cells"; import { displayCellName, isInternalCellName } from "@/core/cells/names"; import { cn } from "@/utils/cn"; import { type CustomNodeProps, INPUTS_HANDLE_ID, nodeBodyLines, nodeDimensions, OUTPUTS_HANDLE_ID, } from "./elements"; import type { LayoutDirection } from "./types"; import { extractCellPreview } from "./utils/cell-preview"; export const EdgeMarkerContext = React.createContext("LR"); const EQUALITY_CHECK = ( prevProps: CustomNodeProps, nextProps: CustomNodeProps, ) => { const keys: (keyof CustomNodeProps)[] = ["data", "selected", "id"]; return keys.every((key) => prevProps[key] === nextProps[key]); }; export const CustomNode = memo((props: CustomNodeProps) => { const { data, selected } = props; // must match the equality check const cell = useAtomValue(data.atom); const cellIndex = useCellIds().inOrderIds.indexOf(cell.id); const handleColor = selected ? "var(--gray-9)" : "var(--gray-3)"; const edgeMarkers = use(EdgeMarkerContext); const { width, height } = nodeDimensions(data); const isNamed = !isInternalCellName(cell.name); const name = displayCellName(cell.name, cellIndex); // Only collapsed nodes without defs render the preview; skip the parse // otherwise, since it scales with notebook size. const preview = !data.expanded && data.defs.length === 0 ? extractCellPreview(cell.code).text : undefined; return (
{name}
{data.expanded ? ( ) : (
{data.defs.length > 0 ? ( // Defs wrapped into comma-separated lines — taller, not wider. nodeBodyLines(data).map((line) => ( {line} )) ) : ( {preview ?? "empty"} )}
)}
); }, EQUALITY_CHECK); CustomNode.displayName = "CustomNode"; export const nodeTypes = { custom: CustomNode, };