import { Editor, TldrawUiMenuActionItem, TldrawUiMenuItem, TldrawUiMenuSubmenu, TLGeoShape, TLShape, useDefaultHelpers, useActions, } from "tldraw" import { TldrawUiMenuGroup } from "tldraw" import { DefaultContextMenu, DefaultContextMenuContent } from "tldraw" import { TLUiContextMenuProps, useEditor } from "tldraw" import { cameraHistory, } from "./cameraUtils" import { useState, useEffect } from "react" import { saveToPdf } from "../utils/pdfUtils" import { TLFrameShape } from "tldraw" import { searchText } from "../utils/searchUtils" import { llm } from "../utils/llmUtils" import { getEdge } from "@/propagators/tlgraph" import { getCustomActions } from './overrides' import { overrides } from './overrides' import { useGlobalCollection } from "@/collections" const getAllFrames = (editor: Editor) => { return editor .getCurrentPageShapes() .filter((shape): shape is TLFrameShape => shape.type === "frame") .map((frame) => ({ id: frame.id, title: frame.props.name || "Untitled Frame", })) } export function CustomContextMenu(props: TLUiContextMenuProps) { const editor = useEditor() const helpers = useDefaultHelpers() const actions = useActions() const tools = overrides.tools?.(editor, {}, helpers) ?? {} const customActions = getCustomActions(editor) as any const [selectedShapes, setSelectedShapes] = useState([]) const [selectedIds, setSelectedIds] = useState([]) // Collection functionality using the global collection manager const { collection, size } = useGlobalCollection('graph') // Update selection state more frequently useEffect(() => { const updateSelection = () => { setSelectedShapes(editor.getSelectedShapes()) setSelectedIds(editor.getSelectedShapeIds()) } // Initial update updateSelection() // Subscribe to selection changes const unsubscribe = editor.addListener("change", updateSelection) return () => { if (typeof unsubscribe === "function") { ;(unsubscribe as () => void)() } } }, [editor]) const hasSelection = selectedIds.length > 0 const hasCameraHistory = cameraHistory.length > 0 // Collection handlers const handleAddToCollection = () => { if (collection) { collection.add(editor.getSelectedShapes()) editor.selectNone() } } const handleRemoveFromCollection = () => { if (collection) { collection.remove(editor.getSelectedShapes()) editor.selectNone() } } const handleHighlightCollection = () => { if (collection) { editor.setHintingShapes([...collection.getShapes().values()]) } } // Check if selected shapes are already in collection const selectedShapesInCollection = collection ? selectedShapes.filter(shape => collection.getShapes().has(shape.id)) : [] const hasSelectedShapesInCollection = selectedShapesInCollection.length > 0 const allSelectedShapesInCollection = selectedShapes.length > 0 && selectedShapesInCollection.length === selectedShapes.length // Check if collection functionality is available const hasCollectionContext = collection !== null // Keyboard shortcut for adding to collection useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'c' && event.altKey && event.shiftKey && !event.ctrlKey && !event.metaKey) { event.preventDefault() if (hasSelection && collection && !allSelectedShapesInCollection) { handleAddToCollection() } } } document.addEventListener('keydown', handleKeyDown) return () => { document.removeEventListener('keydown', handleKeyDown) } }, [hasSelection, collection, allSelectedShapesInCollection]) //TO DO: Fix camera history for camera revert return ( {/* Essential non-edit commands from default context menu */} actions['select-all'].onSelect("context-menu")} /> actions.undo.onSelect("context-menu")} /> actions.redo.onSelect("context-menu")} /> {/* Frames List - Moved to top */} {getAllFrames(editor).map((frame) => ( { const shape = editor.getShape(frame.id) if (shape) { editor.zoomToBounds(editor.getShapePageBounds(shape)!, { animation: { duration: 400, easing: (t) => t * (2 - t) }, }) editor.select(frame.id) } }} /> ))} {/* Camera Controls Group */} {/* Edit Actions Group */} actions.cut.onSelect("context-menu")} /> actions.copy.onSelect("context-menu")} /> actions.paste.onSelect("context-menu")} /> actions.duplicate.onSelect("context-menu")} /> actions.delete.onSelect("context-menu")} /> {/* Creation Tools Group */} {/* Collections Group */} {/* TODO: FIX & IMPLEMENT BROADCASTING*/} {/* { editor.markHistoryStoppingPoint('start-broadcast') editor.updateInstanceState({ isBroadcasting: true }) const url = new URL(window.location.href) url.searchParams.set("followId", editor.user.getId()) window.history.replaceState(null, "", url.toString()) }} /> { editor.markHistoryStoppingPoint('stop-broadcast') editor.updateInstanceState({ isBroadcasting: false }) editor.stopFollowingUser() const url = new URL(window.location.href) url.searchParams.delete("followId") window.history.replaceState(null, "", url.toString()) }} /> */} searchText(editor)} /> ) }