entangle-ui 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +766 -0
- package/README.md +8 -6
- package/dist/esm/assets/src/components/controls/FileTree/FileTree.css.ts.vanilla-YmktPvnr.css +48 -0
- package/dist/esm/assets/src/components/controls/Select/{Select.css.ts.vanilla-oZnFUorL.css → Select.css.ts.vanilla-Cpu2BvWL.css} +3 -0
- package/dist/esm/assets/src/components/controls/TreeView/{TreeNode.css.ts.vanilla-B_f8pUb8.css → TreeNode.css.ts.vanilla-DXPJRWZZ.css} +31 -24
- package/dist/esm/assets/src/components/editor/AssetBrowser/AssetBrowser.css.ts.vanilla-B2S4cEiY.css +262 -0
- package/dist/esm/assets/src/components/editor/Minimap/Minimap.css.ts.vanilla--rNdWQwy.css +133 -0
- package/dist/esm/assets/src/components/editor/NodeGraph/NodeGraph.css.ts.vanilla-ClF4YZvl.css +401 -0
- package/dist/esm/assets/src/components/editor/PropertyInspector/{PropertyPanel.css.ts.vanilla-RYv_XgOu.css → PropertyPanel.css.ts.vanilla-Dpr_k5I3.css} +4 -1
- package/dist/esm/assets/src/components/editor/PropertyInspector/{PropertyRow.css.ts.vanilla-C3YZnnHb.css → PropertyRow.css.ts.vanilla-pq1Uhd2a.css} +15 -8
- package/dist/esm/assets/src/components/editor/PropertyInspector/{PropertySection.css.ts.vanilla-DK12opZc.css → PropertySection.css.ts.vanilla-6MWWAqyd.css} +50 -32
- package/dist/esm/assets/src/components/editor/Timeline/Timeline.css.ts.vanilla-DDX6Zalo.css +203 -0
- package/dist/esm/assets/src/components/feedback/LogView/LogView.css.ts.vanilla-nB5ENOpw.css +312 -0
- package/dist/esm/assets/src/components/navigation/Menu/Menu.css.ts.vanilla-B5iVe5S2.css +88 -0
- package/dist/esm/assets/src/components/navigation/PathBar/PathBar.css.ts.vanilla-BIEzQQOf.css +10 -0
- package/dist/esm/assets/src/components/primitives/viewport/Viewport.css.ts.vanilla-CkFBJ7Vo.css +65 -0
- package/dist/esm/assets/src/components/shell/FloatingPanel/{FloatingPanel.css.ts.vanilla-1rIAXEFp.css → FloatingPanel.css.ts.vanilla-CRaeYJ2b.css} +20 -13
- package/dist/esm/assets/src/theme/{darkTheme.css.ts.vanilla-ab1WD4dr.css → darkTheme.css.ts.vanilla-BHZ3ecRn.css} +3 -3
- package/dist/esm/assets/src/theme/{lightTheme.css.ts.vanilla-OaRI_wIc.css → lightTheme.css.ts.vanilla-Ct6f9Iym.css} +3 -3
- package/dist/esm/components/Icons/AddIcon.js.map +1 -1
- package/dist/esm/components/Icons/AiChatIcon.js.map +1 -1
- package/dist/esm/components/Icons/AiSparklesIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArchiveIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowDownIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowLeftIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowRightIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowUpIcon.js.map +1 -1
- package/dist/esm/components/Icons/BookmarkIcon.js.map +1 -1
- package/dist/esm/components/Icons/BugIcon.js.map +1 -1
- package/dist/esm/components/Icons/BuildingIcon.js.map +1 -1
- package/dist/esm/components/Icons/CalendarIcon.js.map +1 -1
- package/dist/esm/components/Icons/CameraIcon.js +31 -0
- package/dist/esm/components/Icons/CameraIcon.js.map +1 -0
- package/dist/esm/components/Icons/CheckIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronDownIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronLeftIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronRightIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronUpIcon.js.map +1 -1
- package/dist/esm/components/Icons/CircleIcon.js.map +1 -1
- package/dist/esm/components/Icons/ClockIcon.js.map +1 -1
- package/dist/esm/components/Icons/CloseIcon.js.map +1 -1
- package/dist/esm/components/Icons/CloudUploadIcon.js.map +1 -1
- package/dist/esm/components/Icons/CodeIcon.js.map +1 -1
- package/dist/esm/components/Icons/CopyIcon.js.map +1 -1
- package/dist/esm/components/Icons/CubeIcon.js +31 -0
- package/dist/esm/components/Icons/CubeIcon.js.map +1 -0
- package/dist/esm/components/Icons/CutIcon.js.map +1 -1
- package/dist/esm/components/Icons/DotsHorizontalIcon.js.map +1 -1
- package/dist/esm/components/Icons/DotsVerticalIcon.js.map +1 -1
- package/dist/esm/components/Icons/DownloadIcon.js.map +1 -1
- package/dist/esm/components/Icons/EditIcon.js.map +1 -1
- package/dist/esm/components/Icons/ErrorIcon.js.map +1 -1
- package/dist/esm/components/Icons/ExternalLinkIcon.js.map +1 -1
- package/dist/esm/components/Icons/EyeDropperIcon.js.map +1 -1
- package/dist/esm/components/Icons/EyeIcon.js.map +1 -1
- package/dist/esm/components/Icons/FileTextIcon.js.map +1 -1
- package/dist/esm/components/Icons/FilterIcon.js.map +1 -1
- package/dist/esm/components/Icons/FirstIcon.js.map +1 -1
- package/dist/esm/components/Icons/FolderCogIcon.js.map +1 -1
- package/dist/esm/components/Icons/FolderIcon.js.map +1 -1
- package/dist/esm/components/Icons/FolderOpenIcon.js +2 -2
- package/dist/esm/components/Icons/FolderOpenIcon.js.map +1 -1
- package/dist/esm/components/Icons/FullscreenIcon.js.map +1 -1
- package/dist/esm/components/Icons/GitBranchIcon.js.map +1 -1
- package/dist/esm/components/Icons/GridIcon.js.map +1 -1
- package/dist/esm/components/Icons/HeartIcon.js.map +1 -1
- package/dist/esm/components/Icons/HelpIcon.js.map +1 -1
- package/dist/esm/components/Icons/HomeIcon.js.map +1 -1
- package/dist/esm/components/Icons/ImageIcon.js +31 -0
- package/dist/esm/components/Icons/ImageIcon.js.map +1 -0
- package/dist/esm/components/Icons/InfoIcon.js.map +1 -1
- package/dist/esm/components/Icons/LastIcon.js.map +1 -1
- package/dist/esm/components/Icons/LinkIcon.js.map +1 -1
- package/dist/esm/components/Icons/ListIcon.js.map +1 -1
- package/dist/esm/components/Icons/LockIcon.js.map +1 -1
- package/dist/esm/components/Icons/MaximizeIcon.js.map +1 -1
- package/dist/esm/components/Icons/MenuIcon.js.map +1 -1
- package/dist/esm/components/Icons/MinimizeIcon.js.map +1 -1
- package/dist/esm/components/Icons/MinusIcon.js.map +1 -1
- package/dist/esm/components/Icons/OrbitIcon.js +31 -0
- package/dist/esm/components/Icons/OrbitIcon.js.map +1 -0
- package/dist/esm/components/Icons/PasteIcon.js.map +1 -1
- package/dist/esm/components/Icons/PauseIcon.js +2 -2
- package/dist/esm/components/Icons/PauseIcon.js.map +1 -1
- package/dist/esm/components/Icons/PinIcon.js.map +1 -1
- package/dist/esm/components/Icons/PlayIcon.js.map +1 -1
- package/dist/esm/components/Icons/RedoIcon.js.map +1 -1
- package/dist/esm/components/Icons/RefreshIcon.js.map +1 -1
- package/dist/esm/components/Icons/RobotIcon.js.map +1 -1
- package/dist/esm/components/Icons/RotateIcon.js +32 -0
- package/dist/esm/components/Icons/RotateIcon.js.map +1 -0
- package/dist/esm/components/Icons/SaveIcon.js.map +1 -1
- package/dist/esm/components/Icons/SearchIcon.js.map +1 -1
- package/dist/esm/components/Icons/SendIcon.js.map +1 -1
- package/dist/esm/components/Icons/SettingsIcon.js.map +1 -1
- package/dist/esm/components/Icons/SortIcon.js.map +1 -1
- package/dist/esm/components/Icons/SpinIcon.js +32 -0
- package/dist/esm/components/Icons/SpinIcon.js.map +1 -0
- package/dist/esm/components/Icons/StarIcon.js.map +1 -1
- package/dist/esm/components/Icons/StopIcon.js.map +1 -1
- package/dist/esm/components/Icons/SuccessIcon.js.map +1 -1
- package/dist/esm/components/Icons/TagIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentAlignedIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentAutoIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentFreeIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentLinearIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentMirroredIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentStepIcon.js.map +1 -1
- package/dist/esm/components/Icons/TerminalIcon.js.map +1 -1
- package/dist/esm/components/Icons/TrashIcon.js.map +1 -1
- package/dist/esm/components/Icons/UndoIcon.js.map +1 -1
- package/dist/esm/components/Icons/UnlinkIcon.js.map +1 -1
- package/dist/esm/components/Icons/UnlockIcon.js.map +1 -1
- package/dist/esm/components/Icons/UploadIcon.js.map +1 -1
- package/dist/esm/components/Icons/UserIcon.js.map +1 -1
- package/dist/esm/components/Icons/UsersIcon.js.map +1 -1
- package/dist/esm/components/Icons/WarningIcon.js.map +1 -1
- package/dist/esm/components/Icons/ZoomFitIcon.js +32 -0
- package/dist/esm/components/Icons/ZoomFitIcon.js.map +1 -0
- package/dist/esm/components/Icons/ZoomInIcon.js.map +1 -1
- package/dist/esm/components/Icons/ZoomOutIcon.js.map +1 -1
- package/dist/esm/components/controls/CartesianPicker/CartesianPicker.js +3 -3
- package/dist/esm/components/controls/CartesianPicker/CartesianPicker.js.map +1 -1
- package/dist/esm/components/controls/CartesianPicker/useCartesianInteraction.js +7 -5
- package/dist/esm/components/controls/CartesianPicker/useCartesianInteraction.js.map +1 -1
- package/dist/esm/components/controls/CartesianPicker/useCartesianRenderer.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/AlphaSlider.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorArea.js +2 -2
- package/dist/esm/components/controls/ColorPicker/ColorArea.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorInputs.js +16 -2
- package/dist/esm/components/controls/ColorPicker/ColorInputs.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPalette.js +3 -1
- package/dist/esm/components/controls/ColorPicker/ColorPalette.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPicker.js +6 -6
- package/dist/esm/components/controls/ColorPicker/ColorPicker.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPresets.js +3 -1
- package/dist/esm/components/controls/ColorPicker/ColorPresets.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorSwatch.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/EyeDropper.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/HueSlider.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/colorUtils.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/palettes/material.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/palettes/professional.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/palettes/tailwind.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/useColor.js.map +1 -1
- package/dist/esm/components/controls/Combobox/Combobox.js +46 -23
- package/dist/esm/components/controls/Combobox/Combobox.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveCanvas.js +11 -4
- package/dist/esm/components/controls/CurveEditor/CurveCanvas.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveEditor.js +4 -4
- package/dist/esm/components/controls/CurveEditor/CurveEditor.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveToolbar.js +7 -5
- package/dist/esm/components/controls/CurveEditor/CurveToolbar.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/curvePresets.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/curveUtils.js +13 -15
- package/dist/esm/components/controls/CurveEditor/curveUtils.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveInteraction.js +72 -50
- package/dist/esm/components/controls/CurveEditor/useCurveInteraction.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveRenderer.js +11 -3
- package/dist/esm/components/controls/CurveEditor/useCurveRenderer.js.map +1 -1
- package/dist/esm/components/controls/FileTree/FileTree.css.js +9 -0
- package/dist/esm/components/controls/FileTree/FileTree.css.js.map +1 -0
- package/dist/esm/components/controls/FileTree/FileTree.js +252 -0
- package/dist/esm/components/controls/FileTree/FileTree.js.map +1 -0
- package/dist/esm/components/controls/FileTree/fileTreeIcons.js +129 -0
- package/dist/esm/components/controls/FileTree/fileTreeIcons.js.map +1 -0
- package/dist/esm/components/controls/FileTree/fileTreeLabels.js +8 -0
- package/dist/esm/components/controls/FileTree/fileTreeLabels.js.map +1 -0
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js +115 -0
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js.map +1 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.js +3 -6
- package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -1
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js +46 -15
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/controls/NumberInput/NumberInput.js +11 -4
- package/dist/esm/components/controls/NumberInput/NumberInput.js.map +1 -1
- package/dist/esm/components/controls/NumberInput/useNumberInput.js +6 -2
- package/dist/esm/components/controls/NumberInput/useNumberInput.js.map +1 -1
- package/dist/esm/components/controls/Select/Select.css.js +1 -1
- package/dist/esm/components/controls/Select/Select.js +44 -16
- package/dist/esm/components/controls/Select/Select.js.map +1 -1
- package/dist/esm/components/controls/Slider/Slider.js +6 -8
- package/dist/esm/components/controls/Slider/Slider.js.map +1 -1
- package/dist/esm/components/controls/TagInput/TagInput.js +4 -2
- package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeNode.css.js +9 -8
- package/dist/esm/components/controls/TreeView/TreeNode.css.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeNode.js +42 -7
- package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeView.js +18 -6
- package/dist/esm/components/controls/TreeView/TreeView.js.map +1 -1
- package/dist/esm/components/controls/TreeView/useTreeState.js +2 -6
- package/dist/esm/components/controls/TreeView/useTreeState.js.map +1 -1
- package/dist/esm/components/controls/VectorInput/VectorInput.js +13 -4
- package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
- package/dist/esm/components/data/DataTable/DataTable.js +25 -8
- package/dist/esm/components/data/DataTable/DataTable.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.css.js +46 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.css.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.js +254 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserAnnouncementLive.js +17 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserAnnouncementLive.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserBreadcrumbs.js +25 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserBreadcrumbs.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js +140 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContext.js +81 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContext.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContextMenu.js +123 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContextMenu.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js +136 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js +137 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js +226 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js +189 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserRenameField.js +63 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserRenameField.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSidebar.js +19 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSidebar.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStatusBar.js +24 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStatusBar.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStore.js +176 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStore.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js +120 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js +134 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFilter.js +90 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFilter.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFormat.js +32 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFormat.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserGeometry.js +70 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserGeometry.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserKeyboard.js +62 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserKeyboard.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserLabels.js +52 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserLabels.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/slots.js +18 -0
- package/dist/esm/components/editor/AssetBrowser/slots.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserAnnouncement.js +19 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserAnnouncement.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js +70 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js +108 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js +192 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridKeyboardNav.js +91 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridKeyboardNav.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js +91 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js +128 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js +72 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js +141 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js +113 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js +174 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js.map +1 -0
- package/dist/esm/components/editor/ChatPanel/ChatActionBar.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatAttachment.js +8 -2
- package/dist/esm/components/editor/ChatPanel/ChatAttachment.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatBubble.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js +20 -4
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatContextChip.js +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatContextChip.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatEmptyState.js +3 -1
- package/dist/esm/components/editor/ChatPanel/ChatEmptyState.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatIcons.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatInput.js +10 -3
- package/dist/esm/components/editor/ChatPanel/ChatInput.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatInputToolbar.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMarkdownRenderer.js +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMarkdownRenderer.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js +10 -3
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessageList.js +5 -3
- package/dist/esm/components/editor/ChatPanel/ChatMessageList.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatPanel.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatToolCall.js +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatToolCall.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatTypingIndicator.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatInput.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatMessages.js +7 -4
- package/dist/esm/components/editor/ChatPanel/useChatMessages.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatScroll.js +6 -1
- package/dist/esm/components/editor/ChatPanel/useChatScroll.js.map +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.css.js +18 -0
- package/dist/esm/components/editor/Minimap/Minimap.css.js.map +1 -0
- package/dist/esm/components/editor/Minimap/Minimap.js +257 -0
- package/dist/esm/components/editor/Minimap/Minimap.js.map +1 -0
- package/dist/esm/components/editor/Minimap/Minimap.types.js +11 -0
- package/dist/esm/components/editor/Minimap/Minimap.types.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapContext.js +48 -0
- package/dist/esm/components/editor/Minimap/MinimapContext.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapCorner.js +16 -0
- package/dist/esm/components/editor/Minimap/MinimapCorner.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapFooter.js +16 -0
- package/dist/esm/components/editor/Minimap/MinimapFooter.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapStore.js +166 -0
- package/dist/esm/components/editor/Minimap/MinimapStore.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapTitle.js +16 -0
- package/dist/esm/components/editor/Minimap/MinimapTitle.js.map +1 -0
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js +69 -0
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js.map +1 -0
- package/dist/esm/components/editor/Minimap/computeBoundsFromItems.js +65 -0
- package/dist/esm/components/editor/Minimap/computeBoundsFromItems.js.map +1 -0
- package/dist/esm/components/editor/Minimap/index.js +15 -0
- package/dist/esm/components/editor/Minimap/index.js.map +1 -0
- package/dist/esm/components/editor/Minimap/minimapCoords.js +105 -0
- package/dist/esm/components/editor/Minimap/minimapCoords.js.map +1 -0
- package/dist/esm/components/editor/Minimap/minimapDrawing.js +93 -0
- package/dist/esm/components/editor/Minimap/minimapDrawing.js.map +1 -0
- package/dist/esm/components/editor/Minimap/minimapHitTest.js +59 -0
- package/dist/esm/components/editor/Minimap/minimapHitTest.js.map +1 -0
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js +299 -0
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.css.js +36 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.css.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js +627 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.types.js +10 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.types.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphContext.js +54 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphContext.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphEdgeLabels.js +137 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphEdgeLabels.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphGroup.js +211 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphGroup.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphMinimap.js +122 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphMinimap.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js +170 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeContext.js +21 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeContext.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeParts.js +56 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeParts.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js +59 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSectionContext.js +17 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSectionContext.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPin.js +35 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPin.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js +191 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPortVisual.js +46 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPortVisual.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSlots.js +50 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSlots.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSpawnPalette.js +70 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSpawnPalette.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphStore.js +543 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphStore.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphToolbar.js +100 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphToolbar.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/createTypeMatchValidator.js +37 -0
- package/dist/esm/components/editor/NodeGraph/createTypeMatchValidator.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphActions.js +53 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphActions.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphDrawing.js +461 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphDrawing.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphIds.js +31 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphIds.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphMath.js +350 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphMath.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/sortSlots.js +69 -0
- package/dist/esm/components/editor/NodeGraph/sortSlots.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useDragGesture.js +63 -0
- package/dist/esm/components/editor/NodeGraph/useDragGesture.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js +207 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js +334 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js +137 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js +392 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js +164 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js +234 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useStoreSlice.js +49 -0
- package/dist/esm/components/editor/NodeGraph/useStoreSlice.js.map +1 -0
- package/dist/esm/components/editor/PropertyInspector/PropertyGroup.js +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyGroup.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.css.js +4 -3
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.css.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.js +12 -7
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.css.js +7 -6
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.css.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js +21 -5
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertySection.css.js +11 -10
- package/dist/esm/components/editor/PropertyInspector/PropertySection.css.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js +32 -11
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/usePropertyUndo.js.map +1 -1
- package/dist/esm/components/editor/Timeline/Timeline.css.js +23 -0
- package/dist/esm/components/editor/Timeline/Timeline.css.js.map +1 -0
- package/dist/esm/components/editor/Timeline/Timeline.js +450 -0
- package/dist/esm/components/editor/Timeline/Timeline.js.map +1 -0
- package/dist/esm/components/editor/Timeline/Timeline.types.js +10 -0
- package/dist/esm/components/editor/Timeline/Timeline.types.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineContext.js +69 -0
- package/dist/esm/components/editor/Timeline/TimelineContext.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineFooter.js +16 -0
- package/dist/esm/components/editor/Timeline/TimelineFooter.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js +280 -0
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineStore.js +289 -0
- package/dist/esm/components/editor/Timeline/TimelineStore.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineToolbar.js +15 -0
- package/dist/esm/components/editor/Timeline/TimelineToolbar.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js +142 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackScale.js +18 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackScale.js.map +1 -0
- package/dist/esm/components/editor/Timeline/index.js +18 -0
- package/dist/esm/components/editor/Timeline/index.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineCoords.js +118 -0
- package/dist/esm/components/editor/Timeline/timelineCoords.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineDrawing.js +390 -0
- package/dist/esm/components/editor/Timeline/timelineDrawing.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineEdits.js +323 -0
- package/dist/esm/components/editor/Timeline/timelineEdits.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineHitTest.js +153 -0
- package/dist/esm/components/editor/Timeline/timelineHitTest.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineLayout.js +39 -0
- package/dist/esm/components/editor/Timeline/timelineLayout.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineSelection.js +0 -0
- package/dist/esm/components/editor/Timeline/timelineSelection.js.map +1 -0
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js +156 -0
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js.map +1 -0
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js +587 -0
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js.map +1 -0
- package/dist/esm/components/editor/Timeline/useTimelinePlayback.js +85 -0
- package/dist/esm/components/editor/Timeline/useTimelinePlayback.js.map +1 -0
- package/dist/esm/components/editor/TransformControl/TransformControl.js +4 -4
- package/dist/esm/components/editor/TransformControl/TransformControl.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/ViewportGizmo.js +8 -4
- package/dist/esm/components/editor/ViewportGizmo/ViewportGizmo.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/gizmoMath.js +6 -2
- package/dist/esm/components/editor/ViewportGizmo/gizmoMath.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoInteraction.js +8 -6
- package/dist/esm/components/editor/ViewportGizmo/useGizmoInteraction.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoRenderer.js +1 -2
- package/dist/esm/components/editor/ViewportGizmo/useGizmoRenderer.js.map +1 -1
- package/dist/esm/components/feedback/Alert/Alert.js +8 -8
- package/dist/esm/components/feedback/Alert/Alert.js.map +1 -1
- package/dist/esm/components/feedback/Alert/AlertActions.js.map +1 -1
- package/dist/esm/components/feedback/Alert/AlertDescription.js.map +1 -1
- package/dist/esm/components/feedback/Alert/AlertTitle.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js +32 -22
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js +3 -1
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/Dialog.js +6 -3
- package/dist/esm/components/feedback/Dialog/Dialog.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogBody.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogClose.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogFooter.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogHeader.js +1 -1
- package/dist/esm/components/feedback/Dialog/DialogHeader.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/useDialogAnimation.js +10 -2
- package/dist/esm/components/feedback/Dialog/useDialogAnimation.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/Drawer.js +4 -1
- package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js +4 -0
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -1
- package/dist/esm/components/feedback/EmptyState/EmptyState.js +1 -1
- package/dist/esm/components/feedback/EmptyState/EmptyState.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogView.css.js +35 -0
- package/dist/esm/components/feedback/LogView/LogView.css.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogView.js +297 -0
- package/dist/esm/components/feedback/LogView/LogView.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewBody.js +205 -0
- package/dist/esm/components/feedback/LogView/LogViewBody.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewContext.js +31 -0
- package/dist/esm/components/feedback/LogView/LogViewContext.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewParts.js +73 -0
- package/dist/esm/components/feedback/LogView/LogViewParts.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewStore.js +240 -0
- package/dist/esm/components/feedback/LogView/LogViewStore.js.map +1 -0
- package/dist/esm/components/feedback/LogView/logViewLabels.js +25 -0
- package/dist/esm/components/feedback/LogView/logViewLabels.js.map +1 -0
- package/dist/esm/components/feedback/LogView/logViewLevels.js +74 -0
- package/dist/esm/components/feedback/LogView/logViewLevels.js.map +1 -0
- package/dist/esm/components/feedback/LogView/logViewUtils.js +128 -0
- package/dist/esm/components/feedback/LogView/logViewUtils.js.map +1 -0
- package/dist/esm/components/feedback/LogView/useFollowTail.js +95 -0
- package/dist/esm/components/feedback/LogView/useFollowTail.js.map +1 -0
- package/dist/esm/components/feedback/LogView/useLogSelection.js +113 -0
- package/dist/esm/components/feedback/LogView/useLogSelection.js.map +1 -0
- package/dist/esm/components/feedback/ProgressBar/CircularProgress.js +1 -1
- package/dist/esm/components/feedback/ProgressBar/CircularProgress.js.map +1 -1
- package/dist/esm/components/feedback/ProgressBar/ProgressBar.js +1 -1
- package/dist/esm/components/feedback/ProgressBar/ProgressBar.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/Skeleton.js +1 -1
- package/dist/esm/components/feedback/Skeleton/Skeleton.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonGroup.js +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonGroup.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +1 -1
- package/dist/esm/components/feedback/Spinner/Spinner.js +1 -1
- package/dist/esm/components/feedback/Spinner/Spinner.js.map +1 -1
- package/dist/esm/components/feedback/Stat/Stat.js.map +1 -1
- package/dist/esm/components/feedback/Toast/ToastContainer.js +1 -1
- package/dist/esm/components/feedback/Toast/ToastContainer.js.map +1 -1
- package/dist/esm/components/feedback/Toast/ToastItem.js +7 -3
- package/dist/esm/components/feedback/Toast/ToastItem.js.map +1 -1
- package/dist/esm/components/feedback/Toast/ToastProvider.js.map +1 -1
- package/dist/esm/components/feedback/Toast/useToast.js.map +1 -1
- package/dist/esm/components/form/FormHelperText.js.map +1 -1
- package/dist/esm/components/form/FormLabel.js.map +1 -1
- package/dist/esm/components/form/InputWrapper.js.map +1 -1
- package/dist/esm/components/layout/Accordion/Accordion.js +1 -1
- package/dist/esm/components/layout/Accordion/Accordion.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionContent.js +1 -1
- package/dist/esm/components/layout/Accordion/AccordionContent.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionItem.js +1 -1
- package/dist/esm/components/layout/Accordion/AccordionItem.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js +7 -2
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
- package/dist/esm/components/layout/Card/Card.js +2 -2
- package/dist/esm/components/layout/Card/Card.js.map +1 -1
- package/dist/esm/components/layout/Divider/Divider.js.map +1 -1
- package/dist/esm/components/layout/Flex/Flex.js +1 -1
- package/dist/esm/components/layout/Flex/Flex.js.map +1 -1
- package/dist/esm/components/layout/Grid/Grid.js +1 -1
- package/dist/esm/components/layout/Grid/Grid.js.map +1 -1
- package/dist/esm/components/layout/ListItem/ListItem.js +1 -1
- package/dist/esm/components/layout/ListItem/ListItem.js.map +1 -1
- package/dist/esm/components/layout/PageHeader/PageHeader.js.map +1 -1
- package/dist/esm/components/layout/PanelSurface/PanelSurface.js +3 -3
- package/dist/esm/components/layout/PanelSurface/PanelSurface.js.map +1 -1
- package/dist/esm/components/layout/ScrollArea/ScrollArea.js +91 -23
- package/dist/esm/components/layout/ScrollArea/ScrollArea.js.map +1 -1
- package/dist/esm/components/layout/Spacer/Spacer.js.map +1 -1
- package/dist/esm/components/layout/SplitPane/SplitPane.js +29 -16
- package/dist/esm/components/layout/SplitPane/SplitPane.js.map +1 -1
- package/dist/esm/components/layout/SplitPane/SplitPanePanel.js.map +1 -1
- package/dist/esm/components/layout/Stack/Stack.js +1 -1
- package/dist/esm/components/layout/Stack/Stack.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js +4 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js +6 -2
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbSeparator.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js +13 -2
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/esm/components/navigation/ContextMenu/ContextMenu.js +45 -39
- package/dist/esm/components/navigation/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/Menu.css.js +9 -12
- package/dist/esm/components/navigation/Menu/Menu.css.js.map +1 -1
- package/dist/esm/components/navigation/Menu/Menu.js +97 -63
- package/dist/esm/components/navigation/Menu/Menu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/MenuGapContext.js +19 -0
- package/dist/esm/components/navigation/Menu/MenuGapContext.js.map +1 -0
- package/dist/esm/components/navigation/Pagination/Pagination.js.map +1 -1
- package/dist/esm/components/navigation/Pagination/usePagination.js.map +1 -1
- package/dist/esm/components/navigation/PathBar/PathBar.css.js +6 -0
- package/dist/esm/components/navigation/PathBar/PathBar.css.js.map +1 -0
- package/dist/esm/components/navigation/PathBar/PathBar.js +184 -0
- package/dist/esm/components/navigation/PathBar/PathBar.js.map +1 -0
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js +151 -0
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js.map +1 -0
- package/dist/esm/components/navigation/PathBar/pathUtils.js +118 -0
- package/dist/esm/components/navigation/PathBar/pathUtils.js.map +1 -0
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControl.js +16 -6
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControlItem.js +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControlItem.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/Tab.js +10 -3
- package/dist/esm/components/navigation/Tabs/Tab.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/TabList.js +5 -2
- package/dist/esm/components/navigation/Tabs/TabList.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/TabPanel.js +1 -1
- package/dist/esm/components/navigation/Tabs/TabPanel.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/Tabs.js.map +1 -1
- package/dist/esm/components/primitives/Avatar/Avatar.js +11 -4
- package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/primitives/Avatar/AvatarGroup.js +2 -2
- package/dist/esm/components/primitives/Avatar/AvatarGroup.js.map +1 -1
- package/dist/esm/components/primitives/Badge/Badge.js +10 -3
- package/dist/esm/components/primitives/Badge/Badge.js.map +1 -1
- package/dist/esm/components/primitives/Button/Button.js.map +1 -1
- package/dist/esm/components/primitives/Checkbox/Checkbox.js +6 -4
- package/dist/esm/components/primitives/Checkbox/Checkbox.js.map +1 -1
- package/dist/esm/components/primitives/Checkbox/CheckboxGroup.js +2 -2
- package/dist/esm/components/primitives/Checkbox/CheckboxGroup.js.map +1 -1
- package/dist/esm/components/primitives/Code/Code.js.map +1 -1
- package/dist/esm/components/primitives/Collapsible/Collapsible.js +2 -2
- package/dist/esm/components/primitives/Collapsible/Collapsible.js.map +1 -1
- package/dist/esm/components/primitives/HoverCard/HoverCard.js +5 -3
- package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +1 -1
- package/dist/esm/components/primitives/Icon/Icon.js +1 -1
- package/dist/esm/components/primitives/Icon/Icon.js.map +1 -1
- package/dist/esm/components/primitives/IconButton/IconButton.js.map +1 -1
- package/dist/esm/components/primitives/Input/Input.js +1 -1
- package/dist/esm/components/primitives/Input/Input.js.map +1 -1
- package/dist/esm/components/primitives/Kbd/Kbd.js +1 -1
- package/dist/esm/components/primitives/Kbd/Kbd.js.map +1 -1
- package/dist/esm/components/primitives/Link/Link.js.map +1 -1
- package/dist/esm/components/primitives/Paper/Paper.js.map +1 -1
- package/dist/esm/components/primitives/Popover/Popover.js +1 -1
- package/dist/esm/components/primitives/Popover/Popover.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverContent.js +6 -4
- package/dist/esm/components/primitives/Popover/PopoverContent.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverTrigger.js.map +1 -1
- package/dist/esm/components/primitives/Radio/Radio.js +3 -3
- package/dist/esm/components/primitives/Radio/Radio.js.map +1 -1
- package/dist/esm/components/primitives/Radio/RadioGroup.js +2 -2
- package/dist/esm/components/primitives/Radio/RadioGroup.js.map +1 -1
- package/dist/esm/components/primitives/Switch/Switch.js +1 -1
- package/dist/esm/components/primitives/Switch/Switch.js.map +1 -1
- package/dist/esm/components/primitives/Text/Text.js.map +1 -1
- package/dist/esm/components/primitives/TextArea/TextArea.js +3 -4
- package/dist/esm/components/primitives/TextArea/TextArea.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Arrow.js +1 -1
- package/dist/esm/components/primitives/Tooltip/Arrow.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Tooltip.js +9 -7
- package/dist/esm/components/primitives/Tooltip/Tooltip.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/utils.js +1 -1
- package/dist/esm/components/primitives/Tooltip/utils.js.map +1 -1
- package/dist/esm/components/primitives/VisuallyHidden/VisuallyHidden.js.map +1 -1
- package/dist/esm/components/primitives/canvas/CanvasContainer.js +1 -1
- package/dist/esm/components/primitives/canvas/CanvasContainer.js.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasCoords.js.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasDrawing.js +1 -1
- package/dist/esm/components/primitives/canvas/canvasDrawing.js.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasTheme.js.map +1 -1
- package/dist/esm/components/primitives/canvas/useCanvasRenderer.js +8 -2
- package/dist/esm/components/primitives/canvas/useCanvasRenderer.js.map +1 -1
- package/dist/esm/components/primitives/viewport/Viewport.css.js +12 -0
- package/dist/esm/components/primitives/viewport/Viewport.css.js.map +1 -0
- package/dist/esm/components/primitives/viewport/Viewport.js +189 -0
- package/dist/esm/components/primitives/viewport/Viewport.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportContext.js +47 -0
- package/dist/esm/components/primitives/viewport/ViewportContext.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportLayer.js +113 -0
- package/dist/esm/components/primitives/viewport/ViewportLayer.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportOverlay.js +47 -0
- package/dist/esm/components/primitives/viewport/ViewportOverlay.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportStore.js +210 -0
- package/dist/esm/components/primitives/viewport/ViewportStore.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportWorld.js +38 -0
- package/dist/esm/components/primitives/viewport/ViewportWorld.js.map +1 -0
- package/dist/esm/components/primitives/viewport/useViewportGestures.js +333 -0
- package/dist/esm/components/primitives/viewport/useViewportGestures.js.map +1 -0
- package/dist/esm/components/primitives/viewport/viewportCoords.js +37 -0
- package/dist/esm/components/primitives/viewport/viewportCoords.js.map +1 -0
- package/dist/esm/components/primitives/viewport/viewportMath.js +77 -0
- package/dist/esm/components/primitives/viewport/viewportMath.js.map +1 -0
- package/dist/esm/components/shell/AppShell/AppShell.js +1 -1
- package/dist/esm/components/shell/AppShell/AppShell.js.map +1 -1
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.css.js +11 -10
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.css.js.map +1 -1
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.js +60 -24
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.js.map +1 -1
- package/dist/esm/components/shell/MenuBar/MenuBar.js +23 -16
- package/dist/esm/components/shell/MenuBar/MenuBar.js.map +1 -1
- package/dist/esm/components/shell/StatusBar/StatusBar.js.map +1 -1
- package/dist/esm/components/shell/Toolbar/Toolbar.js +4 -3
- package/dist/esm/components/shell/Toolbar/Toolbar.js.map +1 -1
- package/dist/esm/context/KeyboardContext.js.map +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -1
- package/dist/esm/hooks/useClickOutside/useClickOutside.js +9 -9
- package/dist/esm/hooks/useClickOutside/useClickOutside.js.map +1 -1
- package/dist/esm/hooks/useClipboard/useClipboard.js +4 -0
- package/dist/esm/hooks/useClipboard/useClipboard.js.map +1 -1
- package/dist/esm/hooks/useControlledState/useControlledState.js.map +1 -1
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js +11 -6
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js.map +1 -1
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js +16 -4
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js.map +1 -1
- package/dist/esm/hooks/useDisclosure/useDisclosure.js.map +1 -1
- package/dist/esm/hooks/useEventCallback/useEventCallback.js +49 -0
- package/dist/esm/hooks/useEventCallback/useEventCallback.js.map +1 -0
- package/dist/esm/hooks/useFocusTrap/useFocusTrap.js.map +1 -1
- package/dist/esm/hooks/useHotkey/useHotkey.js +7 -12
- package/dist/esm/hooks/useHotkey/useHotkey.js.map +1 -1
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js +2 -0
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -1
- package/dist/esm/hooks/useIsMounted/useIsMounted.js +43 -0
- package/dist/esm/hooks/useIsMounted/useIsMounted.js.map +1 -0
- package/dist/esm/hooks/useKeyboard/useKeyboard.js.map +1 -1
- package/dist/esm/hooks/useKeyboard/utils.js.map +1 -1
- package/dist/esm/hooks/useLatest/useLatest.js +31 -0
- package/dist/esm/hooks/useLatest/useLatest.js.map +1 -0
- package/dist/esm/hooks/useListboxNav/useListboxNav.js +29 -13
- package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -1
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js +7 -1
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -1
- package/dist/esm/hooks/useMergedRef/useMergedRef.js +6 -1
- package/dist/esm/hooks/useMergedRef/useMergedRef.js.map +1 -1
- package/dist/esm/hooks/useNavigationHistory/useNavigationHistory.js +146 -0
- package/dist/esm/hooks/useNavigationHistory/useNavigationHistory.js.map +1 -0
- package/dist/esm/hooks/useResizeObserver/useResizeObserver.js +6 -8
- package/dist/esm/hooks/useResizeObserver/useResizeObserver.js.map +1 -1
- package/dist/esm/hooks/useTheme/useTheme.js +6 -3
- package/dist/esm/hooks/useTheme/useTheme.js.map +1 -1
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js +9 -4
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -1
- package/dist/esm/index.js +63 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node_modules/tslib/tslib.es6.js +37 -0
- package/dist/esm/node_modules/tslib/tslib.es6.js.map +1 -0
- package/dist/esm/styles.js +3 -0
- package/dist/esm/styles.js.map +1 -0
- package/dist/esm/theme/ThemeProvider.js +6 -4
- package/dist/esm/theme/ThemeProvider.js.map +1 -1
- package/dist/esm/theme/VanillaThemeProvider.js.map +1 -1
- package/dist/esm/theme/breakpoints.js.map +1 -1
- package/dist/esm/theme/contract.css.js +1 -1
- package/dist/esm/theme/createCustomTheme.js +2 -2
- package/dist/esm/theme/createCustomTheme.js.map +1 -1
- package/dist/esm/theme/createLightTheme.js.map +1 -1
- package/dist/esm/theme/darkTheme.css.js +2 -2
- package/dist/esm/theme/darkThemeValues.js +1 -1
- package/dist/esm/theme/darkThemeValues.js.map +1 -1
- package/dist/esm/theme/lightTheme.css.js +1 -1
- package/dist/esm/theme/lightThemeValues.js +1 -1
- package/dist/esm/theme/lightThemeValues.js.map +1 -1
- package/dist/esm/theme/themeContractData.js +4 -4
- package/dist/esm/theme/themeContractData.js.map +1 -1
- package/dist/esm/utils/cx.js.map +1 -1
- package/dist/esm/utils/devWarn.js +1 -1
- package/dist/esm/utils/devWarn.js.map +1 -1
- package/dist/esm/utils/mathExpression.js +1 -1
- package/dist/esm/utils/mathExpression.js.map +1 -1
- package/dist/esm/utils/mathUtils.js.map +1 -1
- package/dist/esm/utils/objects.js.map +1 -1
- package/dist/esm/utils/platform.js.map +1 -1
- package/dist/tokens/tokens.dark.css +1 -1
- package/dist/tokens/tokens.json +1 -1
- package/dist/tokens/tokens.light.css +1 -1
- package/dist/types/components/Icons/AddIcon.d.ts +1 -1
- package/dist/types/components/Icons/AiChatIcon.d.ts +1 -1
- package/dist/types/components/Icons/AiSparklesIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArchiveIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowDownIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowLeftIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowRightIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowUpIcon.d.ts +1 -1
- package/dist/types/components/Icons/BookmarkIcon.d.ts +1 -1
- package/dist/types/components/Icons/BugIcon.d.ts +1 -1
- package/dist/types/components/Icons/BuildingIcon.d.ts +1 -1
- package/dist/types/components/Icons/CalendarIcon.d.ts +1 -1
- package/dist/types/components/Icons/CameraIcon.d.ts +34 -0
- package/dist/types/components/Icons/CheckIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronDownIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronLeftIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronRightIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronUpIcon.d.ts +1 -1
- package/dist/types/components/Icons/CircleIcon.d.ts +1 -1
- package/dist/types/components/Icons/ClockIcon.d.ts +1 -1
- package/dist/types/components/Icons/CloseIcon.d.ts +1 -1
- package/dist/types/components/Icons/CloudUploadIcon.d.ts +1 -1
- package/dist/types/components/Icons/CodeIcon.d.ts +1 -1
- package/dist/types/components/Icons/CopyIcon.d.ts +1 -1
- package/dist/types/components/Icons/CubeIcon.d.ts +34 -0
- package/dist/types/components/Icons/CutIcon.d.ts +1 -1
- package/dist/types/components/Icons/DotsHorizontalIcon.d.ts +1 -1
- package/dist/types/components/Icons/DotsVerticalIcon.d.ts +1 -1
- package/dist/types/components/Icons/DownloadIcon.d.ts +1 -1
- package/dist/types/components/Icons/EditIcon.d.ts +1 -1
- package/dist/types/components/Icons/ErrorIcon.d.ts +1 -1
- package/dist/types/components/Icons/ExternalLinkIcon.d.ts +1 -1
- package/dist/types/components/Icons/EyeDropperIcon.d.ts +1 -1
- package/dist/types/components/Icons/EyeIcon.d.ts +1 -1
- package/dist/types/components/Icons/FileTextIcon.d.ts +1 -1
- package/dist/types/components/Icons/FilterIcon.d.ts +1 -1
- package/dist/types/components/Icons/FirstIcon.d.ts +1 -1
- package/dist/types/components/Icons/FolderCogIcon.d.ts +1 -1
- package/dist/types/components/Icons/FolderIcon.d.ts +1 -1
- package/dist/types/components/Icons/FolderOpenIcon.d.ts +1 -1
- package/dist/types/components/Icons/FullscreenIcon.d.ts +1 -1
- package/dist/types/components/Icons/GitBranchIcon.d.ts +1 -1
- package/dist/types/components/Icons/GridIcon.d.ts +1 -1
- package/dist/types/components/Icons/HeartIcon.d.ts +1 -1
- package/dist/types/components/Icons/HelpIcon.d.ts +1 -1
- package/dist/types/components/Icons/HomeIcon.d.ts +1 -1
- package/dist/types/components/Icons/ImageIcon.d.ts +34 -0
- package/dist/types/components/Icons/InfoIcon.d.ts +1 -1
- package/dist/types/components/Icons/LastIcon.d.ts +1 -1
- package/dist/types/components/Icons/LinkIcon.d.ts +1 -1
- package/dist/types/components/Icons/ListIcon.d.ts +1 -1
- package/dist/types/components/Icons/LockIcon.d.ts +1 -1
- package/dist/types/components/Icons/MaximizeIcon.d.ts +1 -1
- package/dist/types/components/Icons/MenuIcon.d.ts +1 -1
- package/dist/types/components/Icons/MinimizeIcon.d.ts +1 -1
- package/dist/types/components/Icons/MinusIcon.d.ts +1 -1
- package/dist/types/components/Icons/OrbitIcon.d.ts +34 -0
- package/dist/types/components/Icons/PasteIcon.d.ts +1 -1
- package/dist/types/components/Icons/PauseIcon.d.ts +1 -1
- package/dist/types/components/Icons/PinIcon.d.ts +1 -1
- package/dist/types/components/Icons/PlayIcon.d.ts +1 -1
- package/dist/types/components/Icons/RedoIcon.d.ts +1 -1
- package/dist/types/components/Icons/RefreshIcon.d.ts +1 -1
- package/dist/types/components/Icons/RobotIcon.d.ts +1 -1
- package/dist/types/components/Icons/RotateIcon.d.ts +35 -0
- package/dist/types/components/Icons/SaveIcon.d.ts +1 -1
- package/dist/types/components/Icons/SearchIcon.d.ts +1 -1
- package/dist/types/components/Icons/SendIcon.d.ts +1 -1
- package/dist/types/components/Icons/SettingsIcon.d.ts +1 -1
- package/dist/types/components/Icons/SortIcon.d.ts +1 -1
- package/dist/types/components/Icons/SpinIcon.d.ts +35 -0
- package/dist/types/components/Icons/StarIcon.d.ts +1 -1
- package/dist/types/components/Icons/StopIcon.d.ts +1 -1
- package/dist/types/components/Icons/SuccessIcon.d.ts +1 -1
- package/dist/types/components/Icons/TagIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentAlignedIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentAutoIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentFreeIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentLinearIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentMirroredIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentStepIcon.d.ts +1 -1
- package/dist/types/components/Icons/TerminalIcon.d.ts +1 -1
- package/dist/types/components/Icons/TrashIcon.d.ts +1 -1
- package/dist/types/components/Icons/UndoIcon.d.ts +1 -1
- package/dist/types/components/Icons/UnlinkIcon.d.ts +1 -1
- package/dist/types/components/Icons/UnlockIcon.d.ts +1 -1
- package/dist/types/components/Icons/UploadIcon.d.ts +1 -1
- package/dist/types/components/Icons/UserIcon.d.ts +1 -1
- package/dist/types/components/Icons/UsersIcon.d.ts +1 -1
- package/dist/types/components/Icons/WarningIcon.d.ts +1 -1
- package/dist/types/components/Icons/ZoomFitIcon.d.ts +35 -0
- package/dist/types/components/Icons/ZoomInIcon.d.ts +1 -1
- package/dist/types/components/Icons/ZoomOutIcon.d.ts +1 -1
- package/dist/types/components/controls/CartesianPicker/CartesianPicker.types.d.ts +3 -3
- package/dist/types/components/controls/ColorPicker/ColorPicker.types.d.ts +2 -2
- package/dist/types/components/controls/ColorPicker/EyeDropper.d.ts +1 -1
- package/dist/types/components/controls/Combobox/Combobox.types.d.ts +1 -1
- package/dist/types/components/controls/CurveEditor/CurveEditor.types.d.ts +8 -58
- package/dist/types/components/controls/CurveEditor/curveUtils.d.ts +2 -1
- package/dist/types/components/controls/FileTree/FileTree.d.ts +34 -0
- package/dist/types/components/controls/FileTree/FileTree.types.d.ts +133 -0
- package/dist/types/components/controls/FileTree/fileTreeIcons.d.ts +29 -0
- package/dist/types/components/controls/FileTree/fileTreeLabels.d.ts +19 -0
- package/dist/types/components/controls/FileUploader/FileUploader.types.d.ts +2 -2
- package/dist/types/components/controls/MultiSelect/MultiSelect.types.d.ts +1 -1
- package/dist/types/components/controls/NumberInput/NumberInput.d.ts +2 -2
- package/dist/types/components/controls/Select/Select.d.ts +1 -1
- package/dist/types/components/controls/Select/Select.types.d.ts +45 -11
- package/dist/types/components/controls/Slider/Slider.d.ts +1 -1
- package/dist/types/components/controls/TagInput/TagInput.types.d.ts +2 -2
- package/dist/types/components/controls/TreeView/TreeView.d.ts +1 -1
- package/dist/types/components/controls/TreeView/TreeView.types.d.ts +24 -2
- package/dist/types/components/controls/VectorInput/VectorInput.types.d.ts +1 -1
- package/dist/types/components/data/DataTable/DataTable.types.d.ts +2 -2
- package/dist/types/components/editor/AssetBrowser/AssetBrowser.d.ts +13 -0
- package/dist/types/components/editor/AssetBrowser/AssetBrowser.types.d.ts +241 -0
- package/dist/types/components/editor/AssetBrowser/assetBrowserLabels.d.ts +70 -0
- package/dist/types/components/editor/ChatPanel/ChatMarkdownRenderer.types.d.ts +1 -1
- package/dist/types/components/editor/ChatPanel/ChatPanel.types.d.ts +2 -2
- package/dist/types/components/editor/Minimap/Minimap.d.ts +26 -0
- package/dist/types/components/editor/Minimap/Minimap.types.d.ts +249 -0
- package/dist/types/components/editor/Minimap/MinimapContext.d.ts +27 -0
- package/dist/types/components/editor/Minimap/MinimapCorner.d.ts +8 -0
- package/dist/types/components/editor/Minimap/MinimapFooter.d.ts +8 -0
- package/dist/types/components/editor/Minimap/MinimapStore.d.ts +17 -0
- package/dist/types/components/editor/Minimap/MinimapTitle.d.ts +8 -0
- package/dist/types/components/editor/Minimap/ViewportMinimap.d.ts +52 -0
- package/dist/types/components/editor/Minimap/computeBoundsFromItems.d.ts +13 -0
- package/dist/types/components/editor/Minimap/index.d.ts +13 -0
- package/dist/types/components/editor/NodeGraph/NodeGraph.d.ts +63 -0
- package/dist/types/components/editor/NodeGraph/NodeGraph.types.d.ts +568 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphContext.d.ts +30 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphNodeParts.d.ts +86 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphNodeSection.d.ts +47 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphPin.d.ts +57 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphPort.d.ts +30 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphPortVisual.d.ts +46 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphSlots.d.ts +16 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphStore.d.ts +256 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphToolbar.d.ts +54 -0
- package/dist/types/components/editor/NodeGraph/createTypeMatchValidator.d.ts +43 -0
- package/dist/types/components/editor/NodeGraph/nodeGraphActions.d.ts +39 -0
- package/dist/types/components/editor/NodeGraph/nodeGraphIds.d.ts +16 -0
- package/dist/types/components/editor/NodeGraph/useNodeGraph.d.ts +103 -0
- package/dist/types/components/editor/PropertyInspector/PropertyInspector.types.d.ts +51 -5
- package/dist/types/components/editor/Timeline/Timeline.d.ts +18 -0
- package/dist/types/components/editor/Timeline/Timeline.types.d.ts +348 -0
- package/dist/types/components/editor/Timeline/TimelineContext.d.ts +51 -0
- package/dist/types/components/editor/Timeline/TimelineFooter.d.ts +8 -0
- package/dist/types/components/editor/Timeline/TimelineMinimap.d.ts +21 -0
- package/dist/types/components/editor/Timeline/TimelineStore.d.ts +41 -0
- package/dist/types/components/editor/Timeline/TimelineToolbar.d.ts +8 -0
- package/dist/types/components/editor/Timeline/TimelineTrackScale.d.ts +8 -0
- package/dist/types/components/editor/Timeline/index.d.ts +15 -0
- package/dist/types/components/editor/Timeline/timelineCoords.d.ts +7 -0
- package/dist/types/components/editor/Timeline/timelineEdits.d.ts +18 -0
- package/dist/types/components/editor/TransformControl/TransformControl.types.d.ts +2 -2
- package/dist/types/components/editor/ViewportGizmo/ViewportGizmo.types.d.ts +1 -1
- package/dist/types/components/feedback/Alert/Alert.d.ts +2 -2
- package/dist/types/components/feedback/Alert/Alert.types.d.ts +5 -5
- package/dist/types/components/feedback/CommandPalette/CommandPalette.types.d.ts +1 -3
- package/dist/types/components/feedback/Dialog/Dialog.types.d.ts +2 -2
- package/dist/types/components/feedback/Drawer/Drawer.types.d.ts +1 -1
- package/dist/types/components/feedback/EmptyState/EmptyState.types.d.ts +2 -2
- package/dist/types/components/feedback/LogView/LogView.d.ts +58 -0
- package/dist/types/components/feedback/LogView/LogView.types.d.ts +333 -0
- package/dist/types/components/feedback/LogView/LogViewBody.d.ts +15 -0
- package/dist/types/components/feedback/LogView/LogViewContext.d.ts +9 -0
- package/dist/types/components/feedback/LogView/LogViewParts.d.ts +35 -0
- package/dist/types/components/feedback/LogView/LogViewStore.d.ts +4 -0
- package/dist/types/components/feedback/LogView/logViewLabels.d.ts +9 -0
- package/dist/types/components/feedback/LogView/logViewUtils.d.ts +41 -0
- package/dist/types/components/feedback/ProgressBar/ProgressBar.types.d.ts +3 -3
- package/dist/types/components/feedback/Skeleton/Skeleton.types.d.ts +4 -4
- package/dist/types/components/feedback/Stat/Stat.types.d.ts +2 -2
- package/dist/types/components/feedback/Toast/Toast.types.d.ts +1 -1
- package/dist/types/components/form/FormHelperText.d.ts +2 -2
- package/dist/types/components/form/FormLabel.d.ts +1 -1
- package/dist/types/components/form/InputWrapper.d.ts +2 -2
- package/dist/types/components/layout/Accordion/Accordion.types.d.ts +2 -2
- package/dist/types/components/layout/Card/Card.types.d.ts +6 -6
- package/dist/types/components/layout/Divider/Divider.types.d.ts +2 -2
- package/dist/types/components/layout/Flex/Flex.d.ts +1 -1
- package/dist/types/components/layout/Grid/Grid.d.ts +1 -1
- package/dist/types/components/layout/ListItem/ListItem.types.d.ts +2 -2
- package/dist/types/components/layout/PageHeader/PageHeader.types.d.ts +1 -1
- package/dist/types/components/layout/PanelSurface/PanelSurface.types.d.ts +6 -6
- package/dist/types/components/layout/ScrollArea/ScrollArea.types.d.ts +2 -2
- package/dist/types/components/layout/Stack/Stack.d.ts +1 -1
- package/dist/types/components/navigation/Breadcrumbs/BreadcrumbItem.d.ts +1 -0
- package/dist/types/components/navigation/Breadcrumbs/Breadcrumbs.types.d.ts +7 -0
- package/dist/types/components/navigation/ContextMenu/ContextMenu.d.ts +18 -3
- package/dist/types/components/navigation/ContextMenu/ContextMenu.types.d.ts +55 -63
- package/dist/types/components/navigation/Menu/Menu.d.ts +38 -35
- package/dist/types/components/navigation/Menu/Menu.types.d.ts +190 -71
- package/dist/types/components/navigation/Pagination/Pagination.types.d.ts +1 -1
- package/dist/types/components/navigation/PathBar/PathBar.d.ts +31 -0
- package/dist/types/components/navigation/PathBar/PathBar.types.d.ts +79 -0
- package/dist/types/components/navigation/SegmentedControl/SegmentedControl.types.d.ts +3 -3
- package/dist/types/components/navigation/Tabs/Tabs.types.d.ts +2 -2
- package/dist/types/components/primitives/Avatar/Avatar.types.d.ts +2 -2
- package/dist/types/components/primitives/Badge/Badge.types.d.ts +1 -1
- package/dist/types/components/primitives/Button/Button.d.ts +2 -2
- package/dist/types/components/primitives/Checkbox/Checkbox.types.d.ts +2 -2
- package/dist/types/components/primitives/Code/Code.types.d.ts +1 -1
- package/dist/types/components/primitives/Collapsible/Collapsible.types.d.ts +2 -2
- package/dist/types/components/primitives/HoverCard/HoverCard.types.d.ts +3 -3
- package/dist/types/components/primitives/Icon/Icon.d.ts +2 -2
- package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
- package/dist/types/components/primitives/Input/Input.d.ts +1 -1
- package/dist/types/components/primitives/Kbd/Kbd.types.d.ts +1 -1
- package/dist/types/components/primitives/Link/Link.types.d.ts +1 -1
- package/dist/types/components/primitives/Popover/Popover.types.d.ts +2 -2
- package/dist/types/components/primitives/Radio/Radio.types.d.ts +3 -3
- package/dist/types/components/primitives/Switch/Switch.d.ts +1 -1
- package/dist/types/components/primitives/TextArea/TextArea.types.d.ts +1 -1
- package/dist/types/components/primitives/Tooltip/Tooltip.d.ts +1 -1
- package/dist/types/components/primitives/VisuallyHidden/VisuallyHidden.types.d.ts +1 -1
- package/dist/types/components/primitives/canvas/canvas.types.d.ts +16 -1
- package/dist/types/components/primitives/viewport/Viewport.d.ts +38 -0
- package/dist/types/components/primitives/viewport/Viewport.types.d.ts +257 -0
- package/dist/types/components/primitives/viewport/ViewportContext.d.ts +23 -0
- package/dist/types/components/primitives/viewport/ViewportLayer.d.ts +26 -0
- package/dist/types/components/primitives/viewport/ViewportOverlay.d.ts +27 -0
- package/dist/types/components/primitives/viewport/ViewportWorld.d.ts +25 -0
- package/dist/types/components/primitives/viewport/viewportCoords.d.ts +25 -0
- package/dist/types/components/primitives/viewport/viewportMath.d.ts +35 -0
- package/dist/types/components/shell/AppShell/AppShell.types.d.ts +2 -2
- package/dist/types/components/shell/FloatingPanel/FloatingPanel.types.d.ts +2 -2
- package/dist/types/components/shell/MenuBar/MenuBar.types.d.ts +5 -5
- package/dist/types/components/shell/StatusBar/StatusBar.types.d.ts +3 -3
- package/dist/types/components/shell/Toolbar/Toolbar.types.d.ts +5 -5
- package/dist/types/hooks/useDebounced/useDebouncedValue.d.ts +13 -4
- package/dist/types/hooks/useEventCallback/useEventCallback.d.ts +32 -0
- package/dist/types/hooks/useIsMounted/useIsMounted.d.ts +28 -0
- package/dist/types/hooks/useKeyboard/types.d.ts +1 -1
- package/dist/types/hooks/useLatest/useLatest.d.ts +23 -0
- package/dist/types/hooks/useResizeObserver/useResizeObserver.d.ts +3 -3
- package/dist/types/index.d.ts +71 -6
- package/dist/types/theme/ThemeProvider.d.ts +5 -3
- package/dist/types/theme/contract.css.d.ts +1 -1
- package/dist/types/theme/createCustomTheme.d.ts +1 -1
- package/dist/types/theme/darkThemeValues.d.ts +1 -1
- package/dist/types/theme/lightThemeValues.d.ts +1 -1
- package/dist/types/theme/themeContractData.d.ts +4 -4
- package/dist/types/theme-values.d.ts +1 -1
- package/dist/types/types/keyframe.d.ts +64 -0
- package/package.json +34 -18
- package/dist/esm/_virtual/_commonjsHelpers.js +0 -6
- package/dist/esm/_virtual/_commonjsHelpers.js.map +0 -1
- package/dist/esm/_virtual/cjs.js +0 -4
- package/dist/esm/_virtual/cjs.js.map +0 -1
- package/dist/esm/_virtual/cssesc.js +0 -8
- package/dist/esm/_virtual/cssesc.js.map +0 -1
- package/dist/esm/_virtual/picocolors.js +0 -8
- package/dist/esm/_virtual/picocolors.js.map +0 -1
- package/dist/esm/_virtual/picocolors2.js +0 -4
- package/dist/esm/_virtual/picocolors2.js.map +0 -1
- package/dist/esm/assets/src/components/navigation/Menu/Menu.css.ts.vanilla-DD0c5D10.css +0 -67
- package/dist/esm/components/navigation/ContextMenu/useContextMenuTarget.js +0 -53
- package/dist/esm/components/navigation/ContextMenu/useContextMenuTarget.js.map +0 -1
- package/dist/esm/components/navigation/Menu/Menu.helpers.js +0 -53
- package/dist/esm/components/navigation/Menu/Menu.helpers.js.map +0 -1
- package/dist/esm/components/navigation/Menu/useMenu.js +0 -88
- package/dist/esm/components/navigation/Menu/useMenu.js.map +0 -1
- package/dist/esm/node_modules/@emotion/hash/dist/emotion-hash.esm.js +0 -56
- package/dist/esm/node_modules/@emotion/hash/dist/emotion-hash.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/adapter/dist/vanilla-extract-css-adapter.esm.js +0 -46
- package/dist/esm/node_modules/@vanilla-extract/css/adapter/dist/vanilla-extract-css-adapter.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/dist/taggedTemplateLiteral-10998315.esm.js +0 -13
- package/dist/esm/node_modules/@vanilla-extract/css/dist/taggedTemplateLiteral-10998315.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/dist/transformCss-fd0786e1.esm.js +0 -962
- package/dist/esm/node_modules/@vanilla-extract/css/dist/transformCss-fd0786e1.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.esm.js +0 -309
- package/dist/esm/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/fileScope/dist/vanilla-extract-css-fileScope.esm.js +0 -18
- package/dist/esm/node_modules/@vanilla-extract/css/fileScope/dist/vanilla-extract-css-fileScope.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/injectStyles/dist/vanilla-extract-css-injectStyles.esm.js +0 -23
- package/dist/esm/node_modules/@vanilla-extract/css/injectStyles/dist/vanilla-extract-css-injectStyles.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/node_modules/lru-cache/dist/esm/index.js +0 -1543
- package/dist/esm/node_modules/@vanilla-extract/css/node_modules/lru-cache/dist/esm/index.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/private/dist/vanilla-extract-private.esm.js +0 -38
- package/dist/esm/node_modules/@vanilla-extract/private/dist/vanilla-extract-private.esm.js.map +0 -1
- package/dist/esm/node_modules/css-what/lib/es/parse.js +0 -424
- package/dist/esm/node_modules/css-what/lib/es/parse.js.map +0 -1
- package/dist/esm/node_modules/css-what/lib/es/types.js +0 -29
- package/dist/esm/node_modules/css-what/lib/es/types.js.map +0 -1
- package/dist/esm/node_modules/cssesc/cssesc.js +0 -120
- package/dist/esm/node_modules/cssesc/cssesc.js.map +0 -1
- package/dist/esm/node_modules/dedent/dist/dedent.js +0 -100
- package/dist/esm/node_modules/dedent/dist/dedent.js.map +0 -1
- package/dist/esm/node_modules/deep-object-diff/mjs/diff.js +0 -40
- package/dist/esm/node_modules/deep-object-diff/mjs/diff.js.map +0 -1
- package/dist/esm/node_modules/deep-object-diff/mjs/utils.js +0 -9
- package/dist/esm/node_modules/deep-object-diff/mjs/utils.js.map +0 -1
- package/dist/esm/node_modules/deepmerge/dist/cjs.js +0 -143
- package/dist/esm/node_modules/deepmerge/dist/cjs.js.map +0 -1
- package/dist/esm/node_modules/media-query-parser/dist/media-query-parser.esm.js +0 -1386
- package/dist/esm/node_modules/media-query-parser/dist/media-query-parser.esm.js.map +0 -1
- package/dist/esm/node_modules/modern-ahocorasick/dist/index.js +0 -102
- package/dist/esm/node_modules/modern-ahocorasick/dist/index.js.map +0 -1
- package/dist/esm/node_modules/picocolors/picocolors.js +0 -87
- package/dist/esm/node_modules/picocolors/picocolors.js.map +0 -1
- package/dist/types/components/navigation/ContextMenu/useContextMenuTarget.d.ts +0 -5
- package/dist/types/components/navigation/Menu/useMenu.d.ts +0 -16
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeGraph.js","sources":["src/components/editor/NodeGraph/NodeGraph.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n useCallback,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useTransition,\n} from 'react';\n\nimport {\n Viewport,\n ViewportLayer,\n ViewportWorld,\n ViewportOverlay,\n screenToWorld as worldFromScreen,\n worldToScreen as screenFromWorld,\n} from '@/components/primitives/viewport';\nimport { useControlledState, useLatest } from '@/hooks';\nimport { cx } from '@/utils/cx';\n\nimport { nodeGraphRootStyle } from './NodeGraph.css';\nimport { NodeGraphStoreContext } from './NodeGraphContext';\nimport {\n buildDrawTheme,\n drawBackground,\n drawConnectionPreview,\n drawEdges,\n drawGroups,\n} from './nodeGraphDrawing';\nimport { EdgeLabelsLayer } from './NodeGraphEdgeLabels';\nimport { NodeGraphGroupView } from './NodeGraphGroup';\nimport {\n computeNodesBounds,\n findEdgeAtPoint,\n getNodeBox,\n isPointInNode,\n isPointInRect,\n rectsIntersect,\n resolvePortRef,\n toggleSelected,\n} from './nodeGraphMath';\nimport { NodeGraphMinimapInner } from './NodeGraphMinimap';\nimport { NodeGraphNodeView } from './NodeGraphNode';\nimport {\n NodeGraphNodeBody,\n NodeGraphNodeHeader,\n NodeGraphPinList,\n NodeGraphPinRow,\n} from './NodeGraphNodeParts';\nimport { NodeGraphNodeSection } from './NodeGraphNodeSection';\nimport { NodeGraphPin } from './NodeGraphPin';\nimport { NodeGraphPort } from './NodeGraphPort';\nimport { NodeGraphPortVisual } from './NodeGraphPortVisual';\nimport {\n NodeGraphBackground,\n NodeGraphMinimap,\n NodeGraphSpawnPalette,\n NodeGraphToolbar,\n} from './NodeGraphSlots';\nimport { NodeGraphSpawnPaletteInner } from './NodeGraphSpawnPalette';\nimport { NodeGraphStore } from './NodeGraphStore';\nimport {\n NodeGraphToolbarInner,\n NodeGraphFitContentButton,\n NodeGraphFitSelectionButton,\n NodeGraphZoomInButton,\n NodeGraphZoomOutButton,\n NodeGraphResetZoomButton,\n NodeGraphToolbarSeparator,\n} from './NodeGraphToolbar';\nimport { sortSlots } from './sortSlots';\nimport { useNodeGraphConnection } from './useNodeGraphConnection';\nimport {\n useNodeGraphEdgeInteraction,\n EDGE_HIT_PX,\n} from './useNodeGraphEdgeInteraction';\nimport { useNodeGraphGroupDrag } from './useNodeGraphGroupDrag';\nimport { useNodeGraphKeyboard } from './useNodeGraphKeyboard';\nimport { useNodeGraphNodeDrag } from './useNodeGraphNodeDrag';\n\nimport type {\n NodeGraphContextMenuInfo,\n NodeGraphEdge,\n NodeGraphGroup,\n NodeGraphHandle,\n NodeGraphLayerName,\n NodeGraphNode,\n NodeGraphPortRef,\n NodeGraphProps,\n NodeGraphSelection,\n NodeGraphTarget,\n} from './NodeGraph.types';\nimport type { Point2D } from '@/components/primitives/canvas/canvas.types';\nimport type {\n ViewportHandle,\n ViewportSelectionEvent,\n ViewportTransform,\n WorldRect,\n} from '@/components/primitives/viewport';\n\nconst EMPTY_NODES: NodeGraphNode[] = [];\nconst EMPTY_EDGES: NodeGraphEdge[] = [];\nconst EMPTY_GROUPS: NodeGraphGroup[] = [];\nconst EMPTY_SELECTION: NodeGraphSelection = {\n nodes: [],\n edges: [],\n groups: [],\n};\nconst DEFAULT_NODE_SIZE = { width: 180, height: 80 };\n\n// ─── Main component ───\n\n/**\n * `NodeGraph` — interactive, data-driven node editor surface built on\n * `Viewport`. Renders nodes as HTML in world space (`renderNode`) and\n * edges as Béziers on a perf-isolated canvas layer.\n *\n * The component is fully controlled-or-uncontrolled across four data\n * dimensions (nodes / edges / groups / selection) — each emits a full\n * array on change. Connection drags, marquee selection, drag-to-move,\n * snap-to-grid, and keyboard nav are all wired in.\n *\n * @example\n * ```tsx\n * <NodeGraph\n * nodes={nodes}\n * edges={edges}\n * onNodesChange={setNodes}\n * onEdgesChange={setEdges}\n * renderNode={node => <MyNode data={node.data} />}\n * isValidConnection={(src, tgt) => src.node !== tgt.node}\n * responsive\n * >\n * <NodeGraph.Background variant=\"dots\" gap={24} />\n * <NodeGraph.Minimap placement=\"bottom-right\" />\n * </NodeGraph>\n * ```\n */\nconst NodeGraphImpl = ({\n nodes: nodesProp,\n defaultNodes,\n onNodesChange,\n edges: edgesProp,\n defaultEdges,\n onEdgesChange,\n groups: groupsProp,\n defaultGroups,\n onGroupsChange,\n selection: selectionProp,\n defaultSelection,\n onSelectionChange,\n renderNode,\n renderEdgeLabel,\n edgeStyle,\n isValidConnection,\n snapToGrid = false,\n onConnectStart,\n onConnectEnd,\n onContextMenu,\n onDelete,\n onActivate,\n pan,\n zoom,\n minZoom = 0.1,\n maxZoom = 4,\n selectionRect = true,\n responsive = false,\n height = 480,\n defaultNodeSize: defaultNodeSizeProp = DEFAULT_NODE_SIZE,\n disabled = false,\n ariaLabel = 'Node graph',\n className,\n style,\n testId,\n id,\n children,\n ref,\n}: NodeGraphProps): React.ReactElement => {\n // ── Refs ──\n const rootRef = useRef<HTMLDivElement>(null);\n const viewportHandleRef = useRef<ViewportHandle>(null);\n const store = useMemo(() => new NodeGraphStore(), []);\n\n // Stabilize defaultNodeSize identity across renders when the consumer\n // passes an inline literal — downstream effects/memos depend on its\n // reference and would otherwise re-fire every render.\n const defaultNodeSize = useMemo(\n () => ({\n width: defaultNodeSizeProp.width,\n height: defaultNodeSizeProp.height,\n }),\n [defaultNodeSizeProp.width, defaultNodeSizeProp.height]\n );\n\n // ── Controlled / uncontrolled data ──\n const [nodes, setNodes] = useControlledState<NodeGraphNode[]>({\n value: nodesProp,\n defaultValue: defaultNodes,\n onChange: onNodesChange,\n fallback: EMPTY_NODES,\n });\n const [edges, setEdges] = useControlledState<NodeGraphEdge[]>({\n value: edgesProp,\n defaultValue: defaultEdges,\n onChange: onEdgesChange,\n fallback: EMPTY_EDGES,\n });\n const [groups, setGroups] = useControlledState<NodeGraphGroup[]>({\n value: groupsProp,\n defaultValue: defaultGroups,\n onChange: onGroupsChange,\n fallback: EMPTY_GROUPS,\n });\n const [selection, setSelection] = useControlledState<NodeGraphSelection>({\n value: selectionProp,\n defaultValue: defaultSelection,\n onChange: onSelectionChange,\n fallback: EMPTY_SELECTION,\n });\n\n // ── Mirror React state into the store ──\n useLayoutEffect(() => {\n store.setData({\n nodes,\n edges,\n groups,\n defaultNodeSize,\n });\n }, [store, nodes, edges, groups, defaultNodeSize]);\n\n useLayoutEffect(() => {\n store.setSelection(selection);\n }, [store, selection]);\n\n // ── Transform tracking (mirror Viewport transform into a ref for math) ──\n const transformRef = useRef<ViewportTransform>({ x: 0, y: 0, zoom: 1 });\n const handleTransformChange = useCallback((next: ViewportTransform) => {\n transformRef.current = next;\n }, []);\n\n // ── Slot sorting ──\n const slots = useMemo(() => sortSlots(children), [children]);\n\n // ── Helpers ──\n const getTransform = useCallback((): ViewportTransform => {\n return viewportHandleRef.current?.getTransform() ?? transformRef.current;\n }, []);\n\n const localScreenPoint = useCallback(\n (clientX: number, clientY: number): Point2D => {\n const el = rootRef.current;\n if (!el) return { x: 0, y: 0 };\n const rect = el.getBoundingClientRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n },\n []\n );\n\n const screenToWorldLocal = useCallback(\n (clientX: number, clientY: number): Point2D => {\n const local = localScreenPoint(clientX, clientY);\n return worldFromScreen(local, getTransform());\n },\n [localScreenPoint, getTransform]\n );\n\n // ── Targets / hit testing ──\n const nodesRef = useLatest(nodes);\n const groupsRef = useLatest(groups);\n const selectionRef = useLatest(selection);\n const setSelectionRef = useLatest(setSelection);\n const setNodesRef = useLatest(setNodes);\n const disabledRef = useLatest(disabled);\n\n const findHitTarget = useCallback(\n (clientX: number, clientY: number): NodeGraphTarget => {\n const worldPoint = screenToWorldLocal(clientX, clientY);\n const currentNodes = nodesRef.current;\n const currentGroups = groupsRef.current;\n // Iterate top-to-bottom so the last-rendered node \"wins\" — matches what\n // the user sees on top.\n for (let i = currentNodes.length - 1; i >= 0; i--) {\n const node = currentNodes[i];\n if (\n node &&\n isPointInNode(\n worldPoint,\n node,\n store.getMeasuredSize(node.id),\n defaultNodeSize\n )\n ) {\n return { kind: 'node', id: node.id };\n }\n }\n // Edges sit above groups / background but below nodes, so hit-test them\n // after nodes and before groups. Drawn on a canvas → no DOM to walk.\n const edgeThreshold = EDGE_HIT_PX / (getTransform().zoom || 1);\n const edgeId = findEdgeAtPoint(\n worldPoint,\n store.getData().edges,\n store.getNodeById,\n store.getPortPosition,\n edgeThreshold\n );\n if (edgeId) return { kind: 'edge', id: edgeId };\n for (let i = currentGroups.length - 1; i >= 0; i--) {\n const group = currentGroups[i];\n if (group && isPointInRect(worldPoint, group.bounds)) {\n return { kind: 'group', id: group.id };\n }\n }\n return { kind: 'empty', worldPoint };\n },\n [\n defaultNodeSize,\n screenToWorldLocal,\n nodesRef,\n groupsRef,\n store,\n getTransform,\n ]\n );\n\n // ── Connection drag (ports) ──\n const { onPortPointerDown, onEdgeReconnectStart } = useNodeGraphConnection({\n viewportRef: rootRef,\n store,\n getTransform,\n isValidConnection,\n onConnectStart,\n onConnectEnd,\n emitEdgesChange: setEdges,\n emitSelectionChange: setSelection,\n });\n\n // ── Node body pointerdown — drag (single/multi) + select on release ──\n const { onNodeBodyPointerDown, onNodeBodyPointerUp } = useNodeGraphNodeDrag({\n store,\n getTransform,\n localScreenPoint,\n snapToGrid,\n disabled,\n emitNodesChange: setNodes,\n emitSelectionChange: setSelection,\n });\n\n // ── Group interaction (drag + resize + click-select) ──\n const setGroupsRef = useLatest(setGroups);\n const {\n onGroupBodyPointerDown,\n onGroupHandlePointerDown,\n onGroupBodyPointerUp,\n } = useNodeGraphGroupDrag({\n store,\n getTransform,\n localScreenPoint,\n snapToGrid,\n disabled,\n defaultNodeSize,\n emitGroupsChange: setGroups,\n emitNodesChange: setNodes,\n emitSelectionChange: setSelection,\n });\n\n // ── Group label / colour edits ──\n //\n // The library's group overlay owns the inline label editor and the\n // colour swatch — these handlers translate UI commits into a controlled\n // `onGroupsChange` emission so the consumer receives the full next\n // groups array, same as every other group mutation.\n const handleGroupLabelChange = useCallback(\n (group: NodeGraphGroup, nextLabel: string): void => {\n const trimmed = nextLabel.trim();\n const nextGroups = groupsRef.current.map(g => {\n if (g.id !== group.id) return g;\n if ((g.label ?? '') === trimmed) return g;\n if (trimmed === '') {\n // Empty label collapses back to `undefined` so the default\n // \"Group\" placeholder shows up cleanly on next render.\n const { label: _omit, ...rest } = g;\n void _omit;\n return rest;\n }\n return { ...g, label: trimmed };\n });\n setGroupsRef.current(nextGroups);\n },\n [groupsRef, setGroupsRef]\n );\n\n const handleGroupColorChange = useCallback(\n (group: NodeGraphGroup, nextColor: string): void => {\n if (group.color === nextColor) return;\n const nextGroups = groupsRef.current.map(g =>\n g.id === group.id ? { ...g, color: nextColor } : g\n );\n setGroupsRef.current(nextGroups);\n },\n [groupsRef, setGroupsRef]\n );\n\n // ── Marquee selection from the Viewport ──\n const [, startMarqueeTransition] = useTransition();\n const handleViewportSelection = useCallback(\n (info: ViewportSelectionEvent): void => {\n if (disabledRef.current) return;\n if (info.inProgress) {\n // Live preview: drive the marquee overlay via the store so canvas\n // hit-test for hover/edge can also respond if needed.\n store.setInteraction({\n kind: 'marquee',\n startWorld: { x: info.rect.x, y: info.rect.y },\n currentWorld: {\n x: info.rect.x + info.rect.width,\n y: info.rect.y + info.rect.height,\n },\n additive: info.additive,\n });\n return;\n }\n\n // Final marquee — compute selection from rect intersection.\n store.setInteraction({ kind: 'idle' });\n const rect: WorldRect = info.rect;\n\n // A near-zero-area rect is a plain click, not a drag. Use it to select\n // an edge — edges are drawn on a canvas and never receive the pointer\n // event directly, so this is where edge clicks are resolved. Falls\n // through to the normal node-marquee / clear path when no edge is hit.\n const zoom = getTransform().zoom || 1;\n const isClick =\n rect.width * zoom <= EDGE_HIT_PX && rect.height * zoom <= EDGE_HIT_PX;\n if (isClick) {\n const edgeId = findEdgeAtPoint(\n { x: rect.x, y: rect.y },\n store.getData().edges,\n store.getNodeById,\n store.getPortPosition,\n EDGE_HIT_PX / zoom\n );\n if (edgeId) {\n const current = selectionRef.current;\n setSelectionRef.current({\n nodes: info.additive ? current.nodes : [],\n edges: toggleSelected(current.edges, edgeId, info.additive),\n groups: info.additive ? current.groups : [],\n });\n return;\n }\n }\n\n const hits: string[] = [];\n for (const node of nodesRef.current) {\n const box = getNodeBox(\n node,\n store.getMeasuredSize(node.id),\n defaultNodeSize\n );\n if (\n rectsIntersect(\n { x: box.x, y: box.y, width: box.width, height: box.height },\n rect\n )\n ) {\n hits.push(node.id);\n }\n }\n const current = selectionRef.current;\n const nextNodes = info.additive\n ? Array.from(new Set([...current.nodes, ...hits]))\n : hits;\n // Selection commits can cascade into expensive renderNode bodies for\n // a large hit set — let React schedule it as non-urgent so the\n // gesture release doesn't hitch.\n startMarqueeTransition(() => {\n setSelectionRef.current({\n nodes: nextNodes,\n edges: info.additive ? current.edges : [],\n groups: info.additive ? current.groups : [],\n });\n });\n },\n [\n store,\n nodesRef,\n selectionRef,\n setSelectionRef,\n defaultNodeSize,\n disabledRef,\n getTransform,\n ]\n );\n\n // ── Context menu ──\n const onContextMenuRef = useLatest(onContextMenu);\n const handleContextMenu = useCallback(\n (event: React.MouseEvent<HTMLDivElement>): void => {\n if (disabledRef.current) return;\n event.preventDefault();\n // Walk up the DOM from the event target to find data attributes\n // identifying a port or a node. Ports take priority because they're\n // descendants of nodes — the loop bails on the first match.\n const targetEl = event.target as Element | null;\n let portRef: NodeGraphPortRef | null = null;\n let nodeId: string | null = null;\n let cursor: Element | null = targetEl;\n while (cursor && cursor !== rootRef.current) {\n const portId = cursor.getAttribute('data-port-id');\n const dataNodeId = cursor.getAttribute('data-node-id');\n if (portId && dataNodeId) {\n portRef = { node: dataNodeId, port: portId };\n break;\n }\n if (dataNodeId && !nodeId) {\n nodeId = dataNodeId;\n }\n cursor = cursor.parentElement;\n }\n const screenPoint = localScreenPoint(event.clientX, event.clientY);\n const worldPoint = worldFromScreen(screenPoint, getTransform());\n let resolvedTarget: NodeGraphTarget;\n if (portRef) {\n resolvedTarget = {\n kind: 'port',\n node: portRef.node,\n port: portRef.port,\n };\n } else if (nodeId) {\n resolvedTarget = { kind: 'node', id: nodeId };\n } else {\n resolvedTarget = findHitTarget(event.clientX, event.clientY);\n }\n const info: NodeGraphContextMenuInfo = {\n target: resolvedTarget,\n screenPoint,\n worldPoint,\n };\n // Consumer callback fires first — they may render their own menu,\n // log telemetry, etc.\n onContextMenuRef.current?.(info);\n // SpawnPalette subscribers (if mounted) get pinged for empty /\n // group targets: those are the \"drop a new node here\" spots.\n if (resolvedTarget.kind === 'empty' || resolvedTarget.kind === 'group') {\n store.requestSpawn({ worldPoint, screenPoint });\n }\n },\n [\n findHitTarget,\n localScreenPoint,\n getTransform,\n onContextMenuRef,\n disabledRef,\n store,\n ]\n );\n\n // ── Edge hover + endpoint grab (canvas edges → pointer hit-test) ──\n const { onRootPointerMove, onRootPointerLeave, onRootPointerDownCapture } =\n useNodeGraphEdgeInteraction({\n store,\n getTransform,\n screenToWorldLocal,\n localScreenPoint,\n onEdgeReconnectStart,\n disabled,\n });\n\n // ── Delete ──\n //\n // Without a consumer-supplied `onDelete`, the library cascades the\n // selection through nodes / edges / groups automatically: nodes get\n // removed, edges are filtered out when either endpoint is gone (or\n // when the edge itself is selected), groups in the selection drop.\n // Saves every consumer the same 8-line filter and avoids the classic\n // \"I forgot to clean up orphan edges\" bug.\n //\n // If the consumer provides `onDelete`, they take over completely —\n // they can still call the exported `applyCascadeDelete` helper to do\n // the same filtering plus their own snapshot / undo / confirmation\n // logic on top.\n const handleDeleteInternal = useCallback(\n (sel: NodeGraphSelection): void => {\n if (onDelete) {\n onDelete(sel);\n return;\n }\n const currentNodes = nodesRef.current;\n const currentEdges = store.getData().edges;\n const currentGroups = groupsRef.current;\n const removedNodeIds = new Set(sel.nodes);\n const removedEdgeIds = new Set(sel.edges);\n const removedGroupIds = new Set(sel.groups);\n if (\n removedNodeIds.size === 0 &&\n removedEdgeIds.size === 0 &&\n removedGroupIds.size === 0\n ) {\n return;\n }\n if (removedNodeIds.size > 0) {\n setNodesRef.current(\n currentNodes.filter(n => !removedNodeIds.has(n.id))\n );\n }\n const nextEdges = currentEdges.filter(\n e =>\n !removedEdgeIds.has(e.id) &&\n !removedNodeIds.has(e.source.node) &&\n !removedNodeIds.has(e.target.node)\n );\n if (nextEdges.length !== currentEdges.length) {\n setEdges(nextEdges);\n }\n if (removedGroupIds.size > 0) {\n setGroupsRef.current(\n currentGroups.filter(g => !removedGroupIds.has(g.id))\n );\n }\n setSelectionRef.current({ nodes: [], edges: [], groups: [] });\n },\n [\n onDelete,\n store,\n nodesRef,\n groupsRef,\n setNodesRef,\n setGroupsRef,\n setSelectionRef,\n setEdges,\n ]\n );\n\n // ── Keyboard ──\n const { onKeyDown } = useNodeGraphKeyboard({\n store,\n emitNodesChange: setNodes,\n emitSelectionChange: setSelection,\n onDelete: handleDeleteInternal,\n onActivate,\n snapToGrid,\n disabled,\n });\n\n // ── Canvas draw callbacks (stable identities via useLatest) ──\n const backgroundProps = slots.backgroundProps;\n const backgroundVariant = backgroundProps?.variant ?? 'dots';\n const backgroundGap = backgroundProps?.gap ?? 24;\n const backgroundColor = backgroundProps?.color;\n const backgroundFill = backgroundProps?.background;\n\n const drawBackgroundLayer = useCallback(\n (\n ctx: CanvasRenderingContext2D,\n info: Parameters<React.ComponentProps<typeof ViewportLayer>['draw']>[1]\n ): void => {\n const theme = buildDrawTheme(info.theme);\n drawBackground(ctx, info, theme, {\n variant: backgroundVariant,\n gap: backgroundGap,\n color: backgroundColor,\n background: backgroundFill,\n });\n },\n [backgroundVariant, backgroundGap, backgroundColor, backgroundFill]\n );\n\n const drawGroupsLayer = useCallback(\n (\n ctx: CanvasRenderingContext2D,\n info: Parameters<React.ComponentProps<typeof ViewportLayer>['draw']>[1]\n ): void => {\n const data = store.getData();\n const sel = store.getSelection();\n const interaction = store.getInteraction();\n const theme = buildDrawTheme(info.theme);\n drawGroups(ctx, info, data, sel, interaction, theme);\n },\n [store]\n );\n\n const edgeStyleRef = useLatest(edgeStyle);\n const drawEdgesLayer = useCallback(\n (\n ctx: CanvasRenderingContext2D,\n info: Parameters<React.ComponentProps<typeof ViewportLayer>['draw']>[1]\n ): void => {\n const data = store.getData();\n const sel = store.getSelection();\n const hover = store.getHover();\n const interaction = store.getInteraction();\n const theme = buildDrawTheme(info.theme);\n drawEdges(\n ctx,\n info,\n data,\n sel,\n hover.hoveredEdgeId,\n interaction,\n store.getNodeById,\n store.getPortPosition,\n edgeStyleRef.current,\n theme\n );\n },\n [store, edgeStyleRef]\n );\n\n const drawPreviewLayer = useCallback(\n (\n ctx: CanvasRenderingContext2D,\n info: Parameters<React.ComponentProps<typeof ViewportLayer>['draw']>[1]\n ): void => {\n const interaction = store.getInteraction();\n if (interaction.kind !== 'connect') return;\n const theme = buildDrawTheme(info.theme);\n drawConnectionPreview(\n ctx,\n info,\n interaction,\n portRef => {\n const resolved = resolvePortRef(\n portRef,\n store.getNodeById,\n store.getPortPosition\n );\n if (!resolved) return null;\n return { position: resolved.position, side: resolved.side };\n },\n theme\n );\n },\n [store]\n );\n\n // Force invalidation of canvas layers when the relevant store slices change.\n // `invalidate` reads `viewportHandleRef.current` at call time so a viewport\n // remount (which would re-bind the imperative handle) doesn't leave us\n // pointing at a stale handle reference.\n useLayoutEffect(() => {\n const invalidate = (layer: NodeGraphLayerName): void => {\n viewportHandleRef.current?.invalidate(layer);\n };\n const unsubs: Array<() => void> = [\n store.subscribeData(() => {\n invalidate('groups');\n invalidate('edges');\n }),\n store.subscribeSelection(() => {\n invalidate('groups');\n invalidate('edges');\n }),\n store.subscribeHover(() => {\n invalidate('edges');\n }),\n // Interaction drives the in-flight drag delta — every canvas layer\n // that visualises something positional needs to redraw on each tick:\n // edges (connectors follow dragged nodes live, not snap on release),\n // groups (drag-groups / resize-group), and the connection preview.\n store.subscribeInteraction(() => {\n invalidate('edges');\n invalidate('groups');\n invalidate('preview');\n }),\n // Port positions (re)registered by `<NodeGraph.Port>` slots — edges\n // anchor at the registered point, so any change must redraw.\n store.subscribePortPositions(() => {\n invalidate('edges');\n }),\n // Measured node sizes feed the marquee / hit-test bounds — no canvas\n // redraw needed, but subscribe so React-side reads in renderers see\n // the updates if they depend on it.\n store.subscribeMeasuredSizes(() => undefined),\n ];\n return () => {\n for (const unsub of unsubs) unsub();\n };\n }, [store]);\n\n // ── Imperative handle ──\n useImperativeHandle(\n ref,\n (): NodeGraphHandle => ({\n fitToContent: padding => {\n const vp = viewportHandleRef.current;\n if (!vp) return;\n const bounds = computeNodesBounds(\n nodesRef.current,\n store.getMeasuredSize,\n defaultNodeSize\n );\n if (bounds.width === 0 && bounds.height === 0) return;\n vp.fitToContent(bounds, padding ?? 32);\n },\n fitToSelection: padding => {\n const vp = viewportHandleRef.current;\n if (!vp) return;\n const sel = selectionRef.current.nodes;\n if (sel.length === 0) return;\n const selectedIds = new Set(sel);\n const selectedNodes = nodesRef.current.filter(n =>\n selectedIds.has(n.id)\n );\n const bounds = computeNodesBounds(\n selectedNodes,\n store.getMeasuredSize,\n defaultNodeSize\n );\n if (bounds.width === 0 && bounds.height === 0) return;\n vp.fitToContent(bounds, padding ?? 64);\n },\n focusNode: idStr => {\n const vp = viewportHandleRef.current;\n if (!vp) return;\n const node = store.getNodeById(idStr);\n if (!node) return;\n const box = getNodeBox(\n node,\n store.getMeasuredSize(node.id),\n defaultNodeSize\n );\n vp.centerOn({\n x: box.x + box.width / 2,\n y: box.y + box.height / 2,\n });\n },\n centerOn: (point, z) => {\n viewportHandleRef.current?.centerOn(point, z);\n },\n zoomToRect: (rect, padding) => {\n viewportHandleRef.current?.zoomToRect(rect, padding);\n },\n getTransform: () => getTransform(),\n getSize: () =>\n viewportHandleRef.current?.getSize() ?? { width: 0, height: 0 },\n worldToScreen: point => screenFromWorld(point, getTransform()),\n screenToWorld: point => worldFromScreen(point, getTransform()),\n invalidate: layerName => viewportHandleRef.current?.invalidate(layerName),\n }),\n [nodesRef, selectionRef, defaultNodeSize, getTransform, store]\n );\n\n // Edge labels rendered as world-space children at the midpoint of each\n // edge. Mounted as its own subscriber component so the labels follow\n // dragged nodes live without re-rendering the whole NodeGraph.\n const renderEdgeLabelRef = useLatest(renderEdgeLabel);\n\n return (\n <NodeGraphStoreContext.Provider value={store}>\n {/*\n The root is an event-delegation wrapper for graph-wide keyboard\n shortcuts and pointer capture; it is not itself focusable. The\n focusable, labelled interactive surface is the inner <Viewport>\n (ariaLabel + ariaRoledescription=\"node graph\"), which jsx-a11y\n cannot see through this wrapper.\n */}\n {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}\n <div\n ref={rootRef}\n id={id}\n data-testid={testId}\n onKeyDown={onKeyDown}\n onContextMenu={handleContextMenu}\n onPointerDownCapture={onRootPointerDownCapture}\n onPointerMove={onRootPointerMove}\n onPointerLeave={onRootPointerLeave}\n className={cx(nodeGraphRootStyle, className)}\n style={style}\n >\n <Viewport\n ref={viewportHandleRef}\n responsive={responsive}\n height={height}\n pan={pan}\n zoom={zoom}\n minZoom={minZoom}\n maxZoom={maxZoom}\n selectionRect={{ enabled: selectionRect, button: 'left' }}\n onSelectionChange={handleViewportSelection}\n onTransformChange={handleTransformChange}\n disabled={disabled}\n ariaLabel={ariaLabel}\n ariaRoledescription=\"node graph\"\n >\n {slots.hasBackground ? (\n <ViewportLayer name=\"background\" draw={drawBackgroundLayer} />\n ) : null}\n <ViewportLayer name=\"groups\" draw={drawGroupsLayer} />\n <ViewportLayer name=\"edges\" draw={drawEdgesLayer} />\n <ViewportWorld>\n {groups.map(group => (\n <NodeGraphGroupView\n key={group.id}\n group={group}\n onBodyPointerDown={onGroupBodyPointerDown}\n onBodyPointerUp={onGroupBodyPointerUp}\n onHandlePointerDown={onGroupHandlePointerDown}\n onBodyContextMenu={handleContextMenu}\n onLabelChange={handleGroupLabelChange}\n onColorChange={handleGroupColorChange}\n />\n ))}\n {nodes.map(node => (\n <NodeGraphNodeView\n key={node.id}\n node={node}\n renderNode={renderNode}\n onBodyPointerDown={onNodeBodyPointerDown}\n onBodyPointerUp={onNodeBodyPointerUp}\n onPortPointerDown={onPortPointerDown}\n onBodyContextMenu={handleContextMenu}\n />\n ))}\n <EdgeLabelsLayer\n renderEdgeLabelRef={renderEdgeLabelRef}\n hasRenderEdgeLabel={renderEdgeLabel !== undefined}\n />\n </ViewportWorld>\n <ViewportLayer name=\"preview\" draw={drawPreviewLayer} />\n {slots.hasMinimap || slots.toolbars.length > 0 ? (\n <ViewportOverlay>\n {slots.hasMinimap ? (\n <NodeGraphMinimapInner {...(slots.minimapProps ?? {})} />\n ) : null}\n {slots.toolbars.map((toolbarProps, i) => (\n <NodeGraphToolbarInner\n // Placement is part of the identity — multiple toolbars\n // in the same NodeGraph use different placements as\n // their natural key.\n key={toolbarProps.placement ?? `toolbar-${i}`}\n {...toolbarProps}\n />\n ))}\n </ViewportOverlay>\n ) : null}\n </Viewport>\n {/* SpawnPalette renders in a portal (via CommandPalette) so it\n sits at the React tree root, outside the Viewport. Subscribes\n to the store's spawn-request channel for right-click triggers. */}\n {slots.spawnPaletteProps ? (\n <NodeGraphSpawnPaletteInner {...slots.spawnPaletteProps} />\n ) : null}\n </div>\n </NodeGraphStoreContext.Provider>\n );\n};\n\n// ─── Compound exports ───\n\ntype NodeGraphCompound = typeof NodeGraphImpl & {\n Minimap: typeof NodeGraphMinimap;\n Background: typeof NodeGraphBackground;\n Port: typeof NodeGraphPort;\n PortVisual: typeof NodeGraphPortVisual;\n Pin: typeof NodeGraphPin;\n NodeBody: typeof NodeGraphNodeBody;\n NodeHeader: typeof NodeGraphNodeHeader;\n NodeSection: typeof NodeGraphNodeSection;\n PinList: typeof NodeGraphPinList;\n PinRow: typeof NodeGraphPinRow;\n Toolbar: typeof NodeGraphToolbar;\n ToolbarSeparator: typeof NodeGraphToolbarSeparator;\n FitContentButton: typeof NodeGraphFitContentButton;\n FitSelectionButton: typeof NodeGraphFitSelectionButton;\n ZoomInButton: typeof NodeGraphZoomInButton;\n ZoomOutButton: typeof NodeGraphZoomOutButton;\n ResetZoomButton: typeof NodeGraphResetZoomButton;\n SpawnPalette: typeof NodeGraphSpawnPalette;\n};\n\nNodeGraphImpl.displayName = 'NodeGraph';\n\nconst NodeGraphWithSlots = NodeGraphImpl as NodeGraphCompound;\nNodeGraphWithSlots.Minimap = NodeGraphMinimap;\nNodeGraphWithSlots.Background = NodeGraphBackground;\nNodeGraphWithSlots.Port = NodeGraphPort;\nNodeGraphWithSlots.PortVisual = NodeGraphPortVisual;\nNodeGraphWithSlots.Pin = NodeGraphPin;\nNodeGraphWithSlots.NodeBody = NodeGraphNodeBody;\nNodeGraphWithSlots.NodeHeader = NodeGraphNodeHeader;\nNodeGraphWithSlots.NodeSection = NodeGraphNodeSection;\nNodeGraphWithSlots.PinList = NodeGraphPinList;\nNodeGraphWithSlots.PinRow = NodeGraphPinRow;\nNodeGraphWithSlots.Toolbar = NodeGraphToolbar;\nNodeGraphWithSlots.ToolbarSeparator = NodeGraphToolbarSeparator;\nNodeGraphWithSlots.FitContentButton = NodeGraphFitContentButton;\nNodeGraphWithSlots.FitSelectionButton = NodeGraphFitSelectionButton;\nNodeGraphWithSlots.ZoomInButton = NodeGraphZoomInButton;\nNodeGraphWithSlots.ZoomOutButton = NodeGraphZoomOutButton;\nNodeGraphWithSlots.ResetZoomButton = NodeGraphResetZoomButton;\nNodeGraphWithSlots.SpawnPalette = NodeGraphSpawnPalette;\n\nexport const NodeGraph = NodeGraphWithSlots;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsGA;AACA;AACA;AACA;AACE;AACA;AACA;;AAEF;AAEA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACH;;AAyCE;AACA;AACA;;;;AAKA;;;;;AASA;AACE;AACA;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACA;AACD;;;;;;;;AASE;AACH;;AAGE;AACF;;AAGA;AACA;AACE;;;AAIF;;AAGA;;;;AAMI;AACA;;AACA;AACA;;;;AAQA;AACF;;AAKF;AACA;AACA;AACA;AACA;AACA;;;AAKI;AACA;;;AAGA;AACE;AACA;AAEE;;;;;;AAYJ;;AAQA;;AACA;AACE;;;;;AAKF;AACF;;;;;;;AAQC;;AAIH;AACE;;;;;;AAMA;AACA;AACD;;AAGD;;;;;;AAME;AACA;AACD;;AAGD;;;;;;;;AAYE;AACA;AACA;AACD;;;;;;;;AAUG;;AAEE;AAAuB;;AACU;AACjC;;;;AAKE;;;AAGJ;AACA;AACF;;AAME;;AACA;AAGA;AACF;;AAKF;AACA;;;AAGI;;;;AAII;AACA;AACA;;;AAGC;;AAEF;;;;;AAMH;;;;;;AAOA;;AAGE;;AAQE;;AAEE;AACA;AACA;AACD;;;;;AAML;AACE;AAKA;AAME;;;AAGJ;AACA;AACE;;;;;;;AAOE;AACA;AACA;AACD;AACH;AACF;;;;;;;;AASC;;AAIH;AACA;;;;;;;AAOI;;;;;;;AAOE;;;;AAIA;;;AAGA;;AAEF;;AAEA;;AAEE;AACE;;;;;;;;;;;AASJ;AACE;;;;;;AAMF;;;AAGA;;;AAGF;;;;;;;AAQC;;;;;;;;;AAYA;;;;;;;;;;;;;;AAeH;;;;;AAMI;;AAEA;;;;AAIA;;AAGE;;;AAIF;;;AAKA;;;;;;AASA;;;AAKA;AACF;;;;;;;;;AAUC;;AAIH;;AAEE;AACA;AACA;;;;AAID;;AAGD;AACA;AACA;AACA;AACA;;;AAQI;AACE;AACA;AACA;AACA;AACD;;;AAUD;AACA;AACA;;AAEA;AACF;AAIF;;AAMI;AACA;AACA;AACA;;AAEA;AAYF;;AASE;AACA;;;;AAOI;AAKA;AAAe;AACf;;AAIN;;;;;;AASA;AACE;AACF;AACA;AACE;;;AAGA;AACA;;;AAGA;AACA;;AAEA;;;;;AAKA;;;;AAIA;;;AAGA;;AAEA;;;;AAIA;;AAEF;;AAC8B;AAC9B;AACF;;AAGA;;AAIM;AACA;;AACA;;;;;;AASA;AACA;;AACA;AACA;;AACA;;AAIA;;;;;;AASA;AACA;;;AAEA;;AACA;;;;AAQC;;AAEH;;;AAGA;;;AAGA;AACA;;;AAIA;AACD;;;;AAOH;AAEA;;;;AAmFoB;AAetB;AAyBA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEO;;"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// ─── Slot markers ───
|
|
2
|
+
/**
|
|
3
|
+
* Symbol attached to `<NodeGraph.Minimap>` and other slot subcomponents so
|
|
4
|
+
* the parent `<NodeGraph>` can identify them without relying on
|
|
5
|
+
* `displayName` (which gets clobbered by `React.memo` / minifiers).
|
|
6
|
+
*/
|
|
7
|
+
const NODE_GRAPH_SLOT = Symbol.for('etui.nodegraph.slot');
|
|
8
|
+
|
|
9
|
+
export { NODE_GRAPH_SLOT };
|
|
10
|
+
//# sourceMappingURL=NodeGraph.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeGraph.types.js","sources":["src/components/editor/NodeGraph/NodeGraph.types.ts"],"sourcesContent":["import type { Point2D } from '@/components/primitives/canvas/canvas.types';\nimport type {\n ViewportPanConfig,\n ViewportSize,\n ViewportTransform,\n ViewportZoomConfig,\n WorldRect,\n} from '@/components/primitives/viewport';\nimport type { BaseComponent } from '@/types/common';\nimport type { Prettify } from '@/types/utilities';\nimport type React from 'react';\n\n// ─── Geometry primitives ───\n\n/** Side of a node a port is anchored to. */\nexport type NodeGraphPortSide = 'left' | 'right' | 'top' | 'bottom';\n\n/** Reference to a single port on a node. */\nexport interface NodeGraphPortRef {\n /** Owning node id. */\n node: string;\n /** Port id within that node. */\n port: string;\n}\n\n// ─── Data model ───\n\nexport interface NodeGraphNode {\n /** Stable identity. */\n id: string;\n /** Position in world coordinates (top-left corner of the node body). */\n position: Point2D;\n /**\n * Override the auto-measured width of the node body. Use this when you\n * need fixed-size nodes. When omitted, the library measures the rendered\n * node DOM and uses that size for hit-testing, marquee selection, and\n * minimap geometry.\n */\n width?: number;\n /** Override the auto-measured height of the node body. See {@link width}. */\n height?: number;\n /** Arbitrary consumer payload passed to `renderNode`. */\n data?: unknown;\n /**\n * When false, the node cannot be moved by drag. Selection still works.\n * @default true\n */\n draggable?: boolean;\n /**\n * When false, the node cannot be selected (clicks pass through to the\n * background). Drag is also disabled.\n * @default true\n */\n selectable?: boolean;\n}\n\nexport interface NodeGraphEdge {\n /** Stable identity. */\n id: string;\n /** Edge source endpoint. */\n source: NodeGraphPortRef;\n /** Edge target endpoint. */\n target: NodeGraphPortRef;\n /** Arbitrary consumer payload (e.g. animation flags, weights). */\n data?: unknown;\n /**\n * Optional HTML label rendered absolutely above the edge midpoint.\n * Use `renderEdgeLabel` for full control, or pass a node here for the\n * default centred placement.\n */\n label?: React.ReactNode;\n /**\n * Override the stroke colour of this edge in its default state.\n * Selected / hovered edges still pick up the theme accents (so the\n * interaction state stays visible), this colour is used only when\n * neither state is active. Useful for type-coloured wires — assign\n * the colour at edge-creation time based on the source pin's data\n * type, the way UE5 paints exec / float / bool wires differently.\n */\n color?: string;\n}\n\n/**\n * A visual backdrop rectangle drawn under nodes. Groups in v1 are purely\n * decorative — nodes are not children of a group, they simply happen to\n * overlap with it. Selection of a group through marquee/click can be\n * wired up by the consumer via `onContextMenu` / future selection API.\n */\nexport interface NodeGraphGroup {\n /** Stable identity. */\n id: string;\n /** Group rectangle in world coordinates. */\n bounds: WorldRect;\n /** Optional title shown above the group's top edge. */\n label?: string;\n /** Optional CSS colour string used for the group tint and label. */\n color?: string;\n}\n\nexport interface NodeGraphSelection {\n /** Selected node ids. */\n nodes: string[];\n /** Selected edge ids. */\n edges: string[];\n /** Selected group ids. */\n groups: string[];\n}\n\n// ─── Targets / events ───\n\n/** Discriminated union describing what was hit by a pointer event. */\nexport type NodeGraphTarget =\n | { kind: 'node'; id: string }\n | { kind: 'edge'; id: string }\n | { kind: 'port'; node: string; port: string }\n | { kind: 'group'; id: string }\n | { kind: 'empty'; worldPoint: Point2D };\n\nexport interface NodeGraphContextMenuInfo {\n target: NodeGraphTarget;\n /** Screen-space (CSS-pixel) position of the pointer relative to viewport. */\n screenPoint: Point2D;\n /** World-space position of the pointer. */\n worldPoint: Point2D;\n}\n\nexport interface NodeGraphConnectStartInfo {\n source: NodeGraphPortRef;\n /** World-space position the drag started at (the source port). */\n worldPoint: Point2D;\n}\n\nexport interface NodeGraphConnectEndInfo {\n source: NodeGraphPortRef;\n /** Resolved drop target, or null when the drag ended on empty space. */\n target: NodeGraphPortRef | null;\n /** True when the drop was rejected (target invalid or empty). */\n cancelled: boolean;\n /**\n * World-space position the drag was released at. With `cancelled` + a null\n * `target` this is an empty-space drop — the hook for a \"drag a wire onto\n * the canvas → open a create-node menu here → connect the new node to\n * `source`\" flow. Place the spawned node at this point.\n */\n worldPoint: Point2D;\n /**\n * Screen-space (CSS-pixel, relative to the viewport top-left) release\n * position — position your create-node menu / popover here.\n */\n screenPoint: Point2D;\n}\n\nexport interface NodeGraphConnectionValidationInfo {\n /** True when source and target reference the same node. */\n sameNode: boolean;\n /** Side combination shorthand, e.g. `'right->left'`. */\n sideCombo: string;\n /** `dataType` declared on the source `<NodeGraph.Port>`, if any. */\n sourceDataType?: string;\n /** `dataType` declared on the target `<NodeGraph.Port>`, if any. */\n targetDataType?: string;\n}\n\n// ─── Rendering ───\n\n/** Context passed to `renderNode`. */\nexport interface NodeGraphRenderCtx {\n /** True when the node is part of the current selection. */\n selected: boolean;\n /** True when the node is being dragged (single or multi). */\n dragging: boolean;\n /** True when the pointer is hovering over the node body. */\n hovered: boolean;\n /** Current viewport zoom — useful for LOD rendering. */\n zoom: number;\n}\n\n/** Resolved port metadata passed to the `edgeStyle` callback. */\nexport interface NodeGraphEdgeStyleCtx {\n /** True when the edge is in the current selection. */\n selected: boolean;\n /** True when the pointer is currently hovering over the edge. */\n hovered: boolean;\n /**\n * The `side` and `dataType` declared on the source `<NodeGraph.Port>`,\n * resolved from the store. `null` when the port hasn't been measured\n * yet (e.g. first paint before `useLayoutEffect` has run).\n */\n sourcePort: { side: NodeGraphPortSide; dataType?: string } | null;\n /** Same for the target endpoint. */\n targetPort: { side: NodeGraphPortSide; dataType?: string } | null;\n}\n\n/**\n * Style overrides for a single edge, returned by `edgeStyle`. Any field\n * left undefined falls back to the default (theme + `edge.color`).\n */\nexport interface NodeGraphEdgeStyle {\n /** Stroke colour. Overrides `edge.color`. */\n color?: string;\n /** Stroke width in screen pixels. */\n width?: number;\n /**\n * Marching-ant dash pattern, in screen pixels.\n * Example: `[8, 6]` draws 8 px dash + 6 px gap repeating.\n * Pass `null` (or omit) for a solid stroke.\n */\n dash?: ReadonlyArray<number> | null;\n}\n\n/**\n * Per-edge style hook. Called on every frame for every edge — keep it\n * cheap (map lookup, ternary). The library memoizes nothing here; if\n * the result is expensive, hoist into a `useMemo` keyed by inputs.\n */\nexport type NodeGraphEdgeStyleFn = (\n edge: NodeGraphEdge,\n ctx: NodeGraphEdgeStyleCtx\n) => NodeGraphEdgeStyle | undefined;\n\n// ─── Port slot ───\n\n/**\n * Built-in port handle shapes. `circle` is the default data-pin look,\n * `triangle` is the UE-style exec/flow arrow, `diamond` / `square` cover\n * the other common node-editor conventions. Render any other visual by\n * passing `children` to `<NodeGraph.Port>`.\n */\nexport type NodeGraphPortShape = 'circle' | 'triangle' | 'diamond' | 'square';\n\n/**\n * Props for `<NodeGraph.Port>` — the compound child rendered inside\n * `renderNode` to declare a connection endpoint.\n *\n * The library tracks the slot's DOM position and registers it as the\n * anchor for any edge that references this port. Pointer events on the\n * slot start a connection drag automatically — no manual wiring from the\n * consumer.\n *\n * @example\n * ```tsx\n * <NodeGraph\n * renderNode={(node, ctx) => (\n * <MyNodeBody>\n * <Row>\n * <NodeGraph.Port id=\"exec-in\" side=\"left\" dataType=\"exec\" />\n * Execute\n * </Row>\n * </MyNodeBody>\n * )}\n * />\n * ```\n */\nexport interface NodeGraphPortSlotProps {\n /** Stable identity within the owning node — referenced by `NodeGraphEdge`. */\n id: string;\n /** Which side of the node the port anchors to. Determines Bézier tangent. */\n side: NodeGraphPortSide;\n /**\n * Opaque type token forwarded to `isValidConnection` and exposed on\n * `data-port-data-type` for CSS theming. Not interpreted by the library.\n */\n dataType?: string;\n /**\n * Built-in handle shape used when no `children` are supplied.\n * @default 'circle'\n */\n shape?: NodeGraphPortShape;\n /**\n * Handle colour for the built-in visual (any CSS colour). When omitted,\n * the port inherits the theme focus colour; the connection-drag states\n * (source / candidate / invalid) still override it so interaction stays\n * visible. Ignored when `children` are supplied.\n */\n color?: string;\n /**\n * Force the built-in visual filled (`true`) or hollow (`false`). When\n * omitted, the port fills automatically while it is connected to at\n * least one edge and stays hollow otherwise — the standard \"wired vs\n * unwired pin\" convention. Ignored when `children` are supplied.\n */\n filled?: boolean;\n /**\n * Replace the built-in port visual entirely. When provided, no built-in\n * chrome is rendered — the children fill the slot wrapper. State for\n * theming (source / candidate / hover / invalid / connected) is exposed\n * via `data-port-*` attributes on the wrapper.\n */\n children?: React.ReactNode;\n /** Accessible label. Falls back to `${side} port ${id}`. */\n label?: string;\n /** Extra class applied to the slot wrapper. */\n className?: string;\n /** Inline style override on the slot wrapper. */\n style?: React.CSSProperties;\n}\n\n// ─── Imperative handle ───\n\nexport interface NodeGraphHandle {\n /** Pan/zoom so all nodes fit inside the viewport with optional padding. */\n fitToContent(padding?: number): void;\n /** Pan/zoom so all *selected* nodes fit. No-op when selection is empty. */\n fitToSelection(padding?: number): void;\n /** Center the viewport on a specific node. */\n focusNode(id: string): void;\n /** Center on a world point, optionally setting zoom. */\n centerOn(point: Point2D, zoom?: number): void;\n /** Pan/zoom to fit a world rectangle. */\n zoomToRect(rect: WorldRect, padding?: number): void;\n /** Read the current transform. */\n getTransform(): ViewportTransform;\n /** Read the current viewport size in CSS pixels. */\n getSize(): ViewportSize;\n /** Convert a world point to a screen (CSS-pixel) point. */\n worldToScreen(point: Point2D): Point2D;\n /** Convert a screen (CSS-pixel) point to a world point. */\n screenToWorld(point: Point2D): Point2D;\n /**\n * Force a redraw. With no `layerName`, all canvas layers redraw on the\n * next frame; with a name (`'edges' | 'groups' | 'preview'`), only that\n * layer redraws.\n */\n invalidate(layerName?: NodeGraphLayerName): void;\n}\n\nexport type NodeGraphLayerName = 'groups' | 'edges' | 'preview';\n\n// ─── Slot markers ───\n\n/**\n * Symbol attached to `<NodeGraph.Minimap>` and other slot subcomponents so\n * the parent `<NodeGraph>` can identify them without relying on\n * `displayName` (which gets clobbered by `React.memo` / minifiers).\n */\nexport const NODE_GRAPH_SLOT: unique symbol = Symbol.for('etui.nodegraph.slot');\n\nexport type NodeGraphSlotKind =\n | 'minimap'\n | 'background'\n | 'toolbar'\n | 'spawn-palette';\n\n/**\n * Template registered with `<NodeGraph.SpawnPalette>` — describes a\n * spawn-able node + how to build it at a given world point. The\n * library handles search / filtering / focus / selection of templates\n * in the popover; this is the data slice consumer cares about.\n */\nexport interface NodeGraphTemplate {\n /** Stable identifier (used for keys + the palette's recent list). */\n id: string;\n /** Human-readable title shown in the palette. */\n title: string;\n /** Optional secondary line. */\n subtitle?: string;\n /** Group label used to section the palette list. */\n group?: string;\n /** Extra strings the fuzzy matcher considers (synonyms, tags). */\n keywords?: ReadonlyArray<string>;\n /** Optional leading icon. */\n icon?: React.ReactNode;\n /**\n * Build the node body from a world position. The library assigns\n * the final `id` (so consumer doesn't have to roll their own\n * uniqueness scheme) — return everything except `id`.\n */\n build: (worldPoint: Point2D) => Omit<NodeGraphNode, 'id'>;\n}\n\nexport interface NodeGraphSpawnContext {\n /** World point where the user invoked the palette. */\n worldPoint: Point2D;\n /** Screen-space point (CSS pixels relative to viewport top-left). */\n screenPoint: Point2D;\n}\n\nexport interface NodeGraphSpawnPaletteSlotProps {\n /** All spawn-able templates. Filtering happens inside the library. */\n templates: ReadonlyArray<NodeGraphTemplate>;\n /**\n * Called when the user picks a template. Library has already\n * assigned an `id`; consumer typically appends to their nodes state.\n */\n onSpawn: (node: NodeGraphNode, ctx: NodeGraphSpawnContext) => void;\n /** Placeholder for the search input. */\n placeholder?: string;\n /** localStorage key for the palette's recent list. */\n recentKey?: string;\n /** Popover width. @default 400 */\n width?: number | string;\n /** Popover max height. @default 360 */\n maxHeight?: number;\n}\n\nexport interface NodeGraphToolbarSlotProps {\n /**\n * Anchored corner inside the graph. Defaults to top-left.\n */\n placement?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n /** Distance from the edge, CSS px. @default 12 */\n margin?: number;\n /** Gap between toolbar buttons / children, CSS px. @default 4 */\n gap?: number;\n /** Optional className applied to the toolbar wrapper. */\n className?: string;\n /** Inline style override. */\n style?: React.CSSProperties;\n /** Toolbar contents — typically `<NodeGraph.FitContentButton>` etc. */\n children?: React.ReactNode;\n}\n\nexport interface NodeGraphSlotMarker {\n [NODE_GRAPH_SLOT]: NodeGraphSlotKind;\n}\n\n// ─── Props ───\n\nexport interface NodeGraphBaseProps extends Omit<\n BaseComponent,\n 'onChange' | 'onContextMenu' | 'ref'\n> {\n // ── Nodes (controlled / uncontrolled) ──\n nodes?: NodeGraphNode[];\n defaultNodes?: NodeGraphNode[];\n onNodesChange?: (nodes: NodeGraphNode[]) => void;\n\n // ── Edges (controlled / uncontrolled) ──\n edges?: NodeGraphEdge[];\n defaultEdges?: NodeGraphEdge[];\n onEdgesChange?: (edges: NodeGraphEdge[]) => void;\n\n // ── Groups (controlled / uncontrolled) ──\n groups?: NodeGraphGroup[];\n defaultGroups?: NodeGraphGroup[];\n onGroupsChange?: (groups: NodeGraphGroup[]) => void;\n\n // ── Selection (controlled / uncontrolled) ──\n selection?: NodeGraphSelection;\n defaultSelection?: NodeGraphSelection;\n onSelectionChange?: (selection: NodeGraphSelection) => void;\n\n // ── Rendering ──\n /**\n * Render the body of a node. The returned content sits inside a wrapper\n * `<div>` positioned in world space — do not set position/transform\n * yourself. Declare connection endpoints by placing `<NodeGraph.Port>`\n * children anywhere inside the returned tree; the library tracks their\n * DOM positions automatically and routes edges through them.\n */\n renderNode?: (\n node: NodeGraphNode,\n ctx: NodeGraphRenderCtx\n ) => React.ReactNode;\n /**\n * Render a label above the midpoint of an edge. Receives the edge data.\n * When omitted, `edge.label` is used as-is (if defined).\n */\n renderEdgeLabel?: (edge: NodeGraphEdge) => React.ReactNode;\n /**\n * Per-edge style hook called on every draw frame. The returned shape\n * is shallow-merged into the default edge style; any field left\n * undefined falls back to `edge.color` / theme defaults.\n *\n * The second argument carries resolved port metadata from the store\n * so consumers don't need to maintain a parallel index just to colour\n * wires by their source pin's `dataType`.\n *\n * @example\n * ```tsx\n * <NodeGraph\n * edgeStyle={(edge, ctx) => ({\n * color: TYPE_COLOR[ctx.sourcePort?.dataType ?? 'any'],\n * width: ctx.selected ? 3 : 1.5,\n * })}\n * />\n * ```\n */\n edgeStyle?: NodeGraphEdgeStyleFn;\n\n // ── Interaction ──\n /**\n * Validate a connection between two ports. Return `false` to reject the\n * drop; the preview edge is still drawn while the user is hovering, but\n * styled as invalid.\n */\n isValidConnection?: (\n source: NodeGraphPortRef,\n target: NodeGraphPortRef,\n info: NodeGraphConnectionValidationInfo\n ) => boolean;\n /**\n * Snap node positions to a grid of `snapToGrid` world units. Pass `false`\n * to disable snapping. Affects drag (mouse), not programmatic position\n * updates.\n * @default false\n */\n snapToGrid?: number | false;\n /** Fired when a connection drag starts (pointer-down on a port). */\n onConnectStart?: (info: NodeGraphConnectStartInfo) => void;\n /** Fired when a connection drag ends (drop on a port or cancellation). */\n onConnectEnd?: (info: NodeGraphConnectEndInfo) => void;\n /**\n * Fired on a context menu (right-click) over any part of the graph.\n * Includes a discriminated target so the consumer can render contextual\n * menus for nodes / edges / ports / groups / empty background.\n */\n onContextMenu?: (info: NodeGraphContextMenuInfo) => void;\n /** Fired when Delete/Backspace is pressed with a non-empty selection. */\n onDelete?: (selection: NodeGraphSelection) => void;\n /** Fired when Enter is pressed with a single focused node. */\n onActivate?: (node: NodeGraphNode) => void;\n\n // ── Viewport pass-through ──\n /**\n * Pan gesture configuration, or `false` to disable. Defaults match\n * `<Viewport>`: middle-mouse drag + space-key + left-drag.\n */\n pan?: ViewportPanConfig | false;\n /** Zoom gesture configuration, or `false` to disable. */\n zoom?: ViewportZoomConfig | false;\n /** @default 0.1 */\n minZoom?: number;\n /** @default 4 */\n maxZoom?: number;\n /**\n * Enable marquee selection rectangle. When true, dragging on empty\n * background draws a marquee that selects intersecting nodes.\n * @default true\n */\n selectionRect?: boolean;\n /**\n * Track parent size via `ResizeObserver`. Identical to `<Viewport>`'s\n * `responsive`. @default false\n */\n responsive?: boolean;\n /** Fixed height when not responsive. @default 480 */\n height?: number;\n /**\n * Fallback size used when neither `node.width`/`height` nor the measured\n * DOM size is yet available — for the brief window between mount and\n * first ResizeObserver tick, and for the imperative `fitToContent` /\n * minimap reads at first paint.\n * @default { width: 180, height: 80 }\n */\n defaultNodeSize?: { width: number; height: number };\n /**\n * Disable all interaction and dim the surface.\n * @default false\n */\n disabled?: boolean;\n\n // ── Accessibility ──\n /** @default 'Node graph' */\n ariaLabel?: string;\n\n /** Slot children: `<NodeGraph.Minimap>`, `<NodeGraph.Background>`. */\n children?: React.ReactNode;\n\n /** Imperative handle. */\n ref?: React.Ref<NodeGraphHandle>;\n}\n\nexport type NodeGraphProps = Prettify<NodeGraphBaseProps>;\n\n// ─── Subcomponent props ───\n\n/**\n * Per-node colouring for the minimap. Return `{ color }` for a flat tinted\n * rect, or add `headerColor` for a two-tone \"header strip + body\" mini-node\n * that echoes the real node's look at a glance.\n */\nexport interface NodeGraphMinimapNodeStyle {\n /** Body fill colour. Falls back to the minimap's default item colour. */\n color?: string;\n /** When set, a header strip is drawn on top in this colour (two-tone). */\n headerColor?: string;\n}\n\nexport interface NodeGraphMinimapSlotProps {\n /**\n * Anchored position inside the graph. Matches `ViewportMinimap`'s\n * `placement` (string preset or custom anchor object).\n * @default 'bottom-right'\n */\n placement?:\n | 'top-left'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-right'\n | {\n top?: number | string;\n right?: number | string;\n bottom?: number | string;\n left?: number | string;\n width?: number | string;\n height?: number | string;\n };\n /** Distance from the edge for preset placements (CSS px). @default 12 */\n margin?: number;\n /** Minimap width in CSS pixels. @default 200 */\n width?: number;\n /** Optional title (rendered as `<Minimap.Title>`). */\n title?: React.ReactNode;\n /** Optional className applied to the minimap wrapper. */\n className?: string;\n /**\n * CSS colour used to highlight selected nodes inside the minimap. When\n * omitted, selected nodes use the same colour as unselected ones.\n */\n selectedColor?: string;\n /**\n * Per-node colouring. Return `{ color }` to tint the rect, or\n * `{ color, headerColor }` to draw a two-tone header/body mini-node that\n * mirrors the real node. When omitted, every node uses the default colour\n * (or `selectedColor` while selected).\n */\n nodeStyle?: (node: NodeGraphNode) => NodeGraphMinimapNodeStyle | undefined;\n}\n\nexport interface NodeGraphBackgroundSlotProps {\n /**\n * Background style. `'dots'` and `'grid'` are built in; pass `'none'`\n * for an opaque background only.\n * @default 'dots'\n */\n variant?: 'dots' | 'grid' | 'none';\n /**\n * Grid spacing in world units. The pattern repeats at this interval\n * regardless of zoom.\n * @default 24\n */\n gap?: number;\n /** Optional dot/line colour. Defaults to theme `border.default`. */\n color?: string;\n /** Optional background colour. Defaults to theme `background.primary`. */\n background?: string;\n}\n"],"names":[],"mappings":"AAwUA;AAEA;;;;AAIG;AACI,MAAM,eAAe,GAAkB,MAAM,CAAC,GAAG,CAAC,qBAAqB;;;;"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useContext, createContext, useSyncExternalStore } from 'react';
|
|
3
|
+
|
|
4
|
+
const NodeGraphStoreContext =
|
|
5
|
+
/*#__PURE__*/ createContext(null);
|
|
6
|
+
function useStoreInternal() {
|
|
7
|
+
const store = useContext(NodeGraphStoreContext);
|
|
8
|
+
if (!store) {
|
|
9
|
+
throw new Error('useNodeGraph* hooks must be called inside a <NodeGraph> component');
|
|
10
|
+
}
|
|
11
|
+
return store;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Access the underlying `NodeGraphStore` instance — escape hatch for
|
|
15
|
+
* subscribing to custom slice combinations or for imperative reads from
|
|
16
|
+
* non-React code paths (gesture handlers, canvas draw frames).
|
|
17
|
+
*/
|
|
18
|
+
function useNodeGraphStore() {
|
|
19
|
+
return useStoreInternal();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to the data slice — re-renders when the graph contents
|
|
23
|
+
* (`nodes`, `edges`, `groups`, default size) change.
|
|
24
|
+
*/
|
|
25
|
+
function useNodeGraphData() {
|
|
26
|
+
const store = useStoreInternal();
|
|
27
|
+
return useSyncExternalStore(store.subscribeData, store.getData);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to the selection slice — re-renders when the selection changes.
|
|
31
|
+
*/
|
|
32
|
+
function useNodeGraphSelection() {
|
|
33
|
+
const store = useStoreInternal();
|
|
34
|
+
return useSyncExternalStore(store.subscribeSelection, store.getSelection);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Subscribe to the interaction slice — re-renders on gesture lifecycle
|
|
38
|
+
* (drag, connect, marquee) and on per-frame deltas during a drag.
|
|
39
|
+
*/
|
|
40
|
+
function useNodeGraphInteraction() {
|
|
41
|
+
const store = useStoreInternal();
|
|
42
|
+
return useSyncExternalStore(store.subscribeInteraction, store.getInteraction);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe to the hover slice — re-renders when the hovered node / edge /
|
|
46
|
+
* port changes.
|
|
47
|
+
*/
|
|
48
|
+
function useNodeGraphHover() {
|
|
49
|
+
const store = useStoreInternal();
|
|
50
|
+
return useSyncExternalStore(store.subscribeHover, store.getHover);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { NodeGraphStoreContext, useNodeGraphData, useNodeGraphHover, useNodeGraphInteraction, useNodeGraphSelection, useNodeGraphStore };
|
|
54
|
+
//# sourceMappingURL=NodeGraphContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeGraphContext.js","sources":["src/components/editor/NodeGraph/NodeGraphContext.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, useSyncExternalStore } from 'react';\n\nimport type { NodeGraphSelection } from './NodeGraph.types';\nimport type {\n NodeGraphDataState,\n NodeGraphHoverState,\n NodeGraphInteractionState,\n NodeGraphStore,\n} from './NodeGraphStore';\n\nexport const NodeGraphStoreContext =\n /*#__PURE__*/ createContext<NodeGraphStore | null>(null);\n\nfunction useStoreInternal(): NodeGraphStore {\n const store = useContext(NodeGraphStoreContext);\n if (!store) {\n throw new Error(\n 'useNodeGraph* hooks must be called inside a <NodeGraph> component'\n );\n }\n return store;\n}\n\n/**\n * Access the underlying `NodeGraphStore` instance — escape hatch for\n * subscribing to custom slice combinations or for imperative reads from\n * non-React code paths (gesture handlers, canvas draw frames).\n */\nexport function useNodeGraphStore(): NodeGraphStore {\n return useStoreInternal();\n}\n\n/**\n * Subscribe to the data slice — re-renders when the graph contents\n * (`nodes`, `edges`, `groups`, default size) change.\n */\nexport function useNodeGraphData(): NodeGraphDataState {\n const store = useStoreInternal();\n return useSyncExternalStore(store.subscribeData, store.getData);\n}\n\n/**\n * Subscribe to the selection slice — re-renders when the selection changes.\n */\nexport function useNodeGraphSelection(): NodeGraphSelection {\n const store = useStoreInternal();\n return useSyncExternalStore(store.subscribeSelection, store.getSelection);\n}\n\n/**\n * Subscribe to the interaction slice — re-renders on gesture lifecycle\n * (drag, connect, marquee) and on per-frame deltas during a drag.\n */\nexport function useNodeGraphInteraction(): NodeGraphInteractionState {\n const store = useStoreInternal();\n return useSyncExternalStore(store.subscribeInteraction, store.getInteraction);\n}\n\n/**\n * Subscribe to the hover slice — re-renders when the hovered node / edge /\n * port changes.\n */\nexport function useNodeGraphHover(): NodeGraphHoverState {\n const store = useStoreInternal();\n return useSyncExternalStore(store.subscribeHover, store.getHover);\n}\n"],"names":[],"mappings":";;;;AAaE;AAEF;AACE;;AAEE;;AAIF;AACF;AAEA;;;;AAIG;;;AAGH;AAEA;;;AAGG;;AAED;;AAEF;AAEA;;AAEG;;AAED;;AAEF;AAEA;;;AAGG;;AAED;;AAEF;AAEA;;;AAGG;;AAED;;AAEF;;"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { useSyncExternalStore, useCallback } from 'react';
|
|
4
|
+
import { useNodeGraphStore } from './NodeGraphContext.js';
|
|
5
|
+
import { resolvePortRef } from './nodeGraphMath.js';
|
|
6
|
+
import { useStoreSlice } from './useStoreSlice.js';
|
|
7
|
+
|
|
8
|
+
// Shared sentinel for "this edge isn't affected by the current gesture" —
|
|
9
|
+
// one reference so the equality check below short-circuits cleanly.
|
|
10
|
+
const NO_EDGE_DELTAS = { src: null, tgt: null };
|
|
11
|
+
function pointEqualNullable(a, b) {
|
|
12
|
+
if (a === b)
|
|
13
|
+
return true;
|
|
14
|
+
if (a === null || b === null)
|
|
15
|
+
return false;
|
|
16
|
+
return a.x === b.x && a.y === b.y;
|
|
17
|
+
}
|
|
18
|
+
function edgeDeltasEqual(a, b) {
|
|
19
|
+
return pointEqualNullable(a.src, b.src) && pointEqualNullable(a.tgt, b.tgt);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Live drag delta for an edge's endpoints, or {@link NO_EDGE_DELTAS} when
|
|
23
|
+
* neither endpoint's node is part of the current drag. Mirrors the per-node
|
|
24
|
+
* selector in `NodeGraphNode` so a label only updates when an endpoint it
|
|
25
|
+
* actually owns is moving.
|
|
26
|
+
*/
|
|
27
|
+
function selectEdgeDeltas(interaction, sourceNode, targetNode) {
|
|
28
|
+
let ids = null;
|
|
29
|
+
let delta = null;
|
|
30
|
+
if (interaction.kind === 'drag-nodes') {
|
|
31
|
+
ids = interaction.nodeIds;
|
|
32
|
+
delta = interaction.delta;
|
|
33
|
+
}
|
|
34
|
+
else if (interaction.kind === 'drag-groups') {
|
|
35
|
+
ids = interaction.containedNodeIds;
|
|
36
|
+
delta = interaction.delta;
|
|
37
|
+
}
|
|
38
|
+
if (!ids || !delta)
|
|
39
|
+
return NO_EDGE_DELTAS;
|
|
40
|
+
const src = ids.has(sourceNode) ? delta : null;
|
|
41
|
+
const tgt = ids.has(targetNode) ? delta : null;
|
|
42
|
+
if (src === null && tgt === null)
|
|
43
|
+
return NO_EDGE_DELTAS;
|
|
44
|
+
return { src, tgt };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Mounts one `<EdgeLabel>` per edge as a world-space sibling inside
|
|
48
|
+
* `<ViewportWorld>`. Each label subscribes to the interaction slice on
|
|
49
|
+
* its own so a drag of an unrelated node doesn't re-render every label
|
|
50
|
+
* in the graph — only the labels whose endpoints actually move.
|
|
51
|
+
*
|
|
52
|
+
* One subscriber per edge: each `EdgeLabel` subscribes to the `interaction`
|
|
53
|
+
* slice through `useStoreSlice` with an endpoint-delta selector + equality,
|
|
54
|
+
* so it re-renders only when an endpoint it actually owns moves. Labels on
|
|
55
|
+
* edges untouched by the current gesture short-circuit on the equality check
|
|
56
|
+
* and skip the per-frame re-render entirely.
|
|
57
|
+
*/
|
|
58
|
+
function EdgeLabelsLayer({ renderEdgeLabelRef, hasRenderEdgeLabel, }) {
|
|
59
|
+
const store = useNodeGraphStore();
|
|
60
|
+
// Subscribe to data + port positions at this level so we know which
|
|
61
|
+
// edges exist. Each `EdgeLabel` below subscribes to interaction on its
|
|
62
|
+
// own for live drag tracking.
|
|
63
|
+
const data = useSyncExternalStore(store.subscribeData, store.getData);
|
|
64
|
+
useSyncExternalStore(store.subscribePortPositions, store.getPortPositionsVersion);
|
|
65
|
+
return (jsx(Fragment, { children: data.edges.map(edge => {
|
|
66
|
+
// Skip mounting an `EdgeLabel` subscriber when there's nothing it
|
|
67
|
+
// could ever render. When `renderEdgeLabel` is provided the
|
|
68
|
+
// consumer controls visibility per edge from inside their callback
|
|
69
|
+
// (and may return `null`), so we still have to mount — but if no
|
|
70
|
+
// callback exists, the only thing the label could show is
|
|
71
|
+
// `edge.label`, and a `null` value there means there's nothing to
|
|
72
|
+
// draw and no reason to pay for a per-edge store subscription.
|
|
73
|
+
if (!hasRenderEdgeLabel && edge.label == null)
|
|
74
|
+
return null;
|
|
75
|
+
return (jsx(EdgeLabel, { edge: edge, renderEdgeLabelRef: renderEdgeLabelRef }, edge.id));
|
|
76
|
+
}) }));
|
|
77
|
+
}
|
|
78
|
+
function EdgeLabel({ edge, renderEdgeLabelRef, }) {
|
|
79
|
+
const store = useNodeGraphStore();
|
|
80
|
+
const sourceNode = edge.source.node;
|
|
81
|
+
const targetNode = edge.target.node;
|
|
82
|
+
// Subscribe to interaction changes affecting either endpoint via the
|
|
83
|
+
// per-node channel — a drag of an unrelated node never wakes this
|
|
84
|
+
// subscriber, even before the selector runs. When source and target
|
|
85
|
+
// sit on the same node, a single subscription is enough (deduplicates
|
|
86
|
+
// the notification).
|
|
87
|
+
const subscribeEndpoints = useCallback((cb) => {
|
|
88
|
+
const unsubSource = store.subscribeNodeInteraction(sourceNode, cb);
|
|
89
|
+
if (sourceNode === targetNode)
|
|
90
|
+
return unsubSource;
|
|
91
|
+
const unsubTarget = store.subscribeNodeInteraction(targetNode, cb);
|
|
92
|
+
return () => {
|
|
93
|
+
unsubSource();
|
|
94
|
+
unsubTarget();
|
|
95
|
+
};
|
|
96
|
+
}, [store, sourceNode, targetNode]);
|
|
97
|
+
// Selector still reads the global snapshot to compute per-endpoint
|
|
98
|
+
// deltas — the subscription change above only narrows *when* this
|
|
99
|
+
// subscriber gets woken, not *what* it sees.
|
|
100
|
+
const { src: srcDelta, tgt: tgtDelta } = useStoreSlice(subscribeEndpoints, store.getInteraction, interaction => selectEdgeDeltas(interaction, sourceNode, targetNode), edgeDeltasEqual);
|
|
101
|
+
// Re-render when port positions update too (initial measure + body
|
|
102
|
+
// re-layout shift the anchor).
|
|
103
|
+
useSyncExternalStore(store.subscribePortPositions, store.getPortPositionsVersion);
|
|
104
|
+
const src = resolvePortRef(edge.source, store.getNodeById, store.getPortPosition);
|
|
105
|
+
const tgt = resolvePortRef(edge.target, store.getNodeById, store.getPortPosition);
|
|
106
|
+
if (!src || !tgt)
|
|
107
|
+
return null;
|
|
108
|
+
const sx = src.position.x + (srcDelta?.x ?? 0);
|
|
109
|
+
const sy = src.position.y + (srcDelta?.y ?? 0);
|
|
110
|
+
const tx = tgt.position.x + (tgtDelta?.x ?? 0);
|
|
111
|
+
const ty = tgt.position.y + (tgtDelta?.y ?? 0);
|
|
112
|
+
const midX = (sx + tx) / 2;
|
|
113
|
+
const midY = (sy + ty) / 2;
|
|
114
|
+
// The render callback lives in a `useLatest` ref so swapping it doesn't
|
|
115
|
+
// re-render every edge label; reading the current value here is deliberate.
|
|
116
|
+
// Labels refresh on the next data / interaction tick, which is the only
|
|
117
|
+
// moment their content is consumed anyway.
|
|
118
|
+
// eslint-disable-next-line react-hooks/refs -- intentional latest-callback read
|
|
119
|
+
const render = renderEdgeLabelRef.current;
|
|
120
|
+
const content = render ? render(edge) : (edge.label ?? null);
|
|
121
|
+
if (content == null || content === false)
|
|
122
|
+
return null;
|
|
123
|
+
return (jsx("div", { style: {
|
|
124
|
+
position: 'absolute',
|
|
125
|
+
left: 0,
|
|
126
|
+
top: 0,
|
|
127
|
+
// World-space position via `transform` so each frame's update is
|
|
128
|
+
// a GPU-friendly transform mutation (no CSS layout). The
|
|
129
|
+
// -50%/-50% piece centres the label on its midpoint.
|
|
130
|
+
transform: `translate3d(${midX}px, ${midY}px, 0) translate(-50%, -50%)`,
|
|
131
|
+
pointerEvents: 'auto',
|
|
132
|
+
willChange: 'transform',
|
|
133
|
+
}, children: content }));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export { EdgeLabelsLayer };
|
|
137
|
+
//# sourceMappingURL=NodeGraphEdgeLabels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeGraphEdgeLabels.js","sources":["src/components/editor/NodeGraph/NodeGraphEdgeLabels.tsx"],"sourcesContent":["'use client';\n\nimport React, { useCallback, useSyncExternalStore } from 'react';\n\nimport { useNodeGraphStore } from './NodeGraphContext';\nimport { resolvePortRef } from './nodeGraphMath';\nimport { useStoreSlice } from './useStoreSlice';\n\nimport type { NodeGraphEdge } from './NodeGraph.types';\nimport type { NodeGraphInteractionState } from './NodeGraphStore';\nimport type { Point2D } from '@/components/primitives/canvas/canvas.types';\n\ninterface RenderEdgeLabelRef {\n current: ((edge: NodeGraphEdge) => React.ReactNode) | undefined;\n}\n\n/** Live drag deltas for an edge's two endpoints during the current gesture. */\ninterface EdgeEndpointDeltas {\n src: Point2D | null;\n tgt: Point2D | null;\n}\n\n// Shared sentinel for \"this edge isn't affected by the current gesture\" —\n// one reference so the equality check below short-circuits cleanly.\nconst NO_EDGE_DELTAS: EdgeEndpointDeltas = { src: null, tgt: null };\n\nfunction pointEqualNullable(a: Point2D | null, b: Point2D | null): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n return a.x === b.x && a.y === b.y;\n}\n\nfunction edgeDeltasEqual(\n a: EdgeEndpointDeltas,\n b: EdgeEndpointDeltas\n): boolean {\n return pointEqualNullable(a.src, b.src) && pointEqualNullable(a.tgt, b.tgt);\n}\n\n/**\n * Live drag delta for an edge's endpoints, or {@link NO_EDGE_DELTAS} when\n * neither endpoint's node is part of the current drag. Mirrors the per-node\n * selector in `NodeGraphNode` so a label only updates when an endpoint it\n * actually owns is moving.\n */\nfunction selectEdgeDeltas(\n interaction: NodeGraphInteractionState,\n sourceNode: string,\n targetNode: string\n): EdgeEndpointDeltas {\n let ids: ReadonlySet<string> | null = null;\n let delta: Point2D | null = null;\n if (interaction.kind === 'drag-nodes') {\n ids = interaction.nodeIds;\n delta = interaction.delta;\n } else if (interaction.kind === 'drag-groups') {\n ids = interaction.containedNodeIds;\n delta = interaction.delta;\n }\n if (!ids || !delta) return NO_EDGE_DELTAS;\n const src = ids.has(sourceNode) ? delta : null;\n const tgt = ids.has(targetNode) ? delta : null;\n if (src === null && tgt === null) return NO_EDGE_DELTAS;\n return { src, tgt };\n}\n\n/**\n * Mounts one `<EdgeLabel>` per edge as a world-space sibling inside\n * `<ViewportWorld>`. Each label subscribes to the interaction slice on\n * its own so a drag of an unrelated node doesn't re-render every label\n * in the graph — only the labels whose endpoints actually move.\n *\n * One subscriber per edge: each `EdgeLabel` subscribes to the `interaction`\n * slice through `useStoreSlice` with an endpoint-delta selector + equality,\n * so it re-renders only when an endpoint it actually owns moves. Labels on\n * edges untouched by the current gesture short-circuit on the equality check\n * and skip the per-frame re-render entirely.\n */\nexport function EdgeLabelsLayer({\n renderEdgeLabelRef,\n hasRenderEdgeLabel,\n}: {\n renderEdgeLabelRef: RenderEdgeLabelRef;\n /**\n * Whether the consumer supplied a `renderEdgeLabel` callback. When `false`,\n * edges without an explicit `edge.label` prop are skipped — no `EdgeLabel`\n * subscriber is mounted for them. This avoids the cost of one interaction-\n * slice subscriber per edge in graphs that don't use labels at all (the\n * benchmark, simple recipes, etc.), which is otherwise pure waste — the\n * subscriber fires on every drag tick and the body returns `null`.\n */\n hasRenderEdgeLabel: boolean;\n}): React.ReactElement | null {\n const store = useNodeGraphStore();\n // Subscribe to data + port positions at this level so we know which\n // edges exist. Each `EdgeLabel` below subscribes to interaction on its\n // own for live drag tracking.\n const data = useSyncExternalStore(store.subscribeData, store.getData);\n useSyncExternalStore(\n store.subscribePortPositions,\n store.getPortPositionsVersion\n );\n\n return (\n <>\n {data.edges.map(edge => {\n // Skip mounting an `EdgeLabel` subscriber when there's nothing it\n // could ever render. When `renderEdgeLabel` is provided the\n // consumer controls visibility per edge from inside their callback\n // (and may return `null`), so we still have to mount — but if no\n // callback exists, the only thing the label could show is\n // `edge.label`, and a `null` value there means there's nothing to\n // draw and no reason to pay for a per-edge store subscription.\n if (!hasRenderEdgeLabel && edge.label == null) return null;\n return (\n <EdgeLabel\n key={edge.id}\n edge={edge}\n renderEdgeLabelRef={renderEdgeLabelRef}\n />\n );\n })}\n </>\n );\n}\n\nfunction EdgeLabel({\n edge,\n renderEdgeLabelRef,\n}: {\n edge: NodeGraphEdge;\n renderEdgeLabelRef: RenderEdgeLabelRef;\n}): React.ReactElement | null {\n const store = useNodeGraphStore();\n const sourceNode = edge.source.node;\n const targetNode = edge.target.node;\n // Subscribe to interaction changes affecting either endpoint via the\n // per-node channel — a drag of an unrelated node never wakes this\n // subscriber, even before the selector runs. When source and target\n // sit on the same node, a single subscription is enough (deduplicates\n // the notification).\n const subscribeEndpoints = useCallback(\n (cb: () => void): (() => void) => {\n const unsubSource = store.subscribeNodeInteraction(sourceNode, cb);\n if (sourceNode === targetNode) return unsubSource;\n const unsubTarget = store.subscribeNodeInteraction(targetNode, cb);\n return () => {\n unsubSource();\n unsubTarget();\n };\n },\n [store, sourceNode, targetNode]\n );\n // Selector still reads the global snapshot to compute per-endpoint\n // deltas — the subscription change above only narrows *when* this\n // subscriber gets woken, not *what* it sees.\n const { src: srcDelta, tgt: tgtDelta } = useStoreSlice<\n NodeGraphInteractionState,\n EdgeEndpointDeltas\n >(\n subscribeEndpoints,\n store.getInteraction,\n interaction => selectEdgeDeltas(interaction, sourceNode, targetNode),\n edgeDeltasEqual\n );\n // Re-render when port positions update too (initial measure + body\n // re-layout shift the anchor).\n useSyncExternalStore(\n store.subscribePortPositions,\n store.getPortPositionsVersion\n );\n\n const src = resolvePortRef(\n edge.source,\n store.getNodeById,\n store.getPortPosition\n );\n const tgt = resolvePortRef(\n edge.target,\n store.getNodeById,\n store.getPortPosition\n );\n if (!src || !tgt) return null;\n\n const sx = src.position.x + (srcDelta?.x ?? 0);\n const sy = src.position.y + (srcDelta?.y ?? 0);\n const tx = tgt.position.x + (tgtDelta?.x ?? 0);\n const ty = tgt.position.y + (tgtDelta?.y ?? 0);\n const midX = (sx + tx) / 2;\n const midY = (sy + ty) / 2;\n // The render callback lives in a `useLatest` ref so swapping it doesn't\n // re-render every edge label; reading the current value here is deliberate.\n // Labels refresh on the next data / interaction tick, which is the only\n // moment their content is consumed anyway.\n // eslint-disable-next-line react-hooks/refs -- intentional latest-callback read\n const render = renderEdgeLabelRef.current;\n const content = render ? render(edge) : (edge.label ?? null);\n if (content == null || content === false) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n // World-space position via `transform` so each frame's update is\n // a GPU-friendly transform mutation (no CSS layout). The\n // -50%/-50% piece centres the label on its midpoint.\n transform: `translate3d(${midX}px, ${midY}px, 0) translate(-50%, -50%)`,\n pointerEvents: 'auto',\n willChange: 'transform',\n }}\n >\n {content}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAsBA;AACA;AACA;AAEA;;AACe;AACb;AAA8B;AAC9B;AACF;AAEA;;AAKA;AAEA;;;;;AAKG;AACH;;;AAOE;AACE;AACA;;AACK;AACL;AACA;;AAEF;AAAoB;AACpB;AACA;AACA;AAAkC;AAClC;AACF;AAEA;;;;;;;;;;;AAWG;;AAgBD;;;;AAIA;;;;;;;;;;AAgBM;AAA+C;AAC/C;;AAUR;AAEA;AAOE;AACA;AACA;;;;;;AAMA;;;AAGmC;;AAE/B;AACE;AACA;AACF;;;;;AAOJ;;;;AAgBA;AAKA;AAKA;AAAkB;AAElB;AACA;AACA;AACA;;;;;;;;AAQA;;AAEA;AAA0C;;AAKpC;AACA;AACA;;;;AAIA;AACA;AACA;;AAMR;;"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|
4
|
+
import { ColorPicker } from '../../controls/ColorPicker/ColorPicker.js';
|
|
5
|
+
import { Popover } from '../../primitives/Popover/Popover.js';
|
|
6
|
+
import { PopoverContent } from '../../primitives/Popover/PopoverContent.js';
|
|
7
|
+
import { PopoverTrigger } from '../../primitives/Popover/PopoverTrigger.js';
|
|
8
|
+
import { cx } from '../../../utils/cx.js';
|
|
9
|
+
import { groupLabelBarStyle, groupLabelInputStyle, groupLabelStyle, groupColorSwatchStyle, groupResizeHandleStyle, groupOverlayRecipe } from './NodeGraph.css.js';
|
|
10
|
+
import { useNodeGraphStore } from './NodeGraphContext.js';
|
|
11
|
+
import { applyGroupResize } from './nodeGraphMath.js';
|
|
12
|
+
import { useStoreSlice } from './useStoreSlice.js';
|
|
13
|
+
|
|
14
|
+
const RESIZE_CURSORS = {
|
|
15
|
+
nw: 'nwse-resize',
|
|
16
|
+
n: 'ns-resize',
|
|
17
|
+
ne: 'nesw-resize',
|
|
18
|
+
e: 'ew-resize',
|
|
19
|
+
se: 'nwse-resize',
|
|
20
|
+
s: 'ns-resize',
|
|
21
|
+
sw: 'nesw-resize',
|
|
22
|
+
w: 'ew-resize',
|
|
23
|
+
};
|
|
24
|
+
const HANDLE_POSITIONS = {
|
|
25
|
+
nw: { left: '-5px', top: '-5px' },
|
|
26
|
+
n: { left: 'calc(50% - 5px)', top: '-5px' },
|
|
27
|
+
ne: { right: '-5px', top: '-5px' },
|
|
28
|
+
e: { right: '-5px', top: 'calc(50% - 5px)' },
|
|
29
|
+
se: { right: '-5px', bottom: '-5px' },
|
|
30
|
+
s: { left: 'calc(50% - 5px)', bottom: '-5px' },
|
|
31
|
+
sw: { left: '-5px', bottom: '-5px' },
|
|
32
|
+
w: { left: '-5px', top: 'calc(50% - 5px)' },
|
|
33
|
+
};
|
|
34
|
+
const ALL_HANDLES = [
|
|
35
|
+
'nw',
|
|
36
|
+
'n',
|
|
37
|
+
'ne',
|
|
38
|
+
'e',
|
|
39
|
+
'se',
|
|
40
|
+
's',
|
|
41
|
+
'sw',
|
|
42
|
+
'w',
|
|
43
|
+
];
|
|
44
|
+
const DEFAULT_PICKER_COLOR = '#5d8dff';
|
|
45
|
+
function gestureStateEqual(a, b) {
|
|
46
|
+
if (a === b)
|
|
47
|
+
return true;
|
|
48
|
+
if (a === null || b === null)
|
|
49
|
+
return false;
|
|
50
|
+
if (a.kind !== b.kind)
|
|
51
|
+
return false;
|
|
52
|
+
if (a.blocked !== b.blocked)
|
|
53
|
+
return false;
|
|
54
|
+
if (a.kind === 'drag' && b.kind === 'drag') {
|
|
55
|
+
return a.delta.x === b.delta.x && a.delta.y === b.delta.y;
|
|
56
|
+
}
|
|
57
|
+
if (a.kind === 'resize' && b.kind === 'resize') {
|
|
58
|
+
return (a.handle === b.handle &&
|
|
59
|
+
a.delta.x === b.delta.x &&
|
|
60
|
+
a.delta.y === b.delta.y &&
|
|
61
|
+
a.startBounds.x === b.startBounds.x &&
|
|
62
|
+
a.startBounds.y === b.startBounds.y &&
|
|
63
|
+
a.startBounds.width === b.startBounds.width &&
|
|
64
|
+
a.startBounds.height === b.startBounds.height);
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
function selectGroupGesture(interaction, groupId) {
|
|
69
|
+
if (interaction.kind === 'drag-groups' && interaction.groupIds.has(groupId)) {
|
|
70
|
+
return {
|
|
71
|
+
kind: 'drag',
|
|
72
|
+
delta: interaction.delta,
|
|
73
|
+
blocked: interaction.blocked,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (interaction.kind === 'resize-group' && interaction.groupId === groupId) {
|
|
77
|
+
return {
|
|
78
|
+
kind: 'resize',
|
|
79
|
+
handle: interaction.handle,
|
|
80
|
+
startBounds: interaction.startBounds,
|
|
81
|
+
delta: interaction.delta,
|
|
82
|
+
blocked: interaction.blocked,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function computeLiveBounds(group, gesture) {
|
|
88
|
+
if (gesture === null)
|
|
89
|
+
return group.bounds;
|
|
90
|
+
if (gesture.kind === 'drag') {
|
|
91
|
+
return {
|
|
92
|
+
x: group.bounds.x + gesture.delta.x,
|
|
93
|
+
y: group.bounds.y + gesture.delta.y,
|
|
94
|
+
width: group.bounds.width,
|
|
95
|
+
height: group.bounds.height,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return applyGroupResize(gesture.startBounds, gesture.handle, gesture.delta);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Render a single interactive group overlay positioned in world space.
|
|
102
|
+
* The canvas layer draws the visual fill underneath — this HTML element
|
|
103
|
+
* handles selection, drag-to-move, corner/edge resize, label editing,
|
|
104
|
+
* and the colour swatch.
|
|
105
|
+
*
|
|
106
|
+
* Uses per-id selectors so a group only re-renders when *its* selection
|
|
107
|
+
* or gesture state changes — drag deltas of other groups never reach the
|
|
108
|
+
* subscriber.
|
|
109
|
+
*
|
|
110
|
+
* Wrapped in `React.memo` for the same reason as
|
|
111
|
+
* {@link NodeGraphNodeView}: a parent render caused by an unrelated
|
|
112
|
+
* controlled-state update would otherwise iterate every group. Handlers
|
|
113
|
+
* passed from `NodeGraphImpl` are all `useCallback`-stable.
|
|
114
|
+
*/
|
|
115
|
+
function NodeGraphGroupViewImpl({ group, onBodyPointerDown, onBodyPointerUp, onHandlePointerDown, onBodyContextMenu, onLabelChange, onColorChange, }) {
|
|
116
|
+
const store = useNodeGraphStore();
|
|
117
|
+
const groupId = group.id;
|
|
118
|
+
const selected = useStoreSlice(store.subscribeSelection, store.getSelection, sel => sel.groups.includes(groupId));
|
|
119
|
+
const gesture = useStoreSlice(store.subscribeInteraction, store.getInteraction, interaction => selectGroupGesture(interaction, groupId), gestureStateEqual);
|
|
120
|
+
const dragging = gesture?.kind === 'drag';
|
|
121
|
+
const blocked = gesture?.blocked ?? false;
|
|
122
|
+
const bounds = computeLiveBounds(group, gesture);
|
|
123
|
+
// ── Inline label edit ──
|
|
124
|
+
// `draft` is initialised from `group.label` on mount and re-synced by
|
|
125
|
+
// `cancelLabel` / `commitLabel` / `handleLabelDoubleClick` on every
|
|
126
|
+
// transition out of / into edit mode. Mirroring `group.label` into
|
|
127
|
+
// state via a side effect would be the classic anti-pattern.
|
|
128
|
+
const [editing, setEditing] = useState(false);
|
|
129
|
+
const [draft, setDraft] = useState(group.label ?? '');
|
|
130
|
+
const inputRef = useRef(null);
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (editing) {
|
|
133
|
+
// Defer focus until the input has been mounted.
|
|
134
|
+
inputRef.current?.focus();
|
|
135
|
+
inputRef.current?.select();
|
|
136
|
+
}
|
|
137
|
+
}, [editing]);
|
|
138
|
+
const commitLabel = useCallback(() => {
|
|
139
|
+
const trimmed = draft.trim();
|
|
140
|
+
setEditing(false);
|
|
141
|
+
if (trimmed === (group.label ?? ''))
|
|
142
|
+
return;
|
|
143
|
+
onLabelChange(group, trimmed);
|
|
144
|
+
}, [draft, group, onLabelChange]);
|
|
145
|
+
const cancelLabel = useCallback(() => {
|
|
146
|
+
setEditing(false);
|
|
147
|
+
setDraft(group.label ?? '');
|
|
148
|
+
}, [group.label]);
|
|
149
|
+
const handleLabelDoubleClick = useCallback((event) => {
|
|
150
|
+
event.stopPropagation();
|
|
151
|
+
// Re-sync from the prop on open — covers the case where `group.label`
|
|
152
|
+
// changed externally while we weren't editing, so the input opens
|
|
153
|
+
// with the current value rather than a stale draft.
|
|
154
|
+
setDraft(group.label ?? '');
|
|
155
|
+
setEditing(true);
|
|
156
|
+
}, [group.label]);
|
|
157
|
+
const handleInputKeyDown = useCallback((event) => {
|
|
158
|
+
if (event.key === 'Enter') {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
commitLabel();
|
|
161
|
+
}
|
|
162
|
+
else if (event.key === 'Escape') {
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
cancelLabel();
|
|
165
|
+
}
|
|
166
|
+
// Stop arrow keys from triggering the graph's nudge handler.
|
|
167
|
+
event.stopPropagation();
|
|
168
|
+
}, [commitLabel, cancelLabel]);
|
|
169
|
+
// ── Colour picker ──
|
|
170
|
+
//
|
|
171
|
+
// The swatch is a `<button>` that triggers the entangle-ui `<ColorPicker>`
|
|
172
|
+
// popover. The picker has `showAlpha` enabled so it returns proper
|
|
173
|
+
// `rgba(...)` strings when the user lowers opacity — the canvas
|
|
174
|
+
// `drawGroups` fill plumbs that straight through with no transform.
|
|
175
|
+
const handleColorChange = useCallback((next) => {
|
|
176
|
+
onColorChange(group, next);
|
|
177
|
+
}, [group, onColorChange]);
|
|
178
|
+
// Picker value falls back to a sensible default when the group hasn't
|
|
179
|
+
// been coloured yet (the library doesn't impose a default).
|
|
180
|
+
const pickerValue = group.color ?? DEFAULT_PICKER_COLOR;
|
|
181
|
+
return (jsxs("div", { className: cx(groupOverlayRecipe({ selected, dragging, blocked })), style: {
|
|
182
|
+
left: bounds.x,
|
|
183
|
+
top: bounds.y,
|
|
184
|
+
width: bounds.width,
|
|
185
|
+
height: bounds.height,
|
|
186
|
+
}, "data-group-id": group.id, "data-blocked": blocked || undefined, onPointerDown: e => {
|
|
187
|
+
onBodyPointerDown(e, group);
|
|
188
|
+
}, onPointerUp: e => {
|
|
189
|
+
onBodyPointerUp(e, group);
|
|
190
|
+
}, onContextMenu: onBodyContextMenu, children: [jsxs("div", { className: groupLabelBarStyle,
|
|
191
|
+
// Don't let pointerdown on the label bar (label text, color swatch,
|
|
192
|
+
// input) fall through to the body drag handler — the label bar is
|
|
193
|
+
// an interactive control row, not a draggable handle.
|
|
194
|
+
onPointerDown: e => {
|
|
195
|
+
e.stopPropagation();
|
|
196
|
+
}, children: [editing ? (jsx("input", { ref: inputRef, className: groupLabelInputStyle, value: draft, placeholder: "Group name", spellCheck: false, onChange: e => {
|
|
197
|
+
setDraft(e.target.value);
|
|
198
|
+
}, onBlur: commitLabel, onKeyDown: handleInputKeyDown })) : (jsx("span", { className: groupLabelStyle, onDoubleClick: handleLabelDoubleClick, title: "Double-click to rename", children: group.label?.trim() ? group.label : 'Group' })), jsxs(Popover, { placement: "bottom-end", children: [jsx(PopoverTrigger, { children: jsx("button", { type: "button", "aria-label": "Change group colour", title: "Change group colour", className: groupColorSwatchStyle, style: { background: group.color ?? DEFAULT_PICKER_COLOR } }) }), jsx(PopoverContent, { padding: "md", children: jsx(ColorPicker, { inline: true, showAlpha: true, defaultValue: pickerValue, format: "rgba", palette: "material", onChangeComplete: handleColorChange }) })] })] }), selected
|
|
199
|
+
? ALL_HANDLES.map(handle => (jsx("div", { className: groupResizeHandleStyle, style: {
|
|
200
|
+
...HANDLE_POSITIONS[handle],
|
|
201
|
+
cursor: RESIZE_CURSORS[handle],
|
|
202
|
+
}, "data-group-id": group.id, "data-group-handle": handle, onPointerDown: e => {
|
|
203
|
+
onHandlePointerDown(e, group, handle);
|
|
204
|
+
} }, handle)))
|
|
205
|
+
: null] }));
|
|
206
|
+
}
|
|
207
|
+
const NodeGraphGroupView = React.memo(NodeGraphGroupViewImpl);
|
|
208
|
+
NodeGraphGroupView.displayName = 'NodeGraphGroupView';
|
|
209
|
+
|
|
210
|
+
export { NodeGraphGroupView };
|
|
211
|
+
//# sourceMappingURL=NodeGraphGroup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeGraphGroup.js","sources":["src/components/editor/NodeGraph/NodeGraphGroup.tsx"],"sourcesContent":["'use client';\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { ColorPicker } from '@/components/controls/ColorPicker/ColorPicker';\nimport { Popover } from '@/components/primitives/Popover/Popover';\nimport { PopoverContent } from '@/components/primitives/Popover/PopoverContent';\nimport { PopoverTrigger } from '@/components/primitives/Popover/PopoverTrigger';\nimport { cx } from '@/utils/cx';\n\nimport {\n groupColorSwatchStyle,\n groupLabelBarStyle,\n groupLabelInputStyle,\n groupLabelStyle,\n groupOverlayRecipe,\n groupResizeHandleStyle,\n} from './NodeGraph.css';\nimport { useNodeGraphStore } from './NodeGraphContext';\nimport { applyGroupResize } from './nodeGraphMath';\nimport { useStoreSlice } from './useStoreSlice';\n\nimport type { NodeGraphGroup } from './NodeGraph.types';\nimport type {\n NodeGraphGroupResizeHandle,\n NodeGraphInteractionState,\n} from './NodeGraphStore';\nimport type { WorldRect } from '@/components/primitives/viewport';\n\ninterface NodeGraphGroupViewProps {\n group: NodeGraphGroup;\n /** Pointer-down on the group body — used for drag / click selection. */\n onBodyPointerDown: (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup\n ) => void;\n /** Pointer-up on the group body — completes click-to-select if no drag. */\n onBodyPointerUp: (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup\n ) => void;\n /** Pointer-down on a resize handle. */\n onHandlePointerDown: (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup,\n handle: NodeGraphGroupResizeHandle\n ) => void;\n onBodyContextMenu: (event: React.MouseEvent<HTMLDivElement>) => void;\n /**\n * Commit a label or colour edit. The library calls this when the user\n * finishes editing the label (Enter / blur) or picks a new colour from\n * the swatch. Parent forwards to `setGroups` / `onGroupsChange`.\n */\n onLabelChange: (group: NodeGraphGroup, nextLabel: string) => void;\n onColorChange: (group: NodeGraphGroup, nextColor: string) => void;\n}\n\nconst RESIZE_CURSORS: Record<NodeGraphGroupResizeHandle, string> = {\n nw: 'nwse-resize',\n n: 'ns-resize',\n ne: 'nesw-resize',\n e: 'ew-resize',\n se: 'nwse-resize',\n s: 'ns-resize',\n sw: 'nesw-resize',\n w: 'ew-resize',\n};\n\nconst HANDLE_POSITIONS: Record<\n NodeGraphGroupResizeHandle,\n { left?: string; right?: string; top?: string; bottom?: string }\n> = {\n nw: { left: '-5px', top: '-5px' },\n n: { left: 'calc(50% - 5px)', top: '-5px' },\n ne: { right: '-5px', top: '-5px' },\n e: { right: '-5px', top: 'calc(50% - 5px)' },\n se: { right: '-5px', bottom: '-5px' },\n s: { left: 'calc(50% - 5px)', bottom: '-5px' },\n sw: { left: '-5px', bottom: '-5px' },\n w: { left: '-5px', top: 'calc(50% - 5px)' },\n};\n\nconst ALL_HANDLES: NodeGraphGroupResizeHandle[] = [\n 'nw',\n 'n',\n 'ne',\n 'e',\n 'se',\n 's',\n 'sw',\n 'w',\n];\n\nconst DEFAULT_PICKER_COLOR = '#5d8dff';\n\n/**\n * Per-group slice of the interaction state — `null` for groups that\n * aren't currently being dragged or resized. Two distinct `null`\n * references would defeat the equality short-circuit, so we share the\n * module-level constant below.\n */\ntype GroupGestureState =\n | {\n kind: 'drag';\n delta: { x: number; y: number };\n blocked: boolean;\n }\n | {\n kind: 'resize';\n handle: NodeGraphGroupResizeHandle;\n startBounds: WorldRect;\n delta: { x: number; y: number };\n blocked: boolean;\n }\n | null;\n\nfunction gestureStateEqual(\n a: GroupGestureState,\n b: GroupGestureState\n): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if (a.kind !== b.kind) return false;\n if (a.blocked !== b.blocked) return false;\n if (a.kind === 'drag' && b.kind === 'drag') {\n return a.delta.x === b.delta.x && a.delta.y === b.delta.y;\n }\n if (a.kind === 'resize' && b.kind === 'resize') {\n return (\n a.handle === b.handle &&\n a.delta.x === b.delta.x &&\n a.delta.y === b.delta.y &&\n a.startBounds.x === b.startBounds.x &&\n a.startBounds.y === b.startBounds.y &&\n a.startBounds.width === b.startBounds.width &&\n a.startBounds.height === b.startBounds.height\n );\n }\n return false;\n}\n\nfunction selectGroupGesture(\n interaction: NodeGraphInteractionState,\n groupId: string\n): GroupGestureState {\n if (interaction.kind === 'drag-groups' && interaction.groupIds.has(groupId)) {\n return {\n kind: 'drag',\n delta: interaction.delta,\n blocked: interaction.blocked,\n };\n }\n if (interaction.kind === 'resize-group' && interaction.groupId === groupId) {\n return {\n kind: 'resize',\n handle: interaction.handle,\n startBounds: interaction.startBounds,\n delta: interaction.delta,\n blocked: interaction.blocked,\n };\n }\n return null;\n}\n\nfunction computeLiveBounds(\n group: NodeGraphGroup,\n gesture: GroupGestureState\n): WorldRect {\n if (gesture === null) return group.bounds;\n if (gesture.kind === 'drag') {\n return {\n x: group.bounds.x + gesture.delta.x,\n y: group.bounds.y + gesture.delta.y,\n width: group.bounds.width,\n height: group.bounds.height,\n };\n }\n return applyGroupResize(gesture.startBounds, gesture.handle, gesture.delta);\n}\n\n/**\n * Render a single interactive group overlay positioned in world space.\n * The canvas layer draws the visual fill underneath — this HTML element\n * handles selection, drag-to-move, corner/edge resize, label editing,\n * and the colour swatch.\n *\n * Uses per-id selectors so a group only re-renders when *its* selection\n * or gesture state changes — drag deltas of other groups never reach the\n * subscriber.\n *\n * Wrapped in `React.memo` for the same reason as\n * {@link NodeGraphNodeView}: a parent render caused by an unrelated\n * controlled-state update would otherwise iterate every group. Handlers\n * passed from `NodeGraphImpl` are all `useCallback`-stable.\n */\nfunction NodeGraphGroupViewImpl({\n group,\n onBodyPointerDown,\n onBodyPointerUp,\n onHandlePointerDown,\n onBodyContextMenu,\n onLabelChange,\n onColorChange,\n}: NodeGraphGroupViewProps): React.ReactElement {\n const store = useNodeGraphStore();\n const groupId = group.id;\n\n const selected = useStoreSlice(\n store.subscribeSelection,\n store.getSelection,\n sel => sel.groups.includes(groupId)\n );\n\n const gesture = useStoreSlice<NodeGraphInteractionState, GroupGestureState>(\n store.subscribeInteraction,\n store.getInteraction,\n interaction => selectGroupGesture(interaction, groupId),\n gestureStateEqual\n );\n\n const dragging = gesture?.kind === 'drag';\n const blocked = gesture?.blocked ?? false;\n const bounds = computeLiveBounds(group, gesture);\n\n // ── Inline label edit ──\n // `draft` is initialised from `group.label` on mount and re-synced by\n // `cancelLabel` / `commitLabel` / `handleLabelDoubleClick` on every\n // transition out of / into edit mode. Mirroring `group.label` into\n // state via a side effect would be the classic anti-pattern.\n const [editing, setEditing] = useState(false);\n const [draft, setDraft] = useState(group.label ?? '');\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (editing) {\n // Defer focus until the input has been mounted.\n inputRef.current?.focus();\n inputRef.current?.select();\n }\n }, [editing]);\n\n const commitLabel = useCallback((): void => {\n const trimmed = draft.trim();\n setEditing(false);\n if (trimmed === (group.label ?? '')) return;\n onLabelChange(group, trimmed);\n }, [draft, group, onLabelChange]);\n\n const cancelLabel = useCallback((): void => {\n setEditing(false);\n setDraft(group.label ?? '');\n }, [group.label]);\n\n const handleLabelDoubleClick = useCallback(\n (event: React.MouseEvent<HTMLSpanElement>): void => {\n event.stopPropagation();\n // Re-sync from the prop on open — covers the case where `group.label`\n // changed externally while we weren't editing, so the input opens\n // with the current value rather than a stale draft.\n setDraft(group.label ?? '');\n setEditing(true);\n },\n [group.label]\n );\n\n const handleInputKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === 'Enter') {\n event.preventDefault();\n commitLabel();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n cancelLabel();\n }\n // Stop arrow keys from triggering the graph's nudge handler.\n event.stopPropagation();\n },\n [commitLabel, cancelLabel]\n );\n\n // ── Colour picker ──\n //\n // The swatch is a `<button>` that triggers the entangle-ui `<ColorPicker>`\n // popover. The picker has `showAlpha` enabled so it returns proper\n // `rgba(...)` strings when the user lowers opacity — the canvas\n // `drawGroups` fill plumbs that straight through with no transform.\n const handleColorChange = useCallback(\n (next: string): void => {\n onColorChange(group, next);\n },\n [group, onColorChange]\n );\n\n // Picker value falls back to a sensible default when the group hasn't\n // been coloured yet (the library doesn't impose a default).\n const pickerValue = group.color ?? DEFAULT_PICKER_COLOR;\n\n return (\n <div\n className={cx(groupOverlayRecipe({ selected, dragging, blocked }))}\n style={{\n left: bounds.x,\n top: bounds.y,\n width: bounds.width,\n height: bounds.height,\n }}\n data-group-id={group.id}\n data-blocked={blocked || undefined}\n onPointerDown={e => {\n onBodyPointerDown(e, group);\n }}\n onPointerUp={e => {\n onBodyPointerUp(e, group);\n }}\n onContextMenu={onBodyContextMenu}\n >\n <div\n className={groupLabelBarStyle}\n // Don't let pointerdown on the label bar (label text, color swatch,\n // input) fall through to the body drag handler — the label bar is\n // an interactive control row, not a draggable handle.\n onPointerDown={e => {\n e.stopPropagation();\n }}\n >\n {editing ? (\n <input\n ref={inputRef}\n className={groupLabelInputStyle}\n value={draft}\n placeholder=\"Group name\"\n spellCheck={false}\n onChange={e => {\n setDraft(e.target.value);\n }}\n onBlur={commitLabel}\n onKeyDown={handleInputKeyDown}\n />\n ) : (\n <span\n className={groupLabelStyle}\n onDoubleClick={handleLabelDoubleClick}\n title=\"Double-click to rename\"\n >\n {group.label?.trim() ? group.label : 'Group'}\n </span>\n )}\n <Popover placement=\"bottom-end\">\n <PopoverTrigger>\n <button\n type=\"button\"\n aria-label=\"Change group colour\"\n title=\"Change group colour\"\n className={groupColorSwatchStyle}\n style={{ background: group.color ?? DEFAULT_PICKER_COLOR }}\n />\n </PopoverTrigger>\n <PopoverContent padding=\"md\">\n {/* Uncontrolled picker — `useColor` only mutates its internal\n state when `value` is undefined, so passing `defaultValue`\n lets the user actually drag the sliders / click the\n palette. PopoverContent unmounts on close, so each open\n gets a fresh mount that picks up the current\n `group.color` via `defaultValue`. `showAlpha` lets the\n picker emit translucent fills directly — we ask for\n `format=\"rgba\"` so the alpha channel is actually included\n in the output string (`\"rgb\"` strips it). Survives\n round-trip through the canvas group fill with no\n consumer-side transform. */}\n <ColorPicker\n inline\n showAlpha\n defaultValue={pickerValue}\n format=\"rgba\"\n palette=\"material\"\n onChangeComplete={handleColorChange}\n />\n </PopoverContent>\n </Popover>\n </div>\n {selected\n ? ALL_HANDLES.map(handle => (\n <div\n key={handle}\n className={groupResizeHandleStyle}\n style={{\n ...HANDLE_POSITIONS[handle],\n cursor: RESIZE_CURSORS[handle],\n }}\n data-group-id={group.id}\n data-group-handle={handle}\n onPointerDown={e => {\n onHandlePointerDown(e, group, handle);\n }}\n />\n ))\n : null}\n </div>\n );\n}\n\nexport const NodeGraphGroupView = React.memo(NodeGraphGroupViewImpl);\nNodeGraphGroupView.displayName = 'NodeGraphGroupView';\n"],"names":[],"mappings":";;;;;;;;;;;;;AAyDA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGF;;;;;;;;;;AAcA;;;;;;;;;;AAWA;AAuBA;;AAIe;AACb;AAA8B;AAC9B;AAAuB;AACvB;AAA6B;AAC7B;;;AAGA;AACE;;;;;;;;AAUF;AACF;AAEA;AAIE;;AAEI;;;;;AAKJ;;AAEI;;;;;;;AAOJ;AACF;AAEA;;;AAKE;;;;AAII;AACA;;;AAGJ;AACF;AAEA;;;;;;;;;;;;;;AAcG;AACH;AASE;AACA;;;AAeA;AACA;;;;;;;;AASA;AACA;;;;AAKI;AACA;;AAEJ;AAEA;AACE;;;;AAGA;;AAGF;;AAEE;AACF;AAEA;;;;;AAMI;;AAEF;AAIF;AAEI;;AAEE;;AACK;;AAEL;;;;AAIJ;;;;;;;AAUF;AAEI;AACF;;;AAMF;AAEA;;;;;AAQK;AAIC;AACF;AAEE;;;;;;;AAWA;AAUM;;AAgDN;;AAMQ;;AAKA;AACF;AAGN;AAGR;AAEO;AACP;;"}
|