"use client"; import { v4 as uuid4 } from "uuid"; import classNames from "classnames"; import { Fragment, MouseEvent, RefObject, useCallback, useEffect, useRef, useState } from "react"; import { useModal } from "@/ui/elements/Modal"; import { CaretIcon, CloseIcon, PlusIcon } from "@/ui/Icons"; import { IconButton, PopupMenu, TextArea, Modal, GhostButton, CTAButton } from "@/ui/elements"; import { GraphControlsAPI } from "@/app/(graph)/GraphControls"; import GraphVisualization, { GraphVisualizationAPI } from "@/app/(graph)/GraphVisualization"; import NotebookCellHeader from "./NotebookCellHeader"; import { Cell, Notebook as NotebookType } from "./types"; interface NotebookProps { notebook: NotebookType; runCell: (notebook: NotebookType, cell: Cell, cogneeInstance: string) => Promise; updateNotebook: (updatedNotebook: NotebookType) => void; } export default function Notebook({ notebook, updateNotebook, runCell }: NotebookProps) { useEffect(() => { if (notebook.cells.length === 0) { const newCell: Cell = { id: uuid4(), name: "first cell", type: "code", content: "", }; updateNotebook({ ...notebook, cells: [newCell], }); toggleCellOpen(newCell.id) } }, [notebook, updateNotebook]); const handleCellRun = useCallback((cell: Cell, cogneeInstance: string) => { return runCell(notebook, cell, cogneeInstance); }, [notebook, runCell]); const handleCellAdd = useCallback((afterCellIndex: number, cellType: "markdown" | "code") => { const newCell: Cell = { id: uuid4(), name: "new cell", type: cellType, content: "", }; const newNotebook = { ...notebook, cells: [ ...notebook.cells.slice(0, afterCellIndex + 1), newCell, ...notebook.cells.slice(afterCellIndex + 1), ], }; toggleCellOpen(newCell.id); updateNotebook(newNotebook); }, [notebook, updateNotebook]); const removeCell = useCallback((cell: Cell, event?: MouseEvent) => { event?.preventDefault(); updateNotebook({ ...notebook, cells: notebook.cells.filter((c: Cell) => c.id !== cell.id), }); }, [notebook, updateNotebook]); const { isModalOpen: isRemoveCellConfirmModalOpen, openModal: openCellRemoveConfirmModal, closeModal: closeCellRemoveConfirmModal, confirmAction: handleCellRemoveConfirm, } = useModal(false, removeCell); const handleCellRemove = useCallback((cell: Cell) => { openCellRemoveConfirmModal(cell); }, [openCellRemoveConfirmModal]); const handleCellInputChange = useCallback((notebook: NotebookType, cell: Cell, value: string) => { const newCell = {...cell, content: value }; updateNotebook({ ...notebook, cells: notebook.cells.map((cell: Cell) => (cell.id === newCell.id ? newCell : cell)), }); }, [updateNotebook]); const handleCellUp = useCallback((cell: Cell) => { const index = notebook.cells.indexOf(cell); if (index > 0) { const newCells = [...notebook.cells]; newCells[index] = notebook.cells[index - 1]; newCells[index - 1] = cell; updateNotebook({ ...notebook, cells: newCells, }); } }, [notebook, updateNotebook]); const handleCellDown = useCallback((cell: Cell) => { const index = notebook.cells.indexOf(cell); if (index < notebook.cells.length - 1) { const newCells = [...notebook.cells]; newCells[index] = notebook.cells[index + 1]; newCells[index + 1] = cell; updateNotebook({ ...notebook, cells: newCells, }); } }, [notebook, updateNotebook]); const handleCellRename = useCallback((cell: Cell) => { const newName = prompt("Enter a new name for the cell:"); if (newName) { updateNotebook({ ...notebook, cells: notebook.cells.map((c: Cell) => (c.id === cell.id ? {...c, name: newName } : c)), }); } }, [notebook, updateNotebook]); const [openCells, setOpenCells] = useState(new Set(notebook.cells.map((c: Cell) => c.id))); const toggleCellOpen = (id: string) => { setOpenCells((prev) => { const newState = new Set(prev); if (newState.has(id)) { newState.delete(id) } else { newState.add(id); } return newState; }); }; return ( <>
{notebook.name}
{notebook.cells.map((cell: Cell, index) => (
{cell.type === "code" ? ( <>
{openCells.has(cell.id) && ( <>