entangle-ui 0.10.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 +72 -0
- package/README.md +7 -5
- 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/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/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.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.map +1 -1
- 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.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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorInputs.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPalette.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPicker.js.map +1 -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.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveCanvas.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveEditor.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveInteraction.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveRenderer.js.map +1 -1
- package/dist/esm/components/controls/FileTree/FileTree.js +7 -1
- package/dist/esm/components/controls/FileTree/FileTree.js.map +1 -1
- package/dist/esm/components/controls/FileTree/fileTreeIcons.js.map +1 -1
- package/dist/esm/components/controls/FileTree/fileTreeLabels.js.map +1 -1
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js +1 -1
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js.map +1 -1
- package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -1
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/controls/NumberInput/NumberInput.js.map +1 -1
- 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 +15 -7
- package/dist/esm/components/controls/Select/Select.js.map +1 -1
- package/dist/esm/components/controls/Slider/Slider.js.map +1 -1
- package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeNode.js +6 -0
- package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeView.js.map +1 -1
- package/dist/esm/components/controls/TreeView/useTreeState.js.map +1 -1
- package/dist/esm/components/controls/VectorInput/VectorInput.js +6 -0
- package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
- package/dist/esm/components/data/DataTable/DataTable.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserAnnouncementLive.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserBreadcrumbs.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContext.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContextMenu.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserRenameField.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSidebar.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStatusBar.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStore.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFilter.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFormat.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserGeometry.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserKeyboard.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserLabels.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/slots.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserAnnouncement.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetGridKeyboardNav.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatActionBar.js.map +1 -1
- 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 +6 -0
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatContextChip.js.map +1 -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 +6 -0
- 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.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js +6 -0
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
- 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.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.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatScroll.js.map +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.js +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.js.map +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.types.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapContext.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapCorner.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapFooter.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapStore.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapTitle.js.map +1 -1
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js +1 -1
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js.map +1 -1
- package/dist/esm/components/editor/Minimap/computeBoundsFromItems.js.map +1 -1
- package/dist/esm/components/editor/Minimap/index.js.map +1 -1
- package/dist/esm/components/editor/Minimap/minimapCoords.js.map +1 -1
- package/dist/esm/components/editor/Minimap/minimapDrawing.js.map +1 -1
- package/dist/esm/components/editor/Minimap/minimapHitTest.js.map +1 -1
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js +1 -1
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraph.types.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphContext.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphEdgeLabels.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphGroup.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphMinimap.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeContext.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeParts.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSectionContext.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPin.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPortVisual.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphSlots.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphSpawnPalette.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphStore.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphToolbar.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/createTypeMatchValidator.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphActions.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphDrawing.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphIds.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphMath.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/sortSlots.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useDragGesture.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useStoreSlice.js.map +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 +5 -4
- 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 +18 -3
- 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 +29 -21
- 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.js +1 -1
- package/dist/esm/components/editor/Timeline/Timeline.js.map +1 -1
- package/dist/esm/components/editor/Timeline/Timeline.types.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineContext.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineFooter.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js +1 -1
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineStore.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineToolbar.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js +1 -1
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineTrackScale.js.map +1 -1
- package/dist/esm/components/editor/Timeline/index.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineCoords.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineDrawing.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineEdits.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineHitTest.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineLayout.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineSelection.js.map +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js.map +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js.map +1 -1
- package/dist/esm/components/editor/Timeline/useTimelinePlayback.js.map +1 -1
- package/dist/esm/components/editor/TransformControl/TransformControl.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/ViewportGizmo.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/gizmoMath.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoInteraction.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoRenderer.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/feedback/Dialog/useDialogAnimation.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -1
- package/dist/esm/components/feedback/EmptyState/EmptyState.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogView.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewBody.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewContext.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewParts.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewStore.js.map +1 -1
- package/dist/esm/components/feedback/LogView/logViewLabels.js.map +1 -1
- package/dist/esm/components/feedback/LogView/logViewLevels.js.map +1 -1
- package/dist/esm/components/feedback/LogView/logViewUtils.js.map +1 -1
- package/dist/esm/components/feedback/LogView/useFollowTail.js.map +1 -1
- package/dist/esm/components/feedback/LogView/useLogSelection.js.map +1 -1
- package/dist/esm/components/feedback/ProgressBar/CircularProgress.js.map +1 -1
- package/dist/esm/components/feedback/ProgressBar/ProgressBar.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/Skeleton.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonGroup.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionContent.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionItem.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/layout/Grid/Grid.js.map +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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/layout/SplitPane/SplitPanePanel.js.map +1 -1
- package/dist/esm/components/layout/Stack/Stack.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
- 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 +6 -0
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/esm/components/navigation/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/Menu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/MenuGapContext.js.map +1 -1
- 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.js +7 -1
- package/dist/esm/components/navigation/PathBar/PathBar.js.map +1 -1
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js +6 -0
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js.map +1 -1
- package/dist/esm/components/navigation/PathBar/pathUtils.js.map +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControlItem.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/Tab.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/TabList.js.map +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 +6 -0
- package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/primitives/Avatar/AvatarGroup.js.map +1 -1
- package/dist/esm/components/primitives/Badge/Badge.js +6 -0
- 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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +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.map +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.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/primitives/Radio/RadioGroup.js.map +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.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Arrow.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Tooltip.js.map +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.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasCoords.js.map +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.map +1 -1
- package/dist/esm/components/primitives/viewport/Viewport.js +1 -1
- package/dist/esm/components/primitives/viewport/Viewport.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportContext.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportLayer.js +1 -1
- package/dist/esm/components/primitives/viewport/ViewportLayer.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportOverlay.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportStore.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportWorld.js.map +1 -1
- package/dist/esm/components/primitives/viewport/useViewportGestures.js +1 -1
- package/dist/esm/components/primitives/viewport/useViewportGestures.js.map +1 -1
- package/dist/esm/components/primitives/viewport/viewportCoords.js.map +1 -1
- package/dist/esm/components/primitives/viewport/viewportMath.js.map +1 -1
- package/dist/esm/components/shell/AppShell/AppShell.js.map +1 -1
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/context/KeyboardContext.js.map +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -1
- package/dist/esm/hooks/useClickOutside/useClickOutside.js.map +1 -1
- 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.map +1 -1
- 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.map +1 -1
- package/dist/esm/hooks/useFocusTrap/useFocusTrap.js.map +1 -1
- package/dist/esm/hooks/useHotkey/useHotkey.js.map +1 -1
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -1
- package/dist/esm/hooks/useIsMounted/useIsMounted.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -1
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -1
- package/dist/esm/hooks/useMergedRef/useMergedRef.js.map +1 -1
- package/dist/esm/hooks/useNavigationHistory/useNavigationHistory.js.map +1 -1
- package/dist/esm/hooks/useResizeObserver/useResizeObserver.js.map +1 -1
- package/dist/esm/hooks/useTheme/useTheme.js +1 -1
- package/dist/esm/hooks/useTheme/useTheme.js.map +1 -1
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node_modules/tslib/tslib.es6.js.map +1 -1
- package/dist/esm/styles.js +3 -0
- package/dist/esm/styles.js.map +1 -0
- package/dist/esm/theme/ThemeProvider.js +5 -3
- 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/createCustomTheme.js +1 -1
- package/dist/esm/theme/createCustomTheme.js.map +1 -1
- package/dist/esm/theme/createLightTheme.js.map +1 -1
- package/dist/esm/theme/darkThemeValues.js.map +1 -1
- package/dist/esm/theme/lightThemeValues.js.map +1 -1
- package/dist/esm/theme/themeContractData.js.map +1 -1
- package/dist/esm/utils/cx.js.map +1 -1
- package/dist/esm/utils/devWarn.js.map +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/CameraIcon.d.ts +34 -0
- package/dist/types/components/Icons/CubeIcon.d.ts +34 -0
- package/dist/types/components/Icons/OrbitIcon.d.ts +34 -0
- package/dist/types/components/Icons/RotateIcon.d.ts +35 -0
- package/dist/types/components/Icons/SpinIcon.d.ts +35 -0
- package/dist/types/components/Icons/ZoomFitIcon.d.ts +35 -0
- package/dist/types/components/controls/Select/Select.d.ts +1 -1
- package/dist/types/components/controls/Select/Select.types.d.ts +43 -9
- package/dist/types/components/editor/PropertyInspector/PropertyInspector.types.d.ts +49 -3
- package/dist/types/components/primitives/Button/Button.d.ts +1 -1
- package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/theme/ThemeProvider.d.ts +5 -3
- package/package.json +6 -2
- 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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timelineHitTest.js","sources":["../../../../../../src/components/editor/Timeline/timelineHitTest.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"timelineHitTest.js","sources":["src/components/editor/Timeline/timelineHitTest.ts"],"sourcesContent":["import { frameToX, valueToY } from './timelineCoords';\n\nimport type { TimelineKeyframeRef, TimelineView } from './Timeline.types';\nimport type { TimelineRow } from './timelineLayout';\nimport type { Point2D } from '@/components/primitives/canvas/canvas.types';\nimport type { ViewportSize } from '@/components/primitives/viewport';\n\n/** Axis-aligned rectangle in content-space (CSS-pixel) coordinates. */\nexport interface TimelineScreenRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type TimelineHit =\n | { kind: 'ruler' }\n | { kind: 'loop-strip' }\n | { kind: 'playhead' }\n | { kind: 'loop-start' }\n | { kind: 'loop-end' }\n | { kind: 'loop-body' }\n | { kind: 'group'; groupId: string }\n | { kind: 'keyframe'; trackId: string; keyframeId: string }\n | {\n kind: 'tangent';\n trackId: string;\n keyframeId: string;\n which: 'in' | 'out';\n }\n | { kind: 'empty'; trackId: string }\n | { kind: 'outside' };\n\nexport interface TimelineHitInput {\n /** Pointer position in track-area CSS px (relative to the body top-left). */\n point: Point2D;\n view: TimelineView;\n size: ViewportSize;\n /** Pre-computed rows (group headers + track lanes). */\n rows: ReadonlyArray<TimelineRow>;\n scrollTop: number;\n rulerHeight: number;\n /** Height of the dedicated loop strip directly below the ruler (0 = off). */\n loopStripHeight?: number;\n /**\n * Bracket-style edges get a wider pick zone (looks like `[ ]`); the default\n * is the thin vertical-bar style.\n */\n loopHandles?: 'edges' | 'brackets';\n frame: number;\n showPlayhead: boolean;\n /** Selection predicate — enables tangent-handle picking in graph mode. */\n isSelected?: (trackId: string, keyframeId: string) => boolean;\n /** Resolved loop region — enables loop-edge / body picking on the ruler. */\n loopRegion?: { startFrame: number; endFrame: number } | null;\n}\n\nconst KEYFRAME_PICK_X = 6;\nconst PLAYHEAD_PICK_X = 4;\nconst HANDLE_PICK_X = 6;\nconst LOOP_EDGE_PICK_X = 5;\nconst LOOP_BRACKET_PICK_X = 9;\n\n/**\n * Resolve what's under a pointer: ruler / loop handles, a group header, a\n * keyframe, a graph tangent handle, an empty track row, or outside. Works in\n * content space (scroll-adjusted), iterating the pre-computed rows.\n */\nexport function hitTestTimeline(input: TimelineHitInput): TimelineHit {\n const { point, view, size, rows, scrollTop, rulerHeight } = input;\n const toX = (f: number): number => frameToX(f, view, size.width);\n\n const playheadX = input.showPlayhead ? toX(input.frame) : null;\n // The playhead line is grabbable along its whole height. The head (ruler\n // band) gets a wider pick zone so the triangle is easy to grab.\n const nearPlayheadHead =\n playheadX !== null && Math.abs(point.x - playheadX) <= PLAYHEAD_PICK_X + 4;\n const nearPlayheadBody =\n playheadX !== null && Math.abs(point.x - playheadX) <= PLAYHEAD_PICK_X;\n\n const loopStripHeight = input.loopStripHeight ?? 0;\n const chromeHeight = rulerHeight + loopStripHeight;\n const edgePick =\n input.loopHandles === 'brackets' ? LOOP_BRACKET_PICK_X : LOOP_EDGE_PICK_X;\n\n // Chrome zone (ruler + optional loop strip): both bands share loop-edge /\n // loop-body picking; the ruler band falls back to `ruler` (scrub), the\n // strip band falls back to `loop-strip` (plain-drag loop create).\n if (point.y < chromeHeight) {\n const inRuler = point.y < rulerHeight;\n const loop = input.loopRegion;\n if (loop) {\n const lx = toX(loop.startFrame);\n const rx = toX(loop.endFrame);\n // Loop edges are deliberate small targets → they win over the playhead.\n if (Math.abs(point.x - lx) <= edgePick) return { kind: 'loop-start' };\n if (Math.abs(point.x - rx) <= edgePick) return { kind: 'loop-end' };\n // The playhead line beats the (wide) loop body so it stays grabbable\n // even when it sits inside the loop region.\n if (nearPlayheadHead) return { kind: 'playhead' };\n if (point.x > lx && point.x < rx) return { kind: 'loop-body' };\n } else if (nearPlayheadHead) {\n return { kind: 'playhead' };\n }\n return inRuler ? { kind: 'ruler' } : { kind: 'loop-strip' };\n }\n\n const contentY = point.y - chromeHeight + scrollTop;\n const nearPlayhead = nearPlayheadBody;\n\n for (const row of rows) {\n if (contentY < row.top || contentY >= row.top + row.height) continue;\n // The playhead line is grabbable over group-header rows too (it spans the\n // full height), but only on its thin pick band so the rest of the header\n // still toggles collapse.\n if (row.kind === 'group') {\n if (nearPlayhead) return { kind: 'playhead' };\n return { kind: 'group', groupId: row.group.id };\n }\n\n const track = row.track;\n const { top, height: rowH } = row;\n const range = row.graph ? row.range : null;\n const centerY = top + rowH / 2;\n\n if (range && input.isSelected) {\n const pickRadiusSq = HANDLE_PICK_X * HANDLE_PICK_X;\n for (const kf of track.keyframes) {\n if (kf.id === undefined || !input.isSelected(track.id, kf.id)) continue;\n if (kf.tangentMode === 'linear' || kf.tangentMode === 'step') continue;\n const inX = toX(kf.x + kf.handleIn.x);\n const inY = valueToY(kf.y + kf.handleIn.y, range, top, rowH);\n const inDx = point.x - inX;\n const inDy = contentY - inY;\n if (inDx * inDx + inDy * inDy <= pickRadiusSq) {\n return {\n kind: 'tangent',\n trackId: track.id,\n keyframeId: kf.id,\n which: 'in',\n };\n }\n const outX = toX(kf.x + kf.handleOut.x);\n const outY = valueToY(kf.y + kf.handleOut.y, range, top, rowH);\n const outDx = point.x - outX;\n const outDy = contentY - outY;\n if (outDx * outDx + outDy * outDy <= pickRadiusSq) {\n return {\n kind: 'tangent',\n trackId: track.id,\n keyframeId: kf.id,\n which: 'out',\n };\n }\n }\n }\n\n for (const kf of track.keyframes) {\n if (kf.id === undefined) continue;\n const x = toX(kf.x);\n if (Math.abs(point.x - x) > KEYFRAME_PICK_X) continue;\n const py = range ? valueToY(kf.y, range, top, rowH) : centerY;\n const yTol = range ? KEYFRAME_PICK_X : rowH / 2;\n if (Math.abs(contentY - py) <= yTol) {\n return { kind: 'keyframe', trackId: track.id, keyframeId: kf.id };\n }\n }\n\n if (nearPlayhead) return { kind: 'playhead' };\n return { kind: 'empty', trackId: track.id };\n }\n\n if (nearPlayhead) return { kind: 'playhead' };\n return { kind: 'outside' };\n}\n\n/**\n * All keyframes whose center falls inside a content-space marquee rectangle.\n * Used for box selection.\n */\nexport function keyframesInRect(\n rect: TimelineScreenRect,\n view: TimelineView,\n size: ViewportSize,\n rows: ReadonlyArray<TimelineRow>\n): TimelineKeyframeRef[] {\n const x0 = Math.min(rect.x, rect.x + rect.width);\n const x1 = Math.max(rect.x, rect.x + rect.width);\n const y0 = Math.min(rect.y, rect.y + rect.height);\n const y1 = Math.max(rect.y, rect.y + rect.height);\n const toX = (f: number): number => frameToX(f, view, size.width);\n const result: TimelineKeyframeRef[] = [];\n\n for (const row of rows) {\n if (row.kind !== 'track') continue;\n const { track, top, height } = row;\n const range = row.graph ? row.range : null;\n const centerY = top + height / 2;\n for (const kf of track.keyframes) {\n if (kf.id === undefined) continue;\n const x = toX(kf.x);\n if (x < x0 || x > x1) continue;\n const py = range ? valueToY(kf.y, range, top, height) : centerY;\n if (py >= y0 && py <= y1) {\n result.push({ trackId: track.id, keyframeId: kf.id });\n }\n }\n }\n\n return result;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timelineLayout.js","sources":["
|
|
1
|
+
{"version":3,"file":"timelineLayout.js","sources":["src/components/editor/Timeline/timelineLayout.ts"],"sourcesContent":["import { autoValueRange } from './timelineCoords';\n\nimport type { TimelineGroup, TimelineTrack } from './Timeline.types';\n\n/** A rendered track lane. */\nexport interface TimelineTrackRow {\n kind: 'track';\n track: TimelineTrack;\n /** Top in content space (before scroll), CSS px. */\n top: number;\n height: number;\n /** Render as a graph (curve) lane. */\n graph: boolean;\n /** Cached value range — `track.valueRange` or auto-fit from keyframes. */\n range: readonly [number, number];\n}\n\n/** A group header row. */\nexport interface TimelineGroupRow {\n kind: 'group';\n group: TimelineGroup;\n top: number;\n height: number;\n}\n\nexport type TimelineRow = TimelineTrackRow | TimelineGroupRow;\n\nexport interface RowLayoutOptions {\n trackHeight: number;\n expandedHeight: number;\n groupHeaderHeight: number;\n /** Global graph mode (each track also graphs when `track.expanded`). */\n globalGraph: boolean;\n groups: ReadonlyArray<TimelineGroup>;\n}\n\nexport interface TrackGeometry {\n top: number;\n height: number;\n graph: boolean;\n range: readonly [number, number];\n}\n\nexport interface RowLayout {\n rows: TimelineRow[];\n /** Per-track content-space geometry, for keyframe positioning + hit-testing. */\n trackTops: Map<string, TrackGeometry>;\n contentHeight: number;\n}\n\n/**\n * Flatten tracks into the rendered row list: a group header before the first\n * visible member of each group (collapsing hides its members), then each\n * non-hidden track at its (variable) height — taller when graph-expanded.\n * With no groups and no expanded tracks the layout is the uniform grid\n * `index * trackHeight`.\n */\nexport function computeRows(\n tracks: ReadonlyArray<TimelineTrack>,\n opts: RowLayoutOptions\n): RowLayout {\n const groupById = new Map(opts.groups.map(g => [g.id, g]));\n const seen = new Set<string>();\n const rows: TimelineRow[] = [];\n const trackTops = new Map<string, TrackGeometry>();\n let top = 0;\n\n for (const track of tracks) {\n if (track.hidden) continue;\n const group = track.groupId ? groupById.get(track.groupId) : undefined;\n if (group && !seen.has(group.id)) {\n seen.add(group.id);\n rows.push({ kind: 'group', group, top, height: opts.groupHeaderHeight });\n top += opts.groupHeaderHeight;\n }\n if (group?.collapsed) continue;\n const graph = opts.globalGraph || !!track.expanded;\n const height =\n track.height ?? (track.expanded ? opts.expandedHeight : opts.trackHeight);\n const range = track.valueRange ?? autoValueRange(track.keyframes);\n const geom: TrackGeometry = { top, height, graph, range };\n rows.push({ kind: 'track', track, top, height, graph, range });\n trackTops.set(track.id, geom);\n top += height;\n }\n\n return { rows, trackTops, contentHeight: top };\n}\n"],"names":[],"mappings":";;AAkDA;;;;;;AAMG;AACG,SAAU,WAAW,CACzB,MAAoC,EACpC,IAAsB,EAAA;IAEtB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1D,IAAA,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU;IAC9B,MAAM,IAAI,GAAkB,EAAE;AAC9B,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB;IAClD,IAAI,GAAG,GAAG,CAAC;AAEX,IAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;QAC1B,IAAI,KAAK,CAAC,MAAM;YAAE;QAClB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,SAAS;AACtE,QAAA,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;AAChC,YAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;AAClB,YAAA,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC;AACxE,YAAA,GAAG,IAAI,IAAI,CAAC,iBAAiB;QAC/B;QACA,IAAI,KAAK,EAAE,SAAS;YAAE;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ;QAClD,MAAM,MAAM,GACV,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC;AAC3E,QAAA,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,IAAI,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC;QACjE,MAAM,IAAI,GAAkB,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE;AACzD,QAAA,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC9D,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC;QAC7B,GAAG,IAAI,MAAM;IACf;IAEA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,EAAE;AAChD;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timelineSelection.js","sources":["
|
|
1
|
+
{"version":3,"file":"timelineSelection.js","sources":["src/components/editor/Timeline/timelineSelection.ts"],"sourcesContent":["import type { TimelineKeyframeRef, TimelineSelection } from './Timeline.types';\n\nconst SEPARATOR = '\u0000';\n\n/** Stable string key for a keyframe ref — safe for Set lookups. */\nexport function selectionKey(ref: TimelineKeyframeRef): string {\n return `${ref.trackId}${SEPARATOR}${ref.keyframeId}`;\n}\n\n/** Build a Set of selection keys for O(1) membership tests. */\nexport function selectionKeySet(selection: TimelineSelection): Set<string> {\n const set = new Set<string>();\n for (const ref of selection) set.add(selectionKey(ref));\n return set;\n}\n\n/** Structural equality of two keyframe refs. */\nexport function sameRef(\n a: TimelineKeyframeRef,\n b: TimelineKeyframeRef\n): boolean {\n return a.trackId === b.trackId && a.keyframeId === b.keyframeId;\n}\n"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,GAAG;AAErB;AACM,SAAU,YAAY,CAAC,GAAwB,EAAA;IACnD,OAAO,CAAA,EAAG,GAAG,CAAC,OAAO,CAAA,EAAG,SAAS,CAAA,EAAG,GAAG,CAAC,UAAU,CAAA,CAAE;AACtD;AAEA;AACM,SAAU,eAAe,CAAC,SAA4B,EAAA;AAC1D,IAAA,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU;IAC7B,KAAK,MAAM,GAAG,IAAI,SAAS;QAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AACvD,IAAA,OAAO,GAAG;AACZ;AAEA;AACM,SAAU,OAAO,CACrB,CAAsB,EACtB,CAAsB,EAAA;AAEtB,IAAA,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;AACjE;;;;"}
|
|
@@ -6,7 +6,7 @@ import { useLatest } from '../../../hooks/useLatest/useLatest.js';
|
|
|
6
6
|
import '../../../hooks/useBreakpoint/useBreakpoint.js';
|
|
7
7
|
import { vars } from '../../../theme/contract.css.js';
|
|
8
8
|
import './../../../assets/src/theme/lightTheme.css.ts.vanilla-Ct6f9Iym.css';
|
|
9
|
-
import '
|
|
9
|
+
import '@vanilla-extract/css';
|
|
10
10
|
import './../../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
11
11
|
import 'react/jsx-runtime';
|
|
12
12
|
import './../../../assets/src/theme/globalScrollbars.css.ts.vanilla-BAJwnUEJ.css';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTimelineDraw.js","sources":["
|
|
1
|
+
{"version":3,"file":"useTimelineDraw.js","sources":["src/components/editor/Timeline/useTimelineDraw.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useLayoutEffect, useRef } from 'react';\n\nimport { resolveVarValue } from '@/components/primitives/canvas/canvasTheme';\nimport { useLatest } from '@/hooks';\nimport { vars } from '@/theme/contract.css';\n\nimport { framesToTimecode } from './timelineCoords';\nimport { drawTimeline } from './timelineDrawing';\nimport { selectionKey, selectionKeySet } from './timelineSelection';\n\nimport type {\n TimelineDrawInfo,\n TimelineSelection,\n TimelineTrackScaleProps,\n TimelineView,\n} from './Timeline.types';\nimport type { TimelineDrawColors } from './timelineDrawing';\nimport type { TimelineRow } from './timelineLayout';\nimport type { ViewportSize } from '@/components/primitives/viewport';\nimport type React from 'react';\n\nexport interface UseTimelineDrawOptions {\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n size: ViewportSize;\n view: TimelineView;\n startFrame: number;\n endFrame: number;\n fps: number;\n frame: number;\n rows: ReadonlyArray<TimelineRow>;\n scrollTop: number;\n rulerHeight: number;\n loopStripHeight: number;\n loopHandles: 'edges' | 'brackets';\n showPlayhead: boolean;\n selection: TimelineSelection;\n hover: { trackId: string; keyframeId: string } | null;\n hoverPlayhead: boolean;\n marquee: { x: number; y: number; width: number; height: number } | null;\n loopRegion: { startFrame: number; endFrame: number } | null;\n loopHover: 'start' | 'end' | 'body' | null;\n backgroundColor?: string;\n playheadColor?: string;\n trackScale?: TimelineTrackScaleProps;\n renderOverlay?: (\n ctx: CanvasRenderingContext2D,\n info: TimelineDrawInfo\n ) => void;\n formatTime?: (frame: number, fps: number) => string;\n}\n\n/**\n * Schedules a canvas redraw via `requestAnimationFrame`, resolves theme\n * tokens per-frame so the live theme is respected, and re-runs on any\n * input change. `renderOverlay` / `formatTime` are read through `useLatest`\n * refs so consumer inline functions don't invalidate the draw schedule\n * (component-patterns.md rule #3).\n */\nexport function useTimelineDraw(opts: UseTimelineDrawOptions): void {\n const rafRef = useRef<number>(0);\n\n // Consumer callbacks — read live without invalidating `scheduleDraw`.\n const renderOverlayRef = useLatest(opts.renderOverlay);\n const formatTimeRef = useLatest(opts.formatTime);\n\n const scheduleDraw = useCallback((): void => {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(() => {\n const canvas = opts.canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const w = Math.max(1, opts.size.width);\n const h = Math.max(1, opts.size.height);\n const dpr =\n typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n const targetW = Math.max(1, Math.round(w * dpr));\n const targetH = Math.max(1, Math.round(h * dpr));\n if (canvas.width !== targetW || canvas.height !== targetH) {\n canvas.width = targetW;\n canvas.height = targetH;\n }\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n\n const fontPx =\n parseFloat(resolveVarValue(canvas, vars.typography.fontSize.xs)) || 10;\n const family = getComputedStyle(canvas).fontFamily || 'sans-serif';\n\n const colors: TimelineDrawColors = {\n background:\n opts.backgroundColor ??\n resolveVarValue(canvas, vars.colors.background.secondary),\n gridLine: resolveVarValue(canvas, vars.colors.border.default),\n rulerText: resolveVarValue(canvas, vars.colors.text.muted),\n rowSeparator: resolveVarValue(canvas, vars.colors.border.default),\n keyframe: resolveVarValue(canvas, vars.colors.accent.secondary),\n keyframeSelected: resolveVarValue(canvas, vars.colors.accent.primary),\n keyframeStroke: resolveVarValue(\n canvas,\n vars.colors.background.secondary\n ),\n playhead:\n opts.playheadColor ??\n resolveVarValue(canvas, vars.colors.accent.primary),\n groupHeader: resolveVarValue(canvas, vars.colors.background.elevated),\n };\n\n const selKeys = selectionKeySet(opts.selection);\n const hover = opts.hover;\n\n drawTimeline({\n ctx,\n size: { width: w, height: h },\n view: opts.view,\n startFrame: opts.startFrame,\n endFrame: opts.endFrame,\n fps: opts.fps,\n frame: opts.frame,\n rows: opts.rows,\n scrollTop: opts.scrollTop,\n rulerHeight: opts.rulerHeight,\n loopStripHeight: opts.loopStripHeight,\n loopHandles: opts.loopHandles,\n showPlayhead: opts.showPlayhead,\n colors,\n font: `${fontPx}px ${family}`,\n formatTime: (f: number, fps: number): string =>\n (formatTimeRef.current ?? framesToTimecode)(f, fps),\n isSelected: (t, k) =>\n selKeys.has(selectionKey({ trackId: t, keyframeId: k })),\n isHovered: (t, k) =>\n hover !== null && hover.trackId === t && hover.keyframeId === k,\n playheadHovered: opts.hoverPlayhead,\n ...(opts.trackScale\n ? {\n trackScale: {\n position: opts.trackScale.position ?? 'start',\n format:\n opts.trackScale.format ??\n ((v: number) => Number(v.toFixed(2)).toString()),\n showMidpoint: opts.trackScale.showMidpoint ?? false,\n gridlines: opts.trackScale.gridlines ?? false,\n minLaneHeight: opts.trackScale.minLaneHeight ?? 48,\n ...(opts.trackScale.color\n ? { color: opts.trackScale.color }\n : {}),\n },\n }\n : {}),\n renderOverlay: (\n ctx: CanvasRenderingContext2D,\n info: TimelineDrawInfo\n ): void => renderOverlayRef.current?.(ctx, info),\n marquee: opts.marquee,\n loopRegion: opts.loopRegion,\n loopHover: opts.loopHover,\n });\n });\n // `renderOverlayRef` / `formatTimeRef` are stable refs from `useLatest`.\n }, [\n opts.canvasRef,\n opts.size,\n opts.view,\n opts.startFrame,\n opts.endFrame,\n opts.fps,\n opts.frame,\n opts.rows,\n opts.scrollTop,\n opts.rulerHeight,\n opts.loopStripHeight,\n opts.loopHandles,\n opts.showPlayhead,\n opts.selection,\n opts.hover,\n opts.hoverPlayhead,\n opts.marquee,\n opts.loopRegion,\n opts.loopHover,\n opts.backgroundColor,\n opts.playheadColor,\n opts.trackScale,\n formatTimeRef,\n renderOverlayRef,\n ]);\n\n useLayoutEffect(() => {\n scheduleDraw();\n return () => {\n cancelAnimationFrame(rafRef.current);\n };\n }, [scheduleDraw]);\n\n // Redraw when DPR changes (e.g. dragging to a different display).\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mq = window.matchMedia(\n `(resolution: ${window.devicePixelRatio}dppx)`\n );\n const handler = (): void => {\n scheduleDraw();\n };\n mq.addEventListener('change', handler);\n return () => {\n mq.removeEventListener('change', handler);\n };\n }, [scheduleDraw]);\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAqDA;;;;;;AAMG;AACG;AACJ;;;;AAMA;AACE;AACA;AACE;AACA;;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACE;AACA;;AAEF;AAEA;;AAIA;;;AAIE;AACA;AACA;AACA;AACA;AACA;;;AAOA;;;AAIF;AAEA;;;;;;;;;;;;;;;AAeE;AACA;;;;;AAQE;AACI;AACE;AACA;AAEE;AACF;AACA;AACA;AACA;;;AAGD;AACF;;AAEL;;;;AAOD;AACH;;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAGD;;AAGC;AACA;AACE;AACF;AACF;;;;;AAKE;;AAIE;AACF;AACA;AACA;AACE;AACF;AACF;AACF;;"}
|
|
@@ -4,7 +4,7 @@ import '../../../utils/devWarn.js';
|
|
|
4
4
|
import { useLatest } from '../../../hooks/useLatest/useLatest.js';
|
|
5
5
|
import '../../../hooks/useBreakpoint/useBreakpoint.js';
|
|
6
6
|
import './../../../assets/src/theme/lightTheme.css.ts.vanilla-Ct6f9Iym.css';
|
|
7
|
-
import '
|
|
7
|
+
import '@vanilla-extract/css';
|
|
8
8
|
import './../../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
9
9
|
import 'react/jsx-runtime';
|
|
10
10
|
import './../../../assets/src/theme/globalScrollbars.css.ts.vanilla-BAJwnUEJ.css';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTimelineGestures.js","sources":["../../../../../../src/components/editor/Timeline/useTimelineGestures.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAoCA;AACA;AA+EA;AAIE;;AACA;;AAEF;AAEA;AAKE;;AAEE;AACE;AACA;;;AAGN;AAEA;AAIE;AACA;AACE;AAAwC;;AAE1C;AACF;AAEA;;;;;;;;;;;AAWG;AACG;;;;AAMJ;AAEA;AACA;AAEA;AAEI;AACA;;;;;;;;;;;;AAYE;AAED;AACH;;AAME;AACA;AACA;AACA;;;AAGA;AACA;AACE;;;AAoBJ;;AAME;AACA;AACA;AAAU;AACV;AACA;AAAW;;AAEX;;AAEA;AAAmB;AACnB;AAEA;;;AAGE;;AAEJ;AAIF;AAEI;AACA;AACA;;;AAEA;AACA;AAEA;;AAEE;;;AAGE;;;AAGA;;AAEF;;;AAGF;;AAEA;AAEA;;AAGE;AAEA;AACA;;;AAEA;;;AAGE;;;AAGA;AAEI;AACA;AACE;AACA;AACN;;AAEF;;;;;;;;AAUA;AACA;;AAGA;AACA;AACA;;;AAGE;;;AAGA;;;AAGF;AACA;;;;AAKF;;AAEE;AACA;AACA;;;AAGE;;;AAGA;;;AAGF;AACA;;;AAIF;;AAEE;;;AAGE;;;;AAIF;AACA;;;AAIF;;AAEE;;;AAGE;;;;AAIA;;;AAGF;;;AAIF;AACE;AACA;;;AAQF;AACE;AACA;AACA;AACA;AACA;;AAEE;;;AAGE;;;;AAIA;AACA;;;AAGF;;;;;AAMJ;;AAEA;;;AAGE;;;;;AAKF;AACE;;AAED;AACH;AAIF;AAEI;AACA;AACA;;;AAEA;;;;;AAME;AACA;AACE;;;AAGC;;;AAED;;;;AAKE;AACA;AACE;AACA;AACE;;;AAKV;;AACA;AACA;AAEA;AACE;AACA;;AAEE;;;;AAIC;;;;AAGC;;AAED;;;;;AAIC;AACD;;;;AAGD;;AAMA;;;;AAIJ;AACE;;;AAGF;AACE;;;;AAIE;AACA;AACD;;;AAGH;;;;;;;AAcA;;;;;AAKA;AACE;AACA;AACE;AACA;AACA;AACA;AACD;AACF;;AAKL;AACE;AACA;AACA;AACA;AACF;;AAII;AACA;AACA;;AACA;;AAEE;;;AAGF;AACA;;AAGE;;;AAGE;AACE;;;AAEG;AACL;;AACK;;;AAEA;;;;;;AAYA;AACL;AACE;AACA;AACA;AACA;;AAEF;AAEE;;AAEqB;;;;;AAKjB;;;;AAQJ;;;;;AAMN;AACA;;AAKJ;AAEI;AACF;AAGF;AAEI;AACF;AAIF;AAEI;;;AAEA;AACA;;;AAEA;AACA;;;AAEA;;AACA;AACA;AACA;;;;;;AAUE;;AAED;AACD;AACA;AACA;AACF;AAIF;AAEI;AACA;AACA;;;AAEA;AACA;;;AAIE;;;AAGE;AACA;AACD;;;;AAKH;AACE;;;AAIF;AACA;AACA;AACA;;AAEA;;AAEA;;AAEE;;AAED;AACH;AAIF;AAEI;AACA;AACE;;;AAGE;AACA;;;;AAIF;;AAEE;;AAEF;;AAEE;;AAEF;;;;AAGE;AACA;;;;AAGA;AACA;AACA;;;AAGF;;;;;AAIE;;;AAGF;;AAEE;;AACA;;;;AAGA;AACA;AACA;AACA;;;AAGF;;;AAGJ;AAIF;;;;;;;;AASG;;;;;;;;AASA;AAGH;AACF;;"}
|
|
1
|
+
{"version":3,"file":"useTimelineGestures.js","sources":["src/components/editor/Timeline/useTimelineGestures.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useRef } from 'react';\n\nimport { useLatest } from '@/hooks';\n\nimport { clamp, snapFrame, xToFrame, yToValue } from './timelineCoords';\nimport {\n addKeyframe,\n copySelectedKeyframes,\n makeKeyframe,\n moveSelectedKeyframes,\n moveSelectedKeyframesGraph,\n pasteKeyframes,\n removeSelectedKeyframes,\n setKeyframeTangent,\n valueAtPointer,\n} from './timelineEdits';\nimport { hitTestTimeline, keyframesInRect } from './timelineHitTest';\nimport { sameRef } from './timelineSelection';\n\nimport type {\n TimelineGroup,\n TimelineKeyframeRef,\n TimelineLoop,\n TimelineSelection,\n TimelineTrack,\n TimelineView,\n} from './Timeline.types';\nimport type { TimelineClipboard } from './timelineEdits';\nimport type { TimelineRow, TrackGeometry } from './timelineLayout';\nimport type { TimelineStore } from './TimelineStore';\nimport type { Point2D } from '@/components/primitives/canvas/canvas.types';\nimport type { ViewportSize } from '@/components/primitives/viewport';\nimport type React from 'react';\n\nconst CLICK_THRESHOLD_PX = 3;\nconst KEYBOARD_LARGE_STEP = 10;\n\nexport interface TimelineGestureActions {\n /** Scrub live (snaps/clamps, fires onFrameChange). */\n seekLive: (frame: number) => void;\n /** Scrub commit (snaps/clamps, fires onFrameChange + onFrameChangeComplete). */\n seekCommit: (frame: number) => void;\n setSelection: (selection: TimelineSelection) => void;\n /** Live track update (onTracksChange). */\n setTracks: (tracks: TimelineTrack[]) => void;\n /** Commit (onTracksChangeComplete). */\n commitTracks: (tracks: TimelineTrack[]) => void;\n setView: (view: TimelineView) => void;\n setLoop: (loop: TimelineLoop) => void;\n scrollBy: (deltaPixels: number) => void;\n setGroups: (groups: TimelineGroup[]) => void;\n}\n\nexport interface UseTimelineGesturesOptions extends TimelineGestureActions {\n containerRef: React.RefObject<HTMLDivElement | null>;\n store: TimelineStore;\n view: TimelineView;\n size: ViewportSize;\n tracks: ReadonlyArray<TimelineTrack>;\n selection: TimelineSelection;\n frame: number;\n startFrame: number;\n endFrame: number;\n trackHeight: number;\n scrollTop: number;\n rulerHeight: number;\n loopStripHeight: number;\n loopHandles: 'edges' | 'brackets';\n snap: boolean | number;\n editable: boolean;\n allowAddKeyframe: boolean;\n allowDeleteKeyframe: boolean;\n showPlayhead: boolean;\n rows: ReadonlyArray<TimelineRow>;\n trackTops: ReadonlyMap<string, TrackGeometry>;\n expandedHeight: number;\n groups: ReadonlyArray<TimelineGroup>;\n minFramesVisible: number;\n maxFramesVisible: number | undefined;\n loopRegion: { startFrame: number; endFrame: number } | null;\n maxScrollTop: number;\n}\n\ninterface PointerState {\n pointerId: number;\n mode: 'scrub' | 'move' | 'marquee' | 'pan' | 'tangent' | 'loop';\n startX: number;\n startY: number;\n origTracks?: ReadonlyArray<TimelineTrack>;\n moveSelection?: TimelineSelection;\n graphMove?: boolean;\n startFramePos?: number;\n startView?: TimelineView;\n additive?: boolean;\n tangentRef?: TimelineKeyframeRef;\n tangentWhich?: 'in' | 'out';\n loopWhich?: 'start' | 'end' | 'body' | 'create';\n startLoop?: { startFrame: number; endFrame: number };\n}\n\nexport interface UseTimelineGesturesReturn {\n handlers: {\n onPointerDown: (e: React.PointerEvent<HTMLDivElement>) => void;\n onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => void;\n onPointerUp: (e: React.PointerEvent<HTMLDivElement>) => void;\n onPointerCancel: (e: React.PointerEvent<HTMLDivElement>) => void;\n onPointerLeave: () => void;\n onDoubleClick: (e: React.MouseEvent<HTMLDivElement>) => void;\n onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void;\n };\n /** Attach via a native non-passive listener so `preventDefault` works. */\n onWheel: (e: WheelEvent) => void;\n}\n\nfunction localPoint(\n e: { clientX: number; clientY: number },\n container: HTMLDivElement | null\n): Point2D {\n if (!container) return { x: 0, y: 0 };\n const rect = container.getBoundingClientRect();\n return { x: e.clientX - rect.left, y: e.clientY - rect.top };\n}\n\nfunction nextSelection(\n selection: TimelineSelection,\n ref: TimelineKeyframeRef,\n additive: boolean\n): TimelineSelection {\n const exists = selection.some(r => sameRef(r, ref));\n if (additive) {\n return exists\n ? selection.filter(r => !sameRef(r, ref))\n : [...selection, ref];\n }\n return exists ? selection : [ref];\n}\n\nfunction mergeSelection(\n base: TimelineSelection,\n add: ReadonlyArray<TimelineKeyframeRef>\n): TimelineSelection {\n const result = [...base];\n for (const ref of add) {\n if (!result.some(r => sameRef(r, ref))) result.push(ref);\n }\n return result;\n}\n\n/**\n * Pointer / wheel / keyboard gesture machine for `<Timeline>`. The hook is\n * called every render with a fresh `options` object (live props + the\n * component's action callbacks, several of which change identity on every\n * frame). They are read through a single `useLatest` ref so the returned\n * handlers keep a **stable identity for the component's lifetime** — they are\n * attached to the DOM (`onPointerDown=…`) and to a native `wheel` listener, so\n * stability is what keeps listeners from re-attaching every render. Reads\n * happen at event time, by which point the ref already holds the latest\n * committed options. Drag state (cursor + marquee) is written to the store,\n * not React state.\n */\nexport function useTimelineGestures(\n options: UseTimelineGesturesOptions\n): UseTimelineGesturesReturn {\n // Single live snapshot of every option/action. `useLatest` keeps it synced\n // to the most recently committed render, so handlers below never need the\n // individual values in their dependency arrays.\n const optsRef = useLatest(options);\n\n const stateRef = useRef<PointerState | null>(null);\n const clipboardRef = useRef<TimelineClipboard | null>(null);\n\n const hitAt = useCallback(\n (point: Point2D) => {\n const o = optsRef.current;\n return hitTestTimeline({\n point,\n view: o.view,\n size: o.size,\n rows: o.rows,\n scrollTop: o.scrollTop,\n rulerHeight: o.rulerHeight,\n loopStripHeight: o.loopStripHeight,\n loopHandles: o.loopHandles,\n frame: o.frame,\n showPlayhead: o.showPlayhead,\n loopRegion: o.loopRegion,\n isSelected: (t, k) =>\n o.selection.some(r => r.trackId === t && r.keyframeId === k),\n });\n },\n [optsRef]\n );\n\n const moveFromState = useCallback(\n (state: PointerState, point: Point2D): TimelineTrack[] => {\n const o = optsRef.current;\n const view = o.view;\n const width = o.size.width;\n const curFrame = xToFrame(point.x, view, width);\n const delta = curFrame - (state.startFramePos ?? curFrame);\n const origTracks = state.origTracks ?? o.tracks;\n const sel = state.moveSelection ?? [];\n if (state.graphMove) {\n return moveSelectedKeyframesGraph(\n origTracks,\n sel,\n delta,\n point.y - state.startY,\n o.trackHeight,\n o.expandedHeight,\n o.snap,\n o.startFrame,\n o.endFrame\n );\n }\n return moveSelectedKeyframes(\n origTracks,\n sel,\n delta,\n o.snap,\n o.startFrame,\n o.endFrame\n );\n },\n [optsRef]\n );\n\n const tangentOffset = useCallback(\n (state: PointerState, point: Point2D): { x: number; y: number } | null => {\n const o = optsRef.current;\n const ref = state.tangentRef;\n if (!ref) return null;\n const geom = o.trackTops.get(ref.trackId);\n if (!geom) return null;\n const tracks = state.origTracks ?? o.tracks;\n const track = tracks.find(t => t.id === ref.trackId);\n const kf = track?.keyframes.find(k => k.id === ref.keyframeId);\n if (!track || !kf) return null;\n const screenTop =\n o.rulerHeight + o.loopStripHeight + geom.top - o.scrollTop;\n const range = geom.range;\n return {\n x: xToFrame(point.x, o.view, o.size.width) - kf.x,\n y: yToValue(point.y, range, screenTop, geom.height) - kf.y,\n };\n },\n [optsRef]\n );\n\n const onPointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>): void => {\n const o = optsRef.current;\n const container = o.containerRef.current;\n if (!container) return;\n const point = localPoint(e, container);\n const view = o.view;\n const width = o.size.width;\n\n if (e.button === 1) {\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'pan',\n startX: point.x,\n startY: point.y,\n startView: view,\n };\n o.store.setDrag({ kind: 'pan', marquee: null });\n return;\n }\n if (e.button !== 0) return;\n\n const hit = hitAt(point);\n\n if (\n hit.kind === 'loop-start' ||\n hit.kind === 'loop-end' ||\n hit.kind === 'loop-body'\n ) {\n const region = o.loopRegion;\n if (!region) return;\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'loop',\n startX: point.x,\n startY: point.y,\n loopWhich:\n hit.kind === 'loop-start'\n ? 'start'\n : hit.kind === 'loop-end'\n ? 'end'\n : 'body',\n startLoop: region,\n };\n o.store.setDrag({ kind: 'scrub', marquee: null });\n return;\n }\n\n // Alt+drag on the ruler creates / narrows the loop region — works\n // even when looping is currently off (the drag flips `loop` to a\n // sub-range object via `setLoop`). Gated on the ruler band so it still\n // fires when the drag starts right on the playhead line.\n if (\n e.altKey &&\n point.y < o.rulerHeight &&\n (hit.kind === 'ruler' || hit.kind === 'playhead')\n ) {\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n const anchorFrame = xToFrame(point.x, view, width);\n const anchor = clamp(anchorFrame, o.startFrame, o.endFrame);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'loop',\n startX: point.x,\n startY: point.y,\n loopWhich: 'create',\n startLoop: { startFrame: anchor, endFrame: anchor },\n };\n o.store.setDrag({ kind: 'scrub', marquee: null });\n o.setLoop({ startFrame: anchor, endFrame: anchor });\n return;\n }\n\n // Plain drag on the dedicated loop strip = loop create (no Alt needed).\n if (hit.kind === 'loop-strip') {\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n const anchorFrame = xToFrame(point.x, view, width);\n const anchor = clamp(anchorFrame, o.startFrame, o.endFrame);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'loop',\n startX: point.x,\n startY: point.y,\n loopWhich: 'create',\n startLoop: { startFrame: anchor, endFrame: anchor },\n };\n o.store.setDrag({ kind: 'scrub', marquee: null });\n o.setLoop({ startFrame: anchor, endFrame: anchor });\n return;\n }\n\n if (hit.kind === 'ruler' || hit.kind === 'playhead') {\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'scrub',\n startX: point.x,\n startY: point.y,\n };\n o.store.setDrag({ kind: 'scrub', marquee: null });\n o.seekLive(xToFrame(point.x, view, width));\n return;\n }\n\n if (hit.kind === 'tangent') {\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'tangent',\n startX: point.x,\n startY: point.y,\n origTracks: o.tracks,\n tangentRef: { trackId: hit.trackId, keyframeId: hit.keyframeId },\n tangentWhich: hit.which,\n };\n o.store.setDrag({ kind: 'move', marquee: null });\n return;\n }\n\n if (hit.kind === 'group') {\n const groups = o.groups;\n o.setGroups(\n groups.map(g =>\n g.id === hit.groupId ? { ...g, collapsed: !g.collapsed } : g\n )\n );\n return;\n }\n\n if (hit.kind === 'keyframe') {\n const additive = e.shiftKey || e.ctrlKey || e.metaKey;\n const ref = { trackId: hit.trackId, keyframeId: hit.keyframeId };\n const sel = nextSelection(o.selection, ref, additive);\n o.setSelection(sel);\n if (o.editable) {\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'move',\n startX: point.x,\n startY: point.y,\n origTracks: o.tracks,\n moveSelection: sel,\n graphMove: o.trackTops.get(hit.trackId)?.graph ?? false,\n startFramePos: xToFrame(point.x, view, width),\n };\n o.store.setDrag({ kind: 'move', marquee: null });\n }\n return;\n }\n\n // empty / outside → marquee box-select\n const additive = e.shiftKey || e.ctrlKey || e.metaKey;\n e.preventDefault();\n container.setPointerCapture(e.pointerId);\n stateRef.current = {\n pointerId: e.pointerId,\n mode: 'marquee',\n startX: point.x,\n startY: point.y,\n additive,\n };\n o.store.setDrag({\n kind: 'marquee',\n marquee: { x: point.x, y: point.y, width: 0, height: 0 },\n });\n },\n [optsRef, hitAt]\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent<HTMLDivElement>): void => {\n const o = optsRef.current;\n const container = o.containerRef.current;\n if (!container) return;\n const point = localPoint(e, container);\n const state = stateRef.current;\n\n // Hover-tracking: only when no active drag (so the diamond / circle can\n // grow under the cursor as a \"grabbable\" affordance; playhead glows\n // when the cursor is over the line or its head).\n if (!state) {\n const hit = hitAt(point);\n if (hit.kind === 'keyframe') {\n o.store.setHover({\n trackId: hit.trackId,\n keyframeId: hit.keyframeId,\n });\n } else {\n o.store.setHover(null);\n }\n o.store.setHoverPlayhead(hit.kind === 'playhead');\n o.store.setHoverLoop(\n hit.kind === 'loop-start'\n ? 'start'\n : hit.kind === 'loop-end'\n ? 'end'\n : hit.kind === 'loop-body'\n ? 'body'\n : null\n );\n }\n\n if (state?.pointerId !== e.pointerId) return;\n const view = o.view;\n const width = o.size.width;\n\n if (state.mode === 'loop') {\n const frame = xToFrame(point.x, view, width);\n const sl = state.startLoop;\n if (sl && state.loopWhich === 'create') {\n const cur = clamp(frame, o.startFrame, o.endFrame);\n o.setLoop({\n startFrame: Math.min(sl.startFrame, cur),\n endFrame: Math.max(sl.startFrame, cur),\n });\n } else if (sl && state.loopWhich === 'start') {\n o.setLoop({\n startFrame: clamp(frame, o.startFrame, sl.endFrame),\n endFrame: sl.endFrame,\n });\n } else if (sl && state.loopWhich === 'end') {\n o.setLoop({\n startFrame: sl.startFrame,\n endFrame: clamp(frame, sl.startFrame, o.endFrame),\n });\n } else if (sl) {\n const span = sl.endFrame - sl.startFrame;\n const delta = frame - xToFrame(state.startX, view, width);\n const ns = clamp(\n sl.startFrame + delta,\n o.startFrame,\n o.endFrame - span\n );\n o.setLoop({ startFrame: ns, endFrame: ns + span });\n }\n return;\n }\n if (state.mode === 'scrub') {\n o.seekLive(xToFrame(point.x, view, width));\n return;\n }\n if (state.mode === 'pan') {\n const startView = state.startView ?? view;\n const span = startView.endFrame - startView.startFrame;\n const dxFrames = ((point.x - state.startX) / Math.max(1, width)) * span;\n o.setView({\n startFrame: startView.startFrame - dxFrames,\n endFrame: startView.endFrame - dxFrames,\n });\n return;\n }\n if (state.mode === 'tangent') {\n const offset = tangentOffset(state, point);\n if (offset && state.tangentRef && state.tangentWhich) {\n o.setTracks(\n setKeyframeTangent(\n state.origTracks ?? o.tracks,\n state.tangentRef,\n state.tangentWhich,\n offset\n )\n );\n }\n return;\n }\n if (state.mode === 'move') {\n o.setTracks(moveFromState(state, point));\n return;\n }\n // Only 'marquee' remains after the early returns above.\n o.store.setDrag({\n kind: 'marquee',\n marquee: {\n x: Math.min(state.startX, point.x),\n y: Math.min(state.startY, point.y),\n width: Math.abs(point.x - state.startX),\n height: Math.abs(point.y - state.startY),\n },\n });\n },\n [optsRef, hitAt, tangentOffset, moveFromState]\n );\n\n const onPointerLeave = useCallback((): void => {\n const o = optsRef.current;\n o.store.setHover(null);\n o.store.setHoverPlayhead(false);\n o.store.setHoverLoop(null);\n }, [optsRef]);\n\n const endGesture = useCallback(\n (e: React.PointerEvent<HTMLDivElement>, cancelled: boolean): void => {\n const o = optsRef.current;\n const state = stateRef.current;\n if (state?.pointerId !== e.pointerId) return;\n const container = o.containerRef.current;\n if (container?.hasPointerCapture(e.pointerId)) {\n container.releasePointerCapture(e.pointerId);\n }\n const point = localPoint(e, container);\n const view = o.view;\n const size = o.size;\n\n if (!cancelled) {\n if (state.mode === 'loop' && state.loopWhich === 'create') {\n // Bare Alt-click (no drag) on the ruler: don't leave a zero-width\n // loop hanging around — clear it.\n if (Math.abs(point.x - state.startX) < CLICK_THRESHOLD_PX) {\n o.setLoop(false);\n }\n } else if (state.mode === 'scrub') {\n o.seekCommit(xToFrame(point.x, view, size.width));\n } else if (state.mode === 'move') {\n o.commitTracks(moveFromState(state, point));\n } else if (state.mode === 'tangent') {\n const offset = tangentOffset(state, point);\n if (offset && state.tangentRef && state.tangentWhich) {\n o.commitTracks(\n setKeyframeTangent(\n state.origTracks ?? o.tracks,\n state.tangentRef,\n state.tangentWhich,\n offset\n )\n );\n }\n } else if (state.mode === 'marquee') {\n const rect = {\n x: Math.min(state.startX, point.x),\n y: Math.min(state.startY, point.y),\n width: Math.abs(point.x - state.startX),\n height: Math.abs(point.y - state.startY),\n };\n if (\n rect.width < CLICK_THRESHOLD_PX &&\n rect.height < CLICK_THRESHOLD_PX\n ) {\n if (!state.additive) o.setSelection([]);\n } else {\n const found = keyframesInRect(\n {\n x: rect.x,\n y: rect.y - o.rulerHeight - o.loopStripHeight + o.scrollTop,\n width: rect.width,\n height: rect.height,\n },\n view,\n size,\n o.rows\n );\n const base = state.additive ? o.selection : [];\n o.setSelection(mergeSelection(base, found));\n }\n }\n }\n\n o.store.setDrag({ kind: 'none', marquee: null });\n stateRef.current = null;\n },\n [optsRef, moveFromState, tangentOffset]\n );\n\n const onPointerUp = useCallback(\n (e: React.PointerEvent<HTMLDivElement>): void => {\n endGesture(e, false);\n },\n [endGesture]\n );\n const onPointerCancel = useCallback(\n (e: React.PointerEvent<HTMLDivElement>): void => {\n endGesture(e, true);\n },\n [endGesture]\n );\n\n const onDoubleClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>): void => {\n const o = optsRef.current;\n if (!o.editable || !o.allowAddKeyframe) return;\n const container = o.containerRef.current;\n if (!container) return;\n const point = localPoint(e, container);\n const hit = hitAt(point);\n if (hit.kind !== 'empty') return;\n const track = o.tracks.find(t => t.id === hit.trackId);\n if (!track || track.locked) return;\n const view = o.view;\n const width = o.size.width;\n const frame = clamp(\n snapFrame(xToFrame(point.x, view, width), o.snap),\n o.startFrame,\n o.endFrame\n );\n const value = valueAtPointer({\n pointerY: point.y,\n frame,\n track,\n geometry: o.trackTops.get(track.id),\n rulerHeight: o.rulerHeight + o.loopStripHeight,\n scrollTop: o.scrollTop,\n });\n const next = addKeyframe(o.tracks, track.id, makeKeyframe(frame, value));\n o.setTracks(next);\n o.commitTracks(next);\n },\n [optsRef, hitAt]\n );\n\n const onWheel = useCallback(\n (e: WheelEvent): void => {\n const o = optsRef.current;\n const container = o.containerRef.current;\n if (!container) return;\n e.preventDefault();\n const view = o.view;\n const width = Math.max(1, o.size.width);\n const span = view.endFrame - view.startFrame;\n\n if (e.shiftKey || Math.abs(e.deltaX) > Math.abs(e.deltaY)) {\n const delta = e.shiftKey ? e.deltaY : e.deltaX;\n const panFrames = (delta / width) * span;\n o.setView({\n startFrame: view.startFrame + panFrames,\n endFrame: view.endFrame + panFrames,\n });\n return;\n }\n\n // Plain vertical wheel scrolls tracks when they overflow; Ctrl/Cmd zooms.\n if (!e.ctrlKey && !e.metaKey && o.maxScrollTop > 0) {\n o.scrollBy(e.deltaY);\n return;\n }\n\n const rangeSpan = Math.max(1, o.endFrame - o.startFrame);\n const maxSpan = o.maxFramesVisible ?? rangeSpan;\n const factor = Math.exp(e.deltaY * 0.0015);\n const newSpan = clamp(span * factor, o.minFramesVisible, maxSpan);\n const point = localPoint(e, container);\n const frac = point.x / width;\n const cursorFrame = view.startFrame + frac * span;\n const newStart = cursorFrame - frac * newSpan;\n o.setView({\n startFrame: newStart,\n endFrame: newStart + newSpan,\n });\n },\n [optsRef]\n );\n\n const onKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>): void => {\n const o = optsRef.current;\n switch (e.key) {\n case 'ArrowLeft':\n case 'ArrowRight': {\n e.preventDefault();\n const dir = e.key === 'ArrowLeft' ? -1 : 1;\n const step = (e.shiftKey ? KEYBOARD_LARGE_STEP : 1) * dir;\n o.seekCommit(o.frame + step);\n return;\n }\n case 'Home':\n e.preventDefault();\n o.seekCommit(o.startFrame);\n return;\n case 'End':\n e.preventDefault();\n o.seekCommit(o.endFrame);\n return;\n case 'Delete':\n case 'Backspace': {\n if (!o.editable || !o.allowDeleteKeyframe) return;\n const sel = o.selection;\n if (sel.length === 0) return;\n e.preventDefault();\n const next = removeSelectedKeyframes(o.tracks, sel);\n o.setTracks(next);\n o.commitTracks(next);\n o.setSelection([]);\n return;\n }\n case 'c':\n case 'C': {\n if (!(e.ctrlKey || e.metaKey)) return;\n e.preventDefault();\n clipboardRef.current = copySelectedKeyframes(o.tracks, o.selection);\n return;\n }\n case 'v':\n case 'V': {\n if (!(e.ctrlKey || e.metaKey) || !o.editable) return;\n const clip = clipboardRef.current;\n if (!clip || clip.entries.length === 0) return;\n e.preventDefault();\n const pasted = pasteKeyframes(o.tracks, clip, o.frame);\n o.setTracks(pasted.tracks);\n o.commitTracks(pasted.tracks);\n o.setSelection(pasted.refs);\n return;\n }\n default:\n return;\n }\n },\n [optsRef]\n );\n\n const handlers = useMemo(\n () => ({\n onPointerDown,\n onPointerMove,\n onPointerUp,\n onPointerCancel,\n onPointerLeave,\n onDoubleClick,\n onKeyDown,\n }),\n [\n onPointerDown,\n onPointerMove,\n onPointerUp,\n onPointerCancel,\n onPointerLeave,\n onDoubleClick,\n onKeyDown,\n ]\n );\n\n return { handlers, onWheel };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAoCA;AACA;AA+EA;AAIE;;AACA;;AAEF;AAEA;AAKE;;AAEE;AACE;AACA;;;AAGN;AAEA;AAIE;AACA;AACE;AAAwC;;AAE1C;AACF;AAEA;;;;;;;;;;;AAWG;AACG;;;;AAMJ;AAEA;AACA;AAEA;AAEI;AACA;;;;;;;;;;;;AAYE;AAED;AACH;;AAME;AACA;AACA;AACA;;;AAGA;AACA;AACE;;;AAoBJ;;AAME;AACA;AACA;AAAU;AACV;AACA;AAAW;;AAEX;;AAEA;AAAmB;AACnB;AAEA;;;AAGE;;AAEJ;AAIF;AAEI;AACA;AACA;;;AAEA;AACA;AAEA;;AAEE;;;AAGE;;;AAGA;;AAEF;;;AAGF;;AAEA;AAEA;;AAGE;AAEA;AACA;;;AAEA;;;AAGE;;;AAGA;AAEI;AACA;AACE;AACA;AACN;;AAEF;;;;;;;;AAUA;AACA;;AAGA;AACA;AACA;;;AAGE;;;AAGA;;;AAGF;AACA;;;;AAKF;;AAEE;AACA;AACA;;;AAGE;;;AAGA;;;AAGF;AACA;;;AAIF;;AAEE;;;AAGE;;;;AAIF;AACA;;;AAIF;;AAEE;;;AAGE;;;;AAIA;;;AAGF;;;AAIF;AACE;AACA;;;AAQF;AACE;AACA;AACA;AACA;AACA;;AAEE;;;AAGE;;;;AAIA;AACA;;;AAGF;;;;;AAMJ;;AAEA;;;AAGE;;;;;AAKF;AACE;;AAED;AACH;AAIF;AAEI;AACA;AACA;;;AAEA;;;;;AAME;AACA;AACE;;;AAGC;;;AAED;;;;AAKE;AACA;AACE;AACA;AACE;;;AAKV;;AACA;AACA;AAEA;AACE;AACA;;AAEE;;;;AAIC;;;;AAGC;;AAED;;;;;AAIC;AACD;;;;AAGD;;AAMA;;;;AAIJ;AACE;;;AAGF;AACE;;;;AAIE;AACA;AACD;;;AAGH;;;;;;;AAcA;;;;;AAKA;AACE;AACA;AACE;AACA;AACA;AACA;AACD;AACF;;AAKL;AACE;AACA;AACA;AACA;AACF;;AAII;AACA;AACA;;AACA;;AAEE;;;AAGF;AACA;;AAGE;;;AAGE;AACE;;;AAEG;AACL;;AACK;;;AAEA;;;;;;AAYA;AACL;AACE;AACA;AACA;AACA;;AAEF;AAEE;;AAEqB;;;;;AAKjB;;;;AAQJ;;;;;AAMN;AACA;;AAKJ;AAEI;AACF;AAGF;AAEI;AACF;AAIF;AAEI;;;AAEA;AACA;;;AAEA;AACA;;;AAEA;;AACA;AACA;AACA;;;;;;AAUE;;AAED;AACD;AACA;AACA;AACF;AAIF;AAEI;AACA;AACA;;;AAEA;AACA;;;AAIE;;;AAGE;AACA;AACD;;;;AAKH;AACE;;;AAIF;AACA;AACA;AACA;;AAEA;;AAEA;;AAEE;;AAED;AACH;AAIF;AAEI;AACA;AACE;;;AAGE;AACA;;;;AAIF;;AAEE;;AAEF;;AAEE;;AAEF;;;;AAGE;AACA;;;;AAGA;AACA;AACA;;;AAGF;;;;;AAIE;;;AAGF;;AAEE;;AACA;;;;AAGA;AACA;AACA;AACA;;;AAGF;;;AAGJ;AAIF;;;;;;;;AASG;;;;;;;;AASA;AAGH;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTimelinePlayback.js","sources":["
|
|
1
|
+
{"version":3,"file":"useTimelinePlayback.js","sources":["src/components/editor/Timeline/useTimelinePlayback.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useEffectEvent, useRef } from 'react';\n\nimport { resolveLoop } from './timelineCoords';\n\nimport type { TimelineLoop } from './Timeline.types';\n\nexport interface PlaybackRange {\n startFrame: number;\n endFrame: number;\n}\n\nexport interface AdvanceResult {\n frame: number;\n /** True when a non-looping playback has reached the end. */\n ended: boolean;\n}\n\n/**\n * Advance a playhead by `dtSeconds` of wall-clock time at `fps`. Forward only.\n * With a `loop` region the playhead wraps within it; without one it stops at\n * `range.endFrame`. Pure — the rAF hook below drives it.\n */\nexport function advancePlayback(\n current: number,\n dtSeconds: number,\n fps: number,\n range: PlaybackRange,\n loop: PlaybackRange | null\n): AdvanceResult {\n const next = current + Math.max(0, dtSeconds) * Math.max(0, fps);\n if (loop) {\n if (next < loop.endFrame) return { frame: next, ended: false };\n const span = Math.max(1e-6, loop.endFrame - loop.startFrame);\n return {\n frame: loop.startFrame + ((next - loop.startFrame) % span),\n ended: false,\n };\n }\n if (next >= range.endFrame) return { frame: range.endFrame, ended: true };\n return { frame: next, ended: false };\n}\n\ninterface UseTimelinePlaybackOptions {\n playing: boolean;\n frame: number;\n fps: number;\n startFrame: number;\n endFrame: number;\n loop: TimelineLoop | undefined;\n /** Push the advanced (float) frame — the component clamps + fires onFrameChange. */\n onAdvance: (frame: number) => void;\n /** Called once when non-looping playback reaches the end. */\n onEnd: () => void;\n}\n\n/**\n * Drives the playhead while `playing` is true: a rAF loop advancing the frame\n * in real time at `fps`. Keeps its own float accumulator (independent of the\n * snapped/committed frame) and resyncs if the frame is changed externally\n * (e.g. the user scrubs mid-playback).\n */\nexport function useTimelinePlayback(opts: UseTimelinePlaybackOptions): void {\n const accRef = useRef(opts.frame);\n const lastEmittedRef = useRef(opts.frame);\n\n // Canonical `useEffectEvent` use case: `tick` is called *only* from inside\n // the rAF loop below (never attached to JSX or passed out), so it may read\n // the latest props/state without re-arming the loop. This is the React\n // \"timer with latest values\" pattern — keep it here; do not convert it to a\n // handler that escapes the effect.\n const tick = useEffectEvent((dt: number): boolean => {\n // External scrub during playback → resync the accumulator.\n if (opts.frame !== lastEmittedRef.current) {\n accRef.current = opts.frame;\n }\n const range: PlaybackRange = {\n startFrame: opts.startFrame,\n endFrame: opts.endFrame,\n };\n const region = resolveLoop(opts.loop, range.startFrame, range.endFrame);\n const result = advancePlayback(accRef.current, dt, opts.fps, range, region);\n accRef.current = result.frame;\n lastEmittedRef.current = result.frame;\n opts.onAdvance(result.frame);\n if (result.ended) {\n opts.onEnd();\n return true;\n }\n return false;\n });\n\n useEffect(() => {\n if (!opts.playing || typeof window === 'undefined') return;\n accRef.current = opts.frame;\n lastEmittedRef.current = opts.frame;\n let raf = 0;\n let last = performance.now();\n const loop = (now: number): void => {\n const dt = (now - last) / 1000;\n last = now;\n const ended = tick(dt);\n if (!ended) raf = requestAnimationFrame(loop);\n };\n raf = requestAnimationFrame(loop);\n return () => {\n cancelAnimationFrame(raf);\n };\n // Only `playing` drives the rAF lifecycle. `opts.frame` is read solely to\n // seed the accumulator at playback start; including it would restart the\n // loop on every emitted frame, so it is intentionally excluded.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [opts.playing]);\n}\n"],"names":[],"mappings":";;;;AAmBA;;;;AAIG;AACG;;;AASF;;AACA;;AAEE;AACA;;;AAGJ;;;AAEF;AAeA;;;;;AAKG;AACG;;;;;;;;AASJ;;;AAGI;;AAEF;;;;AAIA;AACA;AACA;AACA;AACA;AACA;;AAEE;;AAEF;AACF;;;;AAIE;AACA;;AAEA;AACA;;;AAGE;AACA;AAAY;AACd;AACA;AACA;;AAEA;;;;;AAKF;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransformControl.js","sources":["../../../../../../src/components/editor/TransformControl/TransformControl.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;AA4BA;AAEA;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;AACA;;AAGF;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACE;AACA;AACA;AACD;AAED;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;AACI;AAyBL;AACE;;;AAGA;AACD;AAED;AAEI;AACA;AACA;AACA;AACD;AAEH;AACE;AACA;AACA;AACA;AACD;AAED;AACA;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;;AAOA;AAEI;AACF;AAIF;AAEI;AACF;;AAME;;;AAGE;;;AAEA;;AAEJ;AAIF;;AAEA;AAEA;;;;AAIE;;AAMF;AACE;AACF;AAEA;AACE;AACF;AAEA;AACE;AACF;;AAIA;AAiIF;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"TransformControl.js","sources":["src/components/editor/TransformControl/TransformControl.tsx"],"sourcesContent":["'use client';\n\nimport React, { useCallback, useMemo } from 'react';\n\nimport { Select } from '@/components/controls/Select';\nimport { VectorInput } from '@/components/controls/VectorInput';\nimport { PropertyRow } from '@/components/editor/PropertyInspector/PropertyRow';\nimport { LockIcon } from '@/components/Icons/LockIcon';\nimport { UnlockIcon } from '@/components/Icons/UnlockIcon';\nimport { useControlledState } from '@/hooks/useControlledState';\nimport { cx } from '@/utils/cx';\n\nimport {\n lockButtonRecipe,\n lockSpacerRecipe,\n rowInner,\n rowVectorWrapper,\n transformRoot,\n} from './TransformControl.css';\n\nimport type {\n CoordinateSpace,\n CoordinateSpaceOption,\n TransformControlProps,\n TransformValue,\n Vec3,\n} from './TransformControl.types';\n\n// --- Defaults ---\n\nconst DEFAULT_TRANSFORM: TransformValue = {\n position: { x: 0, y: 0, z: 0 },\n rotation: { x: 0, y: 0, z: 0 },\n scale: { x: 1, y: 1, z: 1 },\n};\n\nconst DEFAULT_COORDINATE_SPACE_OPTIONS: CoordinateSpaceOption[] = [\n { value: 'local', label: 'Local' },\n { value: 'world', label: 'World' },\n { value: 'parent', label: 'Parent' },\n];\n\nconst DEFAULT_LABELS = {\n position: 'Position',\n rotation: 'Rotation',\n scale: 'Scale',\n coordinateSpace: 'Space',\n} as const;\n\nconst DEFAULT_PRECISION = { position: 3, rotation: 1, scale: 3 } as const;\nconst DEFAULT_STEP = { position: 0.1, rotation: 1, scale: 0.01 } as const;\nconst DEFAULT_UNITS = { position: 'm', rotation: '°', scale: '' } as const;\n\n// Narrow label column so the three axis inputs (with units) have room\n// to render without clipping inside typical 320–400 px property panels.\nconst ROW_SPLIT_RATIO: [number, number] = [25, 75];\n\n// --- Helpers ---\n\nconst vecToArray = (v: Vec3): number[] => [v.x, v.y, v.z];\n\nconst arrayToVec = (arr: number[]): Vec3 => ({\n x: arr[0] ?? 0,\n y: arr[1] ?? 0,\n z: arr[2] ?? 0,\n});\n\n// --- Component ---\n\n/**\n * The canonical position / rotation / scale property control for 3D\n * editor interfaces. Composes `VectorInput` (per row) with `Select`\n * (coordinate space) and `PropertyRow` (layout) into one high-level\n * component, mirroring the transform widget found in Blender, Unity and\n * Unreal.\n *\n * Three independent atoms — `value`, `coordinateSpace`, `linkedScale` —\n * each support controlled and uncontrolled usage.\n *\n * Changing the coordinate-space dropdown does NOT transform the numeric\n * values. The component is purely a UI surface; the consumer's editor\n * logic is responsible for re-projecting values when the space changes.\n *\n * The component intentionally renders no `PropertySection` wrapper — slot\n * it inside an existing section in your panel.\n *\n * @example\n * ```tsx\n * <PropertySection title=\"Transform\">\n * <TransformControl\n * value={transform}\n * onChange={setTransform}\n * showReset\n * />\n * </PropertySection>\n * ```\n */\nexport const TransformControl: React.FC<TransformControlProps> = ({\n value: valueProp,\n defaultValue,\n onChange,\n coordinateSpace: coordinateSpaceProp,\n defaultCoordinateSpace,\n onCoordinateSpaceChange,\n coordinateSpaceOptions,\n linkedScale: linkedScaleProp,\n defaultLinkedScale,\n onLinkedScaleChange,\n show,\n labels,\n precision,\n step,\n units,\n size = 'sm',\n disabled = false,\n showReset = false,\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const [value, setValue] = useControlledState<TransformValue>({\n value: valueProp,\n defaultValue,\n onChange,\n fallback: DEFAULT_TRANSFORM,\n });\n\n const [coordinateSpace, setCoordinateSpace] =\n useControlledState<CoordinateSpace>({\n value: coordinateSpaceProp,\n defaultValue: defaultCoordinateSpace,\n onChange: onCoordinateSpaceChange,\n fallback: 'local',\n });\n\n const [linkedScale, setLinkedScale] = useControlledState<boolean>({\n value: linkedScaleProp,\n defaultValue: defaultLinkedScale,\n onChange: onLinkedScaleChange,\n fallback: true,\n });\n\n const showPosition = show?.position !== false;\n const showRotation = show?.rotation !== false;\n const showScale = show?.scale !== false;\n const showCoordinateSpace = showPosition || showRotation;\n\n const resolvedLabels = {\n position: labels?.position ?? DEFAULT_LABELS.position,\n rotation: labels?.rotation ?? DEFAULT_LABELS.rotation,\n scale: labels?.scale ?? DEFAULT_LABELS.scale,\n coordinateSpace: labels?.coordinateSpace ?? DEFAULT_LABELS.coordinateSpace,\n };\n\n const resolvedPrecision = {\n position: precision?.position ?? DEFAULT_PRECISION.position,\n rotation: precision?.rotation ?? DEFAULT_PRECISION.rotation,\n scale: precision?.scale ?? DEFAULT_PRECISION.scale,\n };\n\n const resolvedStep = {\n position: step?.position ?? DEFAULT_STEP.position,\n rotation: step?.rotation ?? DEFAULT_STEP.rotation,\n scale: step?.scale ?? DEFAULT_STEP.scale,\n };\n\n const resolvedUnits = {\n position: units?.position ?? DEFAULT_UNITS.position,\n rotation: units?.rotation ?? DEFAULT_UNITS.rotation,\n scale: units?.scale ?? DEFAULT_UNITS.scale,\n };\n\n const resolvedSpaceOptions = useMemo(\n () => coordinateSpaceOptions ?? DEFAULT_COORDINATE_SPACE_OPTIONS,\n [coordinateSpaceOptions]\n );\n\n // --- Per-row change handlers ---\n\n const handlePositionChange = useCallback(\n (next: number[]) => {\n setValue(prev => ({ ...prev, position: arrayToVec(next) }));\n },\n [setValue]\n );\n\n const handleRotationChange = useCallback(\n (next: number[]) => {\n setValue(prev => ({ ...prev, rotation: arrayToVec(next) }));\n },\n [setValue]\n );\n\n const handleScaleChange = useCallback(\n (next: number[], axisIndex: number) => {\n const nextScale = arrayToVec(next);\n if (linkedScale) {\n const v = next[axisIndex] ?? 0;\n setValue(prev => ({ ...prev, scale: { x: v, y: v, z: v } }));\n } else {\n setValue(prev => ({ ...prev, scale: nextScale }));\n }\n },\n [linkedScale, setValue]\n );\n\n const handleLockToggle = useCallback(() => {\n setLinkedScale(prev => !prev);\n }, [setLinkedScale]);\n\n const handleSpaceChange = useCallback(\n (next: string | null) => {\n if (next == null) return;\n setCoordinateSpace(next);\n },\n [setCoordinateSpace]\n );\n\n // --- Reset handlers ---\n\n const handlePositionReset = useCallback(() => {\n setValue(prev => ({ ...prev, position: { x: 0, y: 0, z: 0 } }));\n }, [setValue]);\n\n const handleRotationReset = useCallback(() => {\n setValue(prev => ({ ...prev, rotation: { x: 0, y: 0, z: 0 } }));\n }, [setValue]);\n\n const handleScaleReset = useCallback(() => {\n setValue(prev => ({ ...prev, scale: { x: 1, y: 1, z: 1 } }));\n }, [setValue]);\n\n // --- Render ---\n\n return (\n <div\n ref={ref}\n className={cx(transformRoot, className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n {showCoordinateSpace && (\n <PropertyRow\n label={resolvedLabels.coordinateSpace}\n size={size}\n disabled={disabled}\n splitRatio={ROW_SPLIT_RATIO}\n >\n <Select\n value={coordinateSpace}\n options={resolvedSpaceOptions}\n onChange={handleSpaceChange}\n size={size}\n disabled={disabled}\n aria-label={resolvedLabels.coordinateSpace}\n />\n </PropertyRow>\n )}\n\n {showPosition && (\n <PropertyRow\n label={resolvedLabels.position}\n size={size}\n disabled={disabled}\n splitRatio={ROW_SPLIT_RATIO}\n onReset={showReset ? handlePositionReset : undefined}\n >\n <div className={rowInner}>\n <span aria-hidden=\"true\" className={lockSpacerRecipe({ size })} />\n <div className={rowVectorWrapper}>\n <VectorInput\n value={vecToArray(value.position)}\n onChange={handlePositionChange}\n dimension={3}\n labelPreset=\"xyz\"\n colorPreset=\"spatial\"\n precision={resolvedPrecision.position}\n step={resolvedStep.position}\n unit={resolvedUnits.position}\n size={size}\n disabled={disabled}\n aria-label={resolvedLabels.position}\n />\n </div>\n </div>\n </PropertyRow>\n )}\n\n {showRotation && (\n <PropertyRow\n label={resolvedLabels.rotation}\n size={size}\n disabled={disabled}\n splitRatio={ROW_SPLIT_RATIO}\n onReset={showReset ? handleRotationReset : undefined}\n >\n <div className={rowInner}>\n <span aria-hidden=\"true\" className={lockSpacerRecipe({ size })} />\n <div className={rowVectorWrapper}>\n <VectorInput\n value={vecToArray(value.rotation)}\n onChange={handleRotationChange}\n dimension={3}\n labelPreset=\"xyz\"\n colorPreset=\"spatial\"\n precision={resolvedPrecision.rotation}\n step={resolvedStep.rotation}\n unit={resolvedUnits.rotation}\n size={size}\n disabled={disabled}\n aria-label={resolvedLabels.rotation}\n />\n </div>\n </div>\n </PropertyRow>\n )}\n\n {showScale && (\n <PropertyRow\n label={resolvedLabels.scale}\n size={size}\n disabled={disabled}\n splitRatio={ROW_SPLIT_RATIO}\n onReset={showReset ? handleScaleReset : undefined}\n >\n <div className={rowInner}>\n <button\n type=\"button\"\n className={lockButtonRecipe({ active: linkedScale, size })}\n onClick={handleLockToggle}\n disabled={disabled}\n aria-pressed={linkedScale}\n aria-label={linkedScale ? 'Unlink scale axes' : 'Link scale axes'}\n title={linkedScale ? 'Linked scale' : 'Unlinked scale'}\n data-testid=\"transform-scale-lock\"\n >\n {linkedScale ? (\n <LockIcon size={size} decorative />\n ) : (\n <UnlockIcon size={size} decorative />\n )}\n </button>\n <div className={rowVectorWrapper}>\n <VectorInput\n value={vecToArray(value.scale)}\n onChange={handleScaleChange}\n dimension={3}\n labelPreset=\"xyz\"\n colorPreset=\"spatial\"\n precision={resolvedPrecision.scale}\n step={resolvedStep.scale}\n unit={resolvedUnits.scale}\n size={size}\n disabled={disabled}\n aria-label={resolvedLabels.scale}\n />\n </div>\n </div>\n </PropertyRow>\n )}\n </div>\n );\n};\n\nTransformControl.displayName = 'TransformControl';\n"],"names":[],"mappings":";;;;;;;;;;;;AA4BA;AAEA;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;AACA;;AAGF;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACE;AACA;AACA;AACD;AAED;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;AACI;AAyBL;AACE;;;AAGA;AACD;AAED;AAEI;AACA;AACA;AACA;AACD;AAEH;AACE;AACA;AACA;AACA;AACD;AAED;AACA;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;;AAOA;AAEI;AACF;AAIF;AAEI;AACF;;AAME;;;AAGE;;;AAEA;;AAEJ;AAIF;;AAEA;AAEA;;;;AAIE;;AAMF;AACE;AACF;AAEA;AACE;AACF;AAEA;AACE;AACF;;AAIA;AAiIF;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewportGizmo.js","sources":["
|
|
1
|
+
{"version":3,"file":"ViewportGizmo.js","sources":["src/components/editor/ViewportGizmo/ViewportGizmo.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, { useRef, useMemo } from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { useGizmoInteraction } from './useGizmoInteraction';\nimport { useGizmoRenderer, AXIS_COLORS } from './useGizmoRenderer';\nimport {\n gizmoWrapperRecipe,\n gizmoCanvasStyle,\n gizmoDiameterVar,\n ariaLiveRegionStyle,\n} from './ViewportGizmo.css';\n\nimport type { ViewportGizmoProps } from './ViewportGizmo.types';\n\nconst DEFAULT_LABELS: Record<string, string> = { x: 'X', y: 'Y', z: 'Z' };\nconst DEFAULT_VISIBLE: Record<string, boolean> = { x: true, y: true, z: true };\n\nexport const ViewportGizmo: React.FC<ViewportGizmoProps> = ({\n orientation,\n upAxis = 'y-up',\n axisColorPreset = 'blender',\n axisConfig,\n showLabels = true,\n showNegativeAxes = true,\n showOrbitRing = true,\n showOriginHandle = true,\n background = 'subtle',\n interactionMode = 'full',\n orbitSpeed = 1,\n constrainPitch = true,\n onOrbit,\n onOrbitEnd,\n onSnapToView,\n onAxisClick,\n onOriginClick,\n diameter = 120,\n size = 'md',\n disabled = false,\n className,\n testId,\n id,\n style,\n ...rest\n}) => {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n // Resolve axis colors from preset or custom config\n const axisColors = useMemo((): Record<string, string> => {\n if (axisColorPreset === 'custom' && axisConfig) {\n return {\n x: axisConfig[0].color ?? '#E63946',\n y: axisConfig[1].color ?? '#6AA84F',\n z: axisConfig[2].color ?? '#4A86C8',\n };\n }\n return { ...AXIS_COLORS };\n }, [axisColorPreset, axisConfig]);\n\n const axisLabels = useMemo((): Record<string, string> => {\n if (axisConfig) {\n return {\n x: axisConfig[0].label ?? 'X',\n y: axisConfig[1].label ?? 'Y',\n z: axisConfig[2].label ?? 'Z',\n };\n }\n return DEFAULT_LABELS;\n }, [axisConfig]);\n\n const axisVisible = useMemo((): Record<string, boolean> => {\n if (axisConfig) {\n return {\n x: axisConfig[0].visible ?? true,\n y: axisConfig[1].visible ?? true,\n z: axisConfig[2].visible ?? true,\n };\n }\n return DEFAULT_VISIBLE;\n }, [axisConfig]);\n\n // Interaction\n const interaction = useGizmoInteraction({\n orientation,\n canvasRef,\n diameter,\n upAxis,\n interactionMode,\n orbitSpeed,\n constrainPitch,\n disabled,\n onOrbit,\n onOrbitEnd,\n onSnapToView,\n onAxisClick,\n onOriginClick,\n });\n\n // Renderer\n useGizmoRenderer({\n canvasRef,\n orientation,\n diameter,\n upAxis,\n size,\n axisColors,\n axisLabels,\n axisVisible,\n showLabels,\n showNegativeAxes,\n showOrbitRing,\n showOriginHandle,\n background,\n disabled,\n isDragging: interaction.isDragging,\n hoveredRegion: interaction.hoveredRegion,\n });\n\n // ARIA\n const ariaDescription = `3D orientation gizmo, yaw: ${Math.round(orientation.yaw)}deg, pitch: ${Math.round(orientation.pitch)}deg`;\n\n return (\n <div\n className={cx(gizmoWrapperRecipe({ background, disabled }), className)}\n style={{\n ...style,\n ...assignInlineVars({ [gizmoDiameterVar]: `${diameter}px` }),\n }}\n data-testid={testId}\n id={id}\n {...rest}\n >\n <canvas\n ref={canvasRef}\n className={gizmoCanvasStyle({ disabled })}\n // The canvas is a self-contained interactive widget driven entirely by\n // custom pointer/keyboard handlers, so it is exposed as an ARIA\n // application rather than its implicit canvas role.\n // eslint-disable-next-line jsx-a11y/no-interactive-element-to-noninteractive-role\n role=\"application\"\n aria-label=\"Viewport orientation gizmo\"\n aria-roledescription={ariaDescription}\n tabIndex={disabled ? -1 : 0}\n onPointerDown={interaction.handlers.onPointerDown}\n onPointerMove={interaction.handlers.onPointerMove}\n onPointerUp={interaction.handlers.onPointerUp}\n onKeyDown={interaction.handlers.onKeyDown}\n />\n <div className={ariaLiveRegionStyle} aria-live=\"polite\" role=\"status\">\n {ariaDescription}\n </div>\n </div>\n );\n};\n\nViewportGizmo.displayName = 'ViewportGizmo';\n"],"names":[],"mappings":";;;;;;;;;AAkBA;AACA;AAEO;AA2BL;;AAGA;AACE;;;;;;;AAOA;AACF;AAEA;;;;;;;;AAQE;AACF;AAEA;;;;;;;;AAQE;AACF;;;;;;;;;;;;;;;;AAiBC;;AAGD;;;;;;;;;;;;;;;;AAiBC;;;AAKD;AAIM;;;;;;;AAcA;AAcR;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gizmoMath.js","sources":["../../../../../../src/components/editor/ViewportGizmo/gizmoMath.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gizmoMath.js","sources":["src/components/editor/ViewportGizmo/gizmoMath.ts"],"sourcesContent":["import type {\n GizmoOrientation,\n GizmoPresetView,\n GizmoUpAxis,\n GizmoHitRegion,\n} from './ViewportGizmo.types';\n\n// ─── Vector Type ───\n\n/** 3D vector */\nexport interface Vec3 {\n x: number;\n y: number;\n z: number;\n}\n\n// ─── Constants ───\n\nconst DEG2RAD = Math.PI / 180;\nconst RAD2DEG = 180 / Math.PI;\n\n// ─── Euler → Rotation Matrix ───\n\n/**\n * Build a 3×3 rotation matrix from Euler angles (YXZ intrinsic order).\n * Matches Three.js default Euler order.\n *\n * @param yaw - Y-axis rotation in degrees\n * @param pitch - X-axis rotation in degrees\n * @param roll - Z-axis rotation in degrees\n * @returns [right, up, forward] basis vectors (columns of rotation matrix)\n */\nexport function eulerToRotationMatrix(\n yaw: number,\n pitch: number,\n roll: number = 0\n): [Vec3, Vec3, Vec3] {\n const cy = Math.cos(yaw * DEG2RAD);\n const sy = Math.sin(yaw * DEG2RAD);\n const cp = Math.cos(pitch * DEG2RAD);\n const sp = Math.sin(pitch * DEG2RAD);\n const cr = Math.cos(roll * DEG2RAD);\n const sr = Math.sin(roll * DEG2RAD);\n\n // YXZ rotation: R = Ry * Rx * Rz\n // Column 0 (right vector)\n const r00 = cy * cr + sy * sp * sr;\n const r10 = cp * sr;\n const r20 = -sy * cr + cy * sp * sr;\n\n // Column 1 (up vector)\n const r01 = -cy * sr + sy * sp * cr;\n const r11 = cp * cr;\n const r21 = sy * sr + cy * sp * cr;\n\n // Column 2 (forward vector)\n const r02 = sy * cp;\n const r12 = -sp;\n const r22 = cy * cp;\n\n return [\n { x: r00, y: r10, z: r20 }, // right\n { x: r01, y: r11, z: r21 }, // up\n { x: r02, y: r12, z: r22 }, // forward\n ];\n}\n\n// ─── 3D → 2D Projection ───\n\n/**\n * Project a 3D point to 2D canvas coordinates using orthographic projection.\n *\n * @param point - 3D point to project\n * @param orientation - Camera orientation\n * @param center - Canvas center in pixels\n * @param scale - Pixels per unit\n * @returns Projected position { x, y, depth }\n */\nexport function projectToCanvas(\n point: Vec3,\n orientation: GizmoOrientation,\n center: { x: number; y: number },\n scale: number\n): { x: number; y: number; depth: number } {\n const [right, up, forward] = eulerToRotationMatrix(\n orientation.yaw,\n orientation.pitch,\n orientation.roll ?? 0\n );\n\n // Project point onto camera basis (view space)\n const viewX = point.x * right.x + point.y * right.y + point.z * right.z;\n const viewY = point.x * up.x + point.y * up.y + point.z * up.z;\n const viewZ = point.x * forward.x + point.y * forward.y + point.z * forward.z;\n\n return {\n x: center.x + viewX * scale,\n y: center.y - viewY * scale, // canvas Y is flipped\n depth: viewZ,\n };\n}\n\n/** Projected axis arm data */\nexport interface ProjectedArm {\n axis: 'x' | 'y' | 'z';\n positive: boolean;\n screenX: number;\n screenY: number;\n depth: number;\n}\n\n/**\n * Get all 6 axis arm endpoints projected to 2D, sorted by depth (furthest first).\n */\nexport function projectAxes(\n orientation: GizmoOrientation,\n center: { x: number; y: number },\n armLength: number,\n upAxis: GizmoUpAxis\n): ProjectedArm[] {\n // The drawn direction of each labelled axis depends on the up-axis\n // convention. For z-up (Blender, Unreal, CAD) the world is rotated -90°\n // about X relative to y-up, so the Z arm points up and the Y arm points\n // into the scene. (x, y, z) -> (x, z, -y).\n const axes: Vec3[] =\n upAxis === 'z-up'\n ? [\n { x: 1, y: 0, z: 0 }, // X\n { x: 0, y: 0, z: -1 }, // Y (into the scene)\n { x: 0, y: 1, z: 0 }, // Z (up)\n ]\n : [\n { x: 1, y: 0, z: 0 }, // X\n { x: 0, y: 1, z: 0 }, // Y (up)\n { x: 0, y: 0, z: 1 }, // Z\n ];\n\n const axisNames: Array<'x' | 'y' | 'z'> = ['x', 'y', 'z'];\n const arms: ProjectedArm[] = [];\n\n for (let i = 0; i < 3; i++) {\n const ax = axes[i];\n const name = axisNames[i];\n if (!ax || !name) continue;\n\n // Positive arm\n const pos = projectToCanvas(\n { x: ax.x, y: ax.y, z: ax.z },\n orientation,\n center,\n armLength\n );\n arms.push({\n axis: name,\n positive: true,\n screenX: pos.x,\n screenY: pos.y,\n depth: pos.depth,\n });\n\n // Negative arm\n const neg = projectToCanvas(\n { x: -ax.x, y: -ax.y, z: -ax.z },\n orientation,\n center,\n armLength * 0.4\n );\n arms.push({\n axis: name,\n positive: false,\n screenX: neg.x,\n screenY: neg.y,\n depth: neg.depth,\n });\n }\n\n // Sort by depth — furthest first (painter's algorithm)\n arms.sort((a, b) => a.depth - b.depth);\n\n return arms;\n}\n\n// ─── Hit Testing ───\n\nfunction distPointToSegment(\n px: number,\n py: number,\n x1: number,\n y1: number,\n x2: number,\n y2: number\n): number {\n const dx = x2 - x1;\n const dy = y2 - y1;\n const lenSq = dx * dx + dy * dy;\n if (lenSq === 0) return Math.sqrt((px - x1) ** 2 + (py - y1) ** 2);\n\n let t = ((px - x1) * dx + (py - y1) * dy) / lenSq;\n t = Math.max(0, Math.min(1, t));\n\n const projX = x1 + t * dx;\n const projY = y1 + t * dy;\n return Math.sqrt((px - projX) ** 2 + (py - projY) ** 2);\n}\n\n/**\n * Hit test a screen position against the gizmo geometry.\n */\nexport function gizmoHitTest(\n px: number,\n py: number,\n orientation: GizmoOrientation,\n center: { x: number; y: number },\n armLength: number,\n upAxis: GizmoUpAxis,\n tolerancePx: number = 12,\n originRadius: number = 6\n): GizmoHitRegion {\n // Test origin handle first\n const originDist = Math.sqrt((px - center.x) ** 2 + (py - center.y) ** 2);\n if (originDist <= originRadius + tolerancePx * 0.5) {\n return { type: 'origin', distance: originDist };\n }\n\n const arms = projectAxes(orientation, center, armLength, upAxis);\n let closest: GizmoHitRegion = { type: 'none', distance: Infinity };\n\n for (const arm of arms) {\n // Check tip first (label / axis tip)\n const tipDist = Math.sqrt(\n (px - arm.screenX) ** 2 + (py - arm.screenY) ** 2\n );\n\n if (tipDist <= tolerancePx) {\n const hitType = arm.positive ? 'axis-positive' : 'axis-negative';\n if (tipDist < closest.distance) {\n closest = { type: hitType, axis: arm.axis, distance: tipDist };\n }\n }\n\n // Check arm line segment\n const segDist = distPointToSegment(\n px,\n py,\n center.x,\n center.y,\n arm.screenX,\n arm.screenY\n );\n if (segDist <= tolerancePx * 0.7 && segDist < closest.distance) {\n const hitType = arm.positive ? 'axis-positive' : 'axis-negative';\n closest = { type: hitType, axis: arm.axis, distance: segDist };\n }\n }\n\n // Check orbit ring\n const ringDist = Math.abs(originDist - armLength * 1.1);\n if (ringDist <= tolerancePx * 0.5 && ringDist < closest.distance) {\n return { type: 'ring', distance: ringDist };\n }\n\n return closest;\n}\n\n// ─── Preset Views ───\n\n/**\n * Convert a preset view name to Euler angles.\n */\nexport function presetViewToOrientation(\n view: GizmoPresetView,\n upAxis: GizmoUpAxis\n): GizmoOrientation {\n if (upAxis === 'y-up') {\n switch (view) {\n case 'front':\n return { yaw: 0, pitch: 0 };\n case 'back':\n return { yaw: 180, pitch: 0 };\n case 'right':\n return { yaw: 90, pitch: 0 };\n case 'left':\n return { yaw: -90, pitch: 0 };\n case 'top':\n return { yaw: 0, pitch: 90 };\n case 'bottom':\n return { yaw: 0, pitch: -90 };\n }\n }\n\n // Z-up\n switch (view) {\n case 'front':\n return { yaw: 0, pitch: 0 };\n case 'back':\n return { yaw: 180, pitch: 0 };\n case 'right':\n return { yaw: 90, pitch: 0 };\n case 'left':\n return { yaw: -90, pitch: 0 };\n case 'top':\n return { yaw: 0, pitch: 90 };\n case 'bottom':\n return { yaw: 0, pitch: -90 };\n }\n}\n\n// ─── Quaternion ↔ Euler ───\n\n/**\n * Convert quaternion [x, y, z, w] to Euler representation (YXZ order, degrees).\n * Utility for Three.js integration where `camera.quaternion` is available.\n */\nexport function quaternionToEuler(\n qx: number,\n qy: number,\n qz: number,\n qw: number\n): GizmoOrientation {\n // YXZ intrinsic rotation decomposition\n // Matrix from quaternion\n const xx = qx * qx;\n const yy = qy * qy;\n const zz = qz * qz;\n const xy = qx * qy;\n const xz = qx * qz;\n const yz = qy * qz;\n const wx = qw * qx;\n const wy = qw * qy;\n const wz = qw * qz;\n\n const m11 = 1 - 2 * (yy + zz);\n const m13 = 2 * (xz + wy);\n const m21 = 2 * (xy + wz);\n const m22 = 1 - 2 * (xx + zz);\n const m23 = 2 * (yz - wx);\n const m31 = 2 * (xz - wy);\n const m33 = 1 - 2 * (xx + yy);\n\n // YXZ order extraction\n const pitch = Math.asin(-Math.max(-1, Math.min(1, m23)));\n let yaw: number;\n let roll: number;\n\n if (Math.abs(m23) < 0.9999) {\n yaw = Math.atan2(m13, m33);\n roll = Math.atan2(m21, m22);\n } else {\n yaw = Math.atan2(-m31, m11);\n roll = 0;\n }\n\n return {\n yaw: yaw * RAD2DEG,\n pitch: pitch * RAD2DEG,\n roll: roll * RAD2DEG,\n };\n}\n\n/**\n * Map an axis tip click to the corresponding preset view.\n */\nexport function axisToPresetView(\n axis: 'x' | 'y' | 'z',\n positive: boolean,\n upAxis: GizmoUpAxis\n): GizmoPresetView | null {\n if (upAxis === 'y-up') {\n switch (axis) {\n case 'x':\n return positive ? 'right' : 'left';\n case 'y':\n return positive ? 'top' : 'bottom';\n case 'z':\n return positive ? 'front' : 'back';\n }\n }\n\n // Z-up\n switch (axis) {\n case 'x':\n return positive ? 'right' : 'left';\n case 'y':\n return positive ? 'front' : 'back';\n case 'z':\n return positive ? 'top' : 'bottom';\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGizmoInteraction.js","sources":["../../../../../../src/components/editor/ViewportGizmo/useGizmoInteraction.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;AA4CA;AAEA;AACA;AAEM;AAGJ;;;;AAmBA;;AAEA;AACA;AACA;;;;;AAaA;AAEI;AACA;;AACA;;AAEF;AAIF;;;AAII;AACA;;;AAIA;AACA;AACA;AACA;;AAEA;;AAGA;;AAEF;;;;;;;;;AAUC;AAGH;AAEI;AACA;;AAEA;;;;;;AAOE;AACE;;AAGF;AACE;;;AAIE;AACA;;;;AAMJ;;;;;;;AAOF;;;;AAKE;;AACK;AACL;;;AAEA;;;AAEA;;AAEJ;;;;;;;;;;;;;AAcC;AAGH;AAEI;AACA;;AAEA;AACE;;AAEA;AAEA;;AAEE;;;;;AAIA;AASA;;;;;AAIE;AACA;AAAU;;;;AAGV;AACA;AAAU;;;;AAIlB;;;;;;;;;;;;AAaC;AAGH;AAEI;;AAEA;;;AAIA;AACE;;AAEI;;;;AAIJ;;AAEI;;;;AAIJ;;;;;AAKQ;;;;;;AAMR;;AAEI;;;AAGI;;;;;;AAMR;;AAEI;;;;AAIJ;;AAEI;;;;AAIJ;;AAEI;;;;AAIJ;AACA;;;;;;;;AASJ;;;;;;;;;;AAWC;;;;AAMD;;;;;AAKC;;AAEL;;"}
|
|
1
|
+
{"version":3,"file":"useGizmoInteraction.js","sources":["src/components/editor/ViewportGizmo/useGizmoInteraction.ts"],"sourcesContent":["'use client';\n\nimport { useState, useCallback, useMemo, useRef } from 'react';\n\nimport { clamp } from '@/utils/mathUtils';\n\nimport { gizmoHitTest, axisToPresetView } from './gizmoMath';\n\nimport type {\n GizmoOrientation,\n GizmoUpAxis,\n GizmoPresetView,\n GizmoHitRegion,\n OrbitDelta,\n} from './ViewportGizmo.types';\nimport type React from 'react';\n\ninterface UseGizmoInteractionOptions {\n orientation: GizmoOrientation;\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n diameter: number;\n upAxis: GizmoUpAxis;\n interactionMode: 'full' | 'snap-only' | 'orbit-only' | 'display-only';\n orbitSpeed: number;\n constrainPitch: boolean;\n disabled: boolean;\n onOrbit?: (delta: OrbitDelta) => void;\n onOrbitEnd?: (finalOrientation: GizmoOrientation) => void;\n onSnapToView?: (view: GizmoPresetView) => void;\n onAxisClick?: (axis: 'x' | 'y' | 'z', positive: boolean) => void;\n onOriginClick?: () => void;\n}\n\ninterface UseGizmoInteractionReturn {\n isDragging: boolean;\n hoveredRegion: GizmoHitRegion;\n handlers: {\n onPointerDown: (e: React.PointerEvent) => void;\n onPointerMove: (e: React.PointerEvent) => void;\n onPointerUp: (e: React.PointerEvent) => void;\n onKeyDown: (e: React.KeyboardEvent) => void;\n };\n}\n\nconst NONE_HIT: GizmoHitRegion = { type: 'none', distance: Infinity };\n\nconst ORBIT_KEY_STEP = 15;\nconst ORBIT_FINE_STEP = 5;\n\nexport function useGizmoInteraction(\n options: UseGizmoInteractionOptions\n): UseGizmoInteractionReturn {\n const {\n orientation,\n canvasRef,\n diameter,\n upAxis,\n interactionMode,\n orbitSpeed,\n constrainPitch,\n disabled,\n onOrbit,\n onOrbitEnd,\n onSnapToView,\n onAxisClick,\n onOriginClick,\n } = options;\n\n const [hoveredRegion, setHoveredRegion] = useState<GizmoHitRegion>(NONE_HIT);\n // Ref drives synchronous reads inside the pointer handlers; the state mirror\n // is the value exposed for rendering (kept in sync on drag start/end).\n const isDraggingRef = useRef(false);\n const [isDragging, setIsDragging] = useState(false);\n const lastPointerRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });\n const startPointerRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });\n const hasDraggedRef = useRef(false);\n\n const allowSnap =\n interactionMode === 'full' || interactionMode === 'snap-only';\n const allowOrbit =\n interactionMode === 'full' || interactionMode === 'orbit-only';\n\n const armLength = (diameter / 2) * 0.65;\n const center = useMemo(\n () => ({ x: diameter / 2, y: diameter / 2 }),\n [diameter]\n );\n\n const getPointerPos = useCallback(\n (e: React.PointerEvent) => {\n const canvas = canvasRef.current;\n if (!canvas) return { px: 0, py: 0 };\n const rect = canvas.getBoundingClientRect();\n return { px: e.clientX - rect.left, py: e.clientY - rect.top };\n },\n [canvasRef]\n );\n\n const onPointerDown = useCallback(\n (e: React.PointerEvent) => {\n if (disabled || interactionMode === 'display-only' || e.button !== 0)\n return;\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const { px, py } = getPointerPos(e);\n\n isDraggingRef.current = true;\n hasDraggedRef.current = false;\n lastPointerRef.current = { x: e.clientX, y: e.clientY };\n startPointerRef.current = { x: e.clientX, y: e.clientY };\n setIsDragging(true);\n canvas.setPointerCapture(e.pointerId);\n\n // Store hit for click detection on pointer up\n const hit = gizmoHitTest(px, py, orientation, center, armLength, upAxis);\n setHoveredRegion(hit);\n },\n [\n disabled,\n interactionMode,\n canvasRef,\n getPointerPos,\n orientation,\n center,\n armLength,\n upAxis,\n ]\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n if (isDraggingRef.current && allowOrbit) {\n const dx = e.clientX - lastPointerRef.current.x;\n const dy = e.clientY - lastPointerRef.current.y;\n\n // Only count as drag if moved > 3px from start\n const totalDx = e.clientX - startPointerRef.current.x;\n const totalDy = e.clientY - startPointerRef.current.y;\n if (Math.sqrt(totalDx ** 2 + totalDy ** 2) > 3) {\n hasDraggedRef.current = true;\n }\n\n if (hasDraggedRef.current) {\n const deltaYaw = dx * orbitSpeed * 0.5;\n let deltaPitch = -dy * orbitSpeed * 0.5;\n\n if (constrainPitch) {\n const newPitch = orientation.pitch + deltaPitch;\n deltaPitch = clamp(newPitch, -90, 90) - orientation.pitch;\n }\n\n onOrbit?.({ deltaYaw, deltaPitch });\n }\n\n lastPointerRef.current = { x: e.clientX, y: e.clientY };\n return;\n }\n\n // Hover detection\n if (interactionMode === 'display-only') return;\n const { px, py } = getPointerPos(e);\n const hit = gizmoHitTest(px, py, orientation, center, armLength, upAxis);\n setHoveredRegion(hit);\n\n // Cursor\n if (disabled) {\n canvas.style.cursor = 'default';\n } else if (hit.type === 'axis-positive' || hit.type === 'origin') {\n canvas.style.cursor = 'pointer';\n } else if (allowOrbit) {\n canvas.style.cursor = isDraggingRef.current ? 'grabbing' : 'grab';\n } else {\n canvas.style.cursor = 'default';\n }\n },\n [\n canvasRef,\n allowOrbit,\n orbitSpeed,\n constrainPitch,\n orientation,\n interactionMode,\n disabled,\n getPointerPos,\n center,\n armLength,\n upAxis,\n onOrbit,\n ]\n );\n\n const onPointerUp = useCallback(\n (e: React.PointerEvent) => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n if (isDraggingRef.current) {\n isDraggingRef.current = false;\n setIsDragging(false);\n canvas.releasePointerCapture(e.pointerId);\n\n if (hasDraggedRef.current) {\n // Finished orbit drag\n onOrbitEnd?.(orientation);\n } else if (allowSnap) {\n // Click (no drag) — check hit\n const { px, py } = getPointerPos(e);\n const hit = gizmoHitTest(\n px,\n py,\n orientation,\n center,\n armLength,\n upAxis\n );\n\n if (hit.type === 'origin') {\n onOriginClick?.();\n } else if (hit.type === 'axis-positive' && hit.axis) {\n onAxisClick?.(hit.axis, true);\n const view = axisToPresetView(hit.axis, true, upAxis);\n if (view) onSnapToView?.(view);\n } else if (hit.type === 'axis-negative' && hit.axis) {\n onAxisClick?.(hit.axis, false);\n const view = axisToPresetView(hit.axis, false, upAxis);\n if (view) onSnapToView?.(view);\n }\n }\n }\n },\n [\n canvasRef,\n allowSnap,\n getPointerPos,\n orientation,\n center,\n armLength,\n upAxis,\n onOrbitEnd,\n onOriginClick,\n onAxisClick,\n onSnapToView,\n ]\n );\n\n const onKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled || interactionMode === 'display-only') return;\n\n const fine = e.shiftKey;\n const step = fine ? ORBIT_FINE_STEP : ORBIT_KEY_STEP;\n let handled = false;\n\n switch (e.key) {\n case 'ArrowLeft':\n if (allowOrbit) {\n onOrbit?.({ deltaYaw: -step, deltaPitch: 0 });\n handled = true;\n }\n break;\n case 'ArrowRight':\n if (allowOrbit) {\n onOrbit?.({ deltaYaw: step, deltaPitch: 0 });\n handled = true;\n }\n break;\n case 'ArrowUp':\n if (allowOrbit) {\n let deltaPitch = step;\n if (constrainPitch) {\n deltaPitch =\n clamp(orientation.pitch + step, -90, 90) - orientation.pitch;\n }\n onOrbit?.({ deltaYaw: 0, deltaPitch });\n handled = true;\n }\n break;\n case 'ArrowDown':\n if (allowOrbit) {\n let deltaPitch = -step;\n if (constrainPitch) {\n deltaPitch =\n clamp(orientation.pitch - step, -90, 90) - orientation.pitch;\n }\n onOrbit?.({ deltaYaw: 0, deltaPitch });\n handled = true;\n }\n break;\n case '1':\n if (allowSnap) {\n onSnapToView?.(e.ctrlKey ? 'back' : 'front');\n handled = true;\n }\n break;\n case '3':\n if (allowSnap) {\n onSnapToView?.(e.ctrlKey ? 'left' : 'right');\n handled = true;\n }\n break;\n case '7':\n if (allowSnap) {\n onSnapToView?.(e.ctrlKey ? 'bottom' : 'top');\n handled = true;\n }\n break;\n case '5':\n case 'Home':\n onOriginClick?.();\n handled = true;\n break;\n }\n\n if (handled) {\n e.preventDefault();\n }\n },\n [\n disabled,\n interactionMode,\n allowOrbit,\n allowSnap,\n constrainPitch,\n orientation,\n onOrbit,\n onSnapToView,\n onOriginClick,\n ]\n );\n\n return {\n isDragging,\n hoveredRegion,\n handlers: {\n onPointerDown,\n onPointerMove,\n onPointerUp,\n onKeyDown,\n },\n };\n}\n"],"names":[],"mappings":";;;;;AA4CA;AAEA;AACA;AAEM;AAGJ;;;;AAmBA;;AAEA;AACA;AACA;;;;;AAaA;AAEI;AACA;;AACA;;AAEF;AAIF;;;AAII;AACA;;;AAIA;AACA;AACA;AACA;;AAEA;;AAGA;;AAEF;;;;;;;;;AAUC;AAGH;AAEI;AACA;;AAEA;;;;;;AAOE;AACE;;AAGF;AACE;;;AAIE;AACA;;;;AAMJ;;;;;;;AAOF;;;;AAKE;;AACK;AACL;;;AAEA;;;AAEA;;AAEJ;;;;;;;;;;;;;AAcC;AAGH;AAEI;AACA;;AAEA;AACE;;AAEA;AAEA;;AAEE;;;;;AAIA;AASA;;;;;AAIE;AACA;AAAU;;;;AAGV;AACA;AAAU;;;;AAIlB;;;;;;;;;;;;AAaC;AAGH;AAEI;;AAEA;;;AAIA;AACE;;AAEI;;;;AAIJ;;AAEI;;;;AAIJ;;;;;AAKQ;;;;;;AAMR;;AAEI;;;AAGI;;;;;;AAMR;;AAEI;;;;AAIJ;;AAEI;;;;AAIJ;;AAEI;;;;AAIJ;AACA;;;;;;;;AASJ;;;;;;;;;;AAWC;;;;AAMD;;;;;AAKC;;AAEL;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGizmoRenderer.js","sources":["
|
|
1
|
+
{"version":3,"file":"useGizmoRenderer.js","sources":["src/components/editor/ViewportGizmo/useGizmoRenderer.ts"],"sourcesContent":["'use client';\n\nimport { useCallback } from 'react';\n\nimport { resolveCanvasTheme } from '@/components/primitives/canvas/canvasTheme';\nimport { useCanvasRenderer } from '@/components/primitives/canvas/useCanvasRenderer';\n\nimport { projectAxes } from './gizmoMath';\n\nimport type {\n GizmoOrientation,\n GizmoUpAxis,\n GizmoHitRegion,\n ViewportGizmoSize,\n} from './ViewportGizmo.types';\nimport type { CanvasThemeColors } from '@/components/primitives/canvas/canvas.types';\nimport type React from 'react';\n\n// ─── Size Presets ───\n\ninterface SizePreset {\n labelFontSize: number;\n fontWeight: number;\n armWidthPositive: number;\n armWidthNegative: number;\n tipRadius: number;\n tipHoverRadius: number;\n originRadius: number;\n}\n\nconst SIZE_PRESETS: Record<ViewportGizmoSize, SizePreset> = {\n sm: {\n labelFontSize: 9,\n fontWeight: 600,\n armWidthPositive: 1.5,\n armWidthNegative: 0.75,\n tipRadius: 4,\n tipHoverRadius: 5,\n originRadius: 4,\n },\n md: {\n labelFontSize: 11,\n fontWeight: 600,\n armWidthPositive: 2.5,\n armWidthNegative: 1,\n tipRadius: 5,\n tipHoverRadius: 7,\n originRadius: 5,\n },\n lg: {\n labelFontSize: 13,\n fontWeight: 600,\n armWidthPositive: 3,\n armWidthNegative: 1.5,\n tipRadius: 6,\n tipHoverRadius: 8,\n originRadius: 6,\n },\n};\n\n// ─── Default Axis Colors ───\n\nconst AXIS_COLORS: Record<string, string> = {\n x: '#E63946',\n y: '#6AA84F',\n z: '#4A86C8',\n};\n\ninterface UseGizmoRendererOptions {\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n orientation: GizmoOrientation;\n diameter: number;\n upAxis: GizmoUpAxis;\n size: ViewportGizmoSize;\n axisColors: Record<string, string>;\n axisLabels: Record<string, string>;\n axisVisible: Record<string, boolean>;\n showLabels: boolean;\n showNegativeAxes: boolean;\n showOrbitRing: boolean;\n showOriginHandle: boolean;\n background: 'transparent' | 'subtle' | 'solid';\n disabled: boolean;\n isDragging: boolean;\n hoveredRegion: GizmoHitRegion;\n}\n\nexport { AXIS_COLORS };\n\nexport function useGizmoRenderer(options: UseGizmoRendererOptions): void {\n const {\n canvasRef,\n orientation,\n diameter,\n upAxis,\n size,\n axisColors,\n axisLabels,\n axisVisible,\n showLabels,\n showNegativeAxes,\n showOrbitRing,\n showOriginHandle,\n background,\n disabled,\n hoveredRegion,\n } = options;\n\n const draw = useCallback(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const dpr =\n typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n const rect = canvas.getBoundingClientRect();\n const w = rect.width;\n const h = rect.height;\n\n if (canvas.width !== w * dpr || canvas.height !== h * dpr) {\n canvas.width = w * dpr;\n canvas.height = h * dpr;\n ctx.scale(dpr, dpr);\n } else {\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n\n const theme: CanvasThemeColors = resolveCanvasTheme(canvas);\n const preset = SIZE_PRESETS[size];\n const opacity = disabled ? 0.4 : 1;\n const center = { x: w / 2, y: h / 2 };\n const armLength = (diameter / 2) * 0.65;\n\n // 1. Clear / background\n ctx.clearRect(0, 0, w, h);\n\n if (background === 'subtle') {\n ctx.globalAlpha = opacity * 0.15;\n ctx.fillStyle = theme.backgroundSecondary;\n ctx.beginPath();\n ctx.arc(center.x, center.y, diameter / 2, 0, Math.PI * 2);\n ctx.fill();\n } else if (background === 'solid') {\n ctx.globalAlpha = opacity;\n ctx.fillStyle = theme.backgroundSecondary;\n ctx.fillRect(0, 0, w, h);\n }\n\n ctx.globalAlpha = opacity;\n\n // 2. Orbit ring\n if (showOrbitRing) {\n ctx.strokeStyle = theme.borderDefault;\n ctx.lineWidth = 1;\n ctx.globalAlpha = opacity * 0.3;\n ctx.beginPath();\n ctx.arc(center.x, center.y, armLength * 1.1, 0, Math.PI * 2);\n ctx.stroke();\n ctx.globalAlpha = opacity;\n }\n\n // 3. Draw arms — depth sorted (furthest first = painter's algorithm)\n const arms = projectAxes(orientation, center, armLength, upAxis);\n\n for (const arm of arms) {\n if (!axisVisible[arm.axis]) continue;\n if (!arm.positive && !showNegativeAxes) continue;\n\n const color = axisColors[arm.axis] ?? AXIS_COLORS[arm.axis] ?? '#888';\n const isHovered =\n hoveredRegion.axis === arm.axis &&\n ((arm.positive && hoveredRegion.type === 'axis-positive') ||\n (!arm.positive && hoveredRegion.type === 'axis-negative'));\n\n const alpha = arm.positive ? 1.0 : 0.35;\n const lineWidth = arm.positive\n ? preset.armWidthPositive\n : preset.armWidthNegative;\n\n // Draw arm line\n ctx.globalAlpha = opacity * alpha;\n ctx.strokeStyle = color;\n ctx.lineWidth = lineWidth;\n ctx.lineCap = 'round';\n ctx.beginPath();\n ctx.moveTo(center.x, center.y);\n ctx.lineTo(arm.screenX, arm.screenY);\n ctx.stroke();\n\n // Draw tip handle (positive arms only)\n if (arm.positive) {\n const tipR = isHovered ? preset.tipHoverRadius : preset.tipRadius;\n ctx.globalAlpha = opacity;\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.arc(arm.screenX, arm.screenY, tipR, 0, Math.PI * 2);\n ctx.fill();\n\n // Draw label\n if (showLabels) {\n const label = axisLabels[arm.axis] ?? arm.axis.toUpperCase();\n const labelOffset = tipR + preset.labelFontSize * 0.6;\n\n // Position label outward from center\n const dx = arm.screenX - center.x;\n const dy = arm.screenY - center.y;\n const dist = Math.sqrt(dx * dx + dy * dy) || 1;\n const nx = dx / dist;\n const ny = dy / dist;\n const lx = arm.screenX + nx * labelOffset;\n const ly = arm.screenY + ny * labelOffset;\n\n ctx.globalAlpha = opacity;\n ctx.fillStyle = color;\n ctx.font = `${preset.fontWeight} ${preset.labelFontSize}px sans-serif`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(label, lx, ly);\n }\n }\n }\n\n // 4. Origin handle\n if (showOriginHandle) {\n const isOriginHovered = hoveredRegion.type === 'origin';\n const originR = isOriginHovered\n ? preset.originRadius + 1.5\n : preset.originRadius;\n\n ctx.globalAlpha = opacity * 0.6;\n ctx.fillStyle = theme.textSecondary;\n ctx.beginPath();\n ctx.arc(center.x, center.y, originR, 0, Math.PI * 2);\n ctx.fill();\n\n // Border ring\n ctx.globalAlpha = opacity * 0.3;\n ctx.strokeStyle = theme.textMuted;\n ctx.lineWidth = 1;\n ctx.stroke();\n }\n\n ctx.globalAlpha = 1;\n }, [\n canvasRef,\n orientation,\n diameter,\n upAxis,\n size,\n axisColors,\n axisLabels,\n axisVisible,\n showLabels,\n showNegativeAxes,\n showOrbitRing,\n showOriginHandle,\n background,\n disabled,\n hoveredRegion,\n ]);\n\n useCanvasRenderer({\n draw,\n deps: [draw],\n paused: false,\n });\n}\n"],"names":[],"mappings":";;;;;;AA8BA;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;;AAGH;AAEA;AACE;AACA;AACA;;AAwBI;AACJ;AAkBA;AACE;AACA;;;AAGA;;AAEA;AAEA;AACA;AACA;AAEA;AACE;AACA;AACA;;;AAEA;;AAGF;AACA;;AAEA;;;;AAMA;AACE;AACA;;;;;AAIK;AACL;AACA;;;AAIF;;;AAIE;AACA;AACA;;;;AAIA;;;AAIF;AAEA;AACE;;AACA;;AAEA;;;AAII;AAEJ;AACA;;AAEE;;AAGF;AACA;AACA;AACA;;;;;;AAOA;AACE;AACA;AACA;;;;;;AAOE;;;;;AAMA;AACA;AACA;;;AAIA;AACA;AACA;AACA;AACA;;;;;;;AAQJ;;AAEE;AACA;AAEF;AACA;;;;;AAMA;AACA;AACA;;;AAIF;AACF;;;;;;;;;;;;;;;;AAgBC;AAED;;;AAGE;AACD;AACH;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.js","sources":["
|
|
1
|
+
{"version":3,"file":"Alert.js","sources":["src/components/feedback/Alert/Alert.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React from 'react';\n\nimport { CheckIcon } from '@/components/Icons/CheckIcon';\nimport { CloseIcon } from '@/components/Icons/CloseIcon';\nimport { ErrorIcon } from '@/components/Icons/ErrorIcon';\nimport { InfoIcon } from '@/components/Icons/InfoIcon';\nimport { WarningIcon } from '@/components/Icons/WarningIcon';\nimport { IconButton } from '@/components/primitives/IconButton';\nimport { vars } from '@/theme/contract.css';\nimport { cx } from '@/utils/cx';\n\nimport {\n alertCloseButtonSolidStyle,\n alertCloseColumnStyle,\n alertColorVar,\n alertContentStyle,\n alertIconSolidStyle,\n alertIconStyle,\n alertRecipe,\n} from './Alert.css';\nimport { AlertActions } from './AlertActions';\nimport { AlertDescription } from './AlertDescription';\nimport { AlertTitle } from './AlertTitle';\n\nimport type { AlertProps, AlertVariant } from './Alert.types';\n\nconst VARIANT_COLOR: Record<AlertVariant, string> = {\n info: vars.colors.accent.primary,\n success: vars.colors.accent.success,\n warning: vars.colors.accent.warning,\n error: vars.colors.accent.error,\n neutral: vars.colors.text.muted,\n};\n\nconst VARIANT_ROLE: Record<AlertVariant, { role: string; ariaLabel?: string }> =\n {\n info: { role: 'status' },\n success: { role: 'status' },\n warning: { role: 'alert' },\n error: { role: 'alert' },\n neutral: { role: 'region', ariaLabel: 'Notice' },\n };\n\nconst VARIANT_DEFAULT_ICON: Record<AlertVariant, React.ReactNode> = {\n info: <InfoIcon size=\"md\" decorative />,\n success: <CheckIcon size=\"md\" decorative />,\n warning: <WarningIcon size=\"md\" decorative />,\n error: <ErrorIcon size=\"md\" decorative />,\n neutral: null,\n};\n\n/**\n * Persistent inline alert / callout.\n *\n * Use for non-transient status messages tied to the surrounding UI — read-only\n * notices, expired credentials, unsaved-changes banners, etc. For transient\n * confirmations like \"File saved\", reach for `useToast` instead.\n *\n * Compound API: pair with `<Alert.Title>`, `<Alert.Description>`, and\n * `<Alert.Actions>` for richer layouts. The same components are also\n * available as standalone named exports (`AlertTitle`, `AlertDescription`,\n * `AlertActions`).\n *\n * Accessibility: roles are derived from the variant — `alert` for\n * `error`/`warning`, `status` for `info`/`success`, `region` for `neutral`.\n * The component never auto-focuses; inline alerts shouldn't grab focus.\n *\n * @example\n * ```tsx\n * <Alert variant=\"warning\" title=\"Read-only mode\">\n * Switch to edit mode to make changes.\n * </Alert>\n *\n * <Alert variant=\"error\" onClose={() => setOpen(false)}>\n * <Alert.Title>Couldn't reach the server</Alert.Title>\n * <Alert.Description>The request timed out.</Alert.Description>\n * <Alert.Actions align=\"right\">\n * <Button onClick={retry}>Retry</Button>\n * </Alert.Actions>\n * </Alert>\n * ```\n */\nconst AlertImpl = /*#__PURE__*/ React.memo<AlertProps>(\n ({\n variant = 'info',\n appearance = 'subtle',\n icon = true,\n onClose,\n title,\n width = '100%',\n children,\n className,\n style,\n testId,\n 'aria-label': ariaLabelProp,\n ref,\n ...rest\n }) => {\n const isSolid = appearance === 'solid';\n const { role, ariaLabel: defaultAriaLabel } = VARIANT_ROLE[variant];\n\n const resolvedIcon: React.ReactNode =\n icon === false\n ? null\n : icon === true\n ? VARIANT_DEFAULT_ICON[variant]\n : icon;\n\n const inlineVars = assignInlineVars({\n [alertColorVar]: VARIANT_COLOR[variant],\n });\n\n const resolvedWidth = typeof width === 'number' ? `${width}px` : width;\n\n return (\n <div\n ref={ref}\n role={role}\n aria-label={ariaLabelProp ?? defaultAriaLabel}\n className={cx(alertRecipe({ variant, appearance }), className)}\n style={{ ...inlineVars, width: resolvedWidth, ...style }}\n data-testid={testId}\n data-variant={variant}\n data-appearance={appearance}\n {...rest}\n >\n {resolvedIcon ? (\n <div\n className={cx(alertIconStyle, isSolid && alertIconSolidStyle)}\n data-alert-icon=\"\"\n aria-hidden=\"true\"\n >\n {resolvedIcon}\n </div>\n ) : null}\n\n <div className={alertContentStyle}>\n {title !== undefined && title !== null ? (\n <AlertTitle>{title}</AlertTitle>\n ) : null}\n {typeof children === 'string' ? (\n <AlertDescription>{children}</AlertDescription>\n ) : (\n children\n )}\n </div>\n\n {onClose ? (\n <div className={alertCloseColumnStyle}>\n <IconButton\n type=\"button\"\n aria-label=\"Close alert\"\n variant=\"ghost\"\n size=\"sm\"\n radius=\"sm\"\n onClick={onClose}\n className={isSolid ? alertCloseButtonSolidStyle : undefined}\n >\n <CloseIcon size=\"sm\" decorative />\n </IconButton>\n </div>\n ) : null}\n </div>\n );\n }\n);\n\nAlertImpl.displayName = 'Alert';\n\n/**\n * Public API: `Alert` with attached compound members.\n *\n * `React.memo` returns an exotic component that doesn't accept arbitrary\n * static fields with proper typing — we expose the compound members through\n * a typed wrapper.\n */\ntype AlertCompound = typeof AlertImpl & {\n Title: typeof AlertTitle;\n Description: typeof AlertDescription;\n Actions: typeof AlertActions;\n};\n\nexport const Alert = AlertImpl as AlertCompound;\nAlert.Title = AlertTitle;\nAlert.Description = AlertDescription;\nAlert.Actions = AlertActions;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AA6BA;AACE;AACA;AACA;AACA;AACA;;AAGF;AAEI;AACA;AACA;AACA;;;AAIJ;;;;;AAKE;;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;AACH;AAgBI;AACA;AAEA;AAEI;;AAEE;;;AAIJ;AACD;AAED;;AAoDF;AAGF;AAeO;AACP;AACA;AACA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlertActions.js","sources":["
|
|
1
|
+
{"version":3,"file":"AlertActions.js","sources":["src/components/feedback/Alert/AlertActions.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { alertActionsRecipe } from './Alert.css';\n\nimport type { AlertActionsProps } from './Alert.types';\n\n/**\n * Action-button row for an `Alert`. Lays out children with a horizontal gap\n * and configurable alignment. Renders only when actions are needed — for\n * dismissible alerts the close button in the top-right is usually enough.\n *\n * @example\n * ```tsx\n * <Alert variant=\"error\">\n * <Alert.Title>Couldn't reach the server</Alert.Title>\n * <Alert.Description>The request timed out after 30 seconds.</Alert.Description>\n * <Alert.Actions align=\"right\">\n * <Button variant=\"filled\" onClick={retry}>Retry</Button>\n * <Button onClick={dismiss}>Dismiss</Button>\n * </Alert.Actions>\n * </Alert>\n * ```\n */\nexport const AlertActions = /*#__PURE__*/ React.memo<AlertActionsProps>(\n ({ children, align = 'left', className, style, testId, ref, ...rest }) => {\n return (\n <div\n ref={ref}\n className={cx(alertActionsRecipe({ align }), className)}\n style={style}\n data-testid={testId}\n data-alert-actions=\"\"\n {...rest}\n >\n {children}\n </div>\n );\n }\n);\n\nAlertActions.displayName = 'AlertActions';\n"],"names":[],"mappings":";;;;;;AAUA;;;;;;;;;;;;;;;;AAgBG;AACI;AAEH;AAYF;AAGF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlertDescription.js","sources":["
|
|
1
|
+
{"version":3,"file":"AlertDescription.js","sources":["src/components/feedback/Alert/AlertDescription.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { alertDescriptionStyle } from './Alert.css';\n\nimport type { AlertDescriptionProps } from './Alert.types';\n\n/**\n * Body text slot for an `Alert`. Most one-line alerts can pass a string as\n * children to `<Alert>` directly; reach for this when you need to compose\n * multiple paragraphs or inline elements.\n *\n * @example\n * ```tsx\n * <Alert variant=\"info\">\n * <Alert.Title>Heads up</Alert.Title>\n * <Alert.Description>\n * This editor is read-only. Open the file in edit mode to make changes.\n * </Alert.Description>\n * </Alert>\n * ```\n */\nexport const AlertDescription = /*#__PURE__*/ React.memo<AlertDescriptionProps>(\n ({ children, className, style, testId, ref, ...rest }) => {\n return (\n <div\n ref={ref}\n className={cx(alertDescriptionStyle, className)}\n style={style}\n data-testid={testId}\n data-alert-description=\"\"\n {...rest}\n >\n {children}\n </div>\n );\n }\n);\n\nAlertDescription.displayName = 'AlertDescription';\n"],"names":[],"mappings":";;;;;;AAUA;;;;;;;;;;;;;;AAcG;AACI;AAEH;AAYF;AAGF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlertTitle.js","sources":["
|
|
1
|
+
{"version":3,"file":"AlertTitle.js","sources":["src/components/feedback/Alert/AlertTitle.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { alertTitleStyle } from './Alert.css';\n\nimport type { AlertTitleProps } from './Alert.types';\n\n/**\n * Title slot for an `Alert`. Renders a single line of emphasized text above\n * the description.\n *\n * Equivalent to passing `title` directly on `<Alert>`. Use this form when you\n * want richer content (links, inline elements) inside the title.\n *\n * @example\n * ```tsx\n * <Alert variant=\"warning\">\n * <Alert.Title>Read-only mode</Alert.Title>\n * <Alert.Description>Switch to edit to change this file.</Alert.Description>\n * </Alert>\n * ```\n */\nexport const AlertTitle = /*#__PURE__*/ React.memo<AlertTitleProps>(\n ({ children, className, style, testId, ref, ...rest }) => {\n return (\n <div\n ref={ref}\n className={cx(alertTitleStyle, className)}\n style={style}\n data-testid={testId}\n data-alert-title=\"\"\n {...rest}\n >\n {children}\n </div>\n );\n }\n);\n\nAlertTitle.displayName = 'AlertTitle';\n"],"names":[],"mappings":";;;;;;AAUA;;;;;;;;;;;;;;AAcG;AACI;AAEH;AAYF;AAGF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPalette.js","sources":["../../../../../../src/components/feedback/CommandPalette/CommandPalette.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;AAuCA;AACA;AAUA;AACE;;AACsB;;;AAEtB;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACI;AAoBL;AACA;AACA;AAEA;AACA;;;;;;AAOA;AAEA;AACE;;AAED;;;;;;;;;;AAWD;;;;AAKI;AACE;;AAEF;;AAEA;;AAEF;AACF;;;;AAKE;;;;AAKE;;AAEE;AAAwB;;AAE1B;AACE;AACA;;;;AAKJ;AAEA;AACE;;AAEE;AACA;;;;;;;AASJ;;AAEE;;AACA;AAEI;;AAEE;;;;AAIF;AACA;;AAED;;AAEH;AACE;;AAEE;AACA;;;;AAID;AACD;;;AAIJ;;;;;AAMF;;;;AAEO;AACL;;AAGF;;AAGM;;AACe;AACf;;AACc;;;AAEd;AACF;AACF;AAIF;;;AAGI;AACA;;;;AAGE;;AAEF;;AAKJ;AAEI;;AAEE;;;AAGF;;;;;AAKA;;AAEE;;;AAGF;;;;;AAKA;;AAEE;;;AAGF;;AAEE;AACA;;;AAEJ;;;AAMA;;AACA;AACA;;AACA;;;;AAMF;AAEA;;AAGM;;AAEJ;AAIF;AAAW;AAEX;AACA;;;;AA8CY;;AAEF;AAqBE;;;AAWA;AACA;AACA;AAAsC;AACtC;;;;;;;AAOE;;;AAYE;;;AAOI;AACA;;AAOV;AAOV;;;AAGA;AACF;AAEA;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"CommandPalette.js","sources":["src/components/feedback/CommandPalette/CommandPalette.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n useCallback,\n useDeferredValue,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { SearchIcon } from '@/components/Icons/SearchIcon';\nimport { Kbd } from '@/components/primitives/Kbd';\nimport { cx } from '@/utils/cx';\n\nimport {\n emptyStyle,\n groupHeaderStyle,\n inputStyle,\n inputWrapperStyle,\n itemDescriptionStyle,\n itemIconStyle,\n itemLabelStyle,\n itemSelectedStyle,\n itemShortcutStyle,\n itemStyle,\n itemTextStyle,\n listStyle,\n overlayStyle,\n panelStyle,\n searchIconStyle,\n} from './CommandPalette.css';\nimport { fuzzyFilter } from './fuzzySearch';\nimport { useRecentItems } from './useRecentItems';\n\nimport type { CommandItem, CommandPaletteProps } from './CommandPalette.types';\n\nconst DEFAULT_GROUP = 'default';\nconst RECENT_GROUP = '__recent__';\n\ninterface ResolvedRow {\n type: 'header' | 'item';\n group: string;\n groupLabel: string;\n item?: CommandItem;\n index?: number; // index into the flat selectable items list\n}\n\nfunction getItemSearchStrings(item: CommandItem): string[] {\n const out = [item.label];\n if (item.description) out.push(item.description);\n if (item.keywords) out.push(...item.keywords);\n return out;\n}\n\n/**\n * Search-driven command list shown as a centred floating dialog. Type to\n * fuzzy-filter, ArrowUp/ArrowDown to navigate, Enter to select, Escape to\n * close. Recent selections are tracked in localStorage when `recentKey` is\n * set. The component does not bind a global hotkey — wire `useHotkey` in\n * the consumer.\n *\n * @example\n * ```tsx\n * useHotkey('Mod+K', () => { setOpen(true); });\n *\n * <CommandPalette\n * open={open}\n * onClose={() => setOpen(false)}\n * items={[\n * { id: 'open', label: 'Open File', shortcut: 'Cmd+O', group: 'File' },\n * { id: 'save', label: 'Save', shortcut: 'Cmd+S', group: 'File' },\n * ]}\n * onSelect={(item) => runCommand(item.id)}\n * recentKey=\"myapp:command-palette\"\n * />\n * ```\n */\nexport const CommandPalette: React.FC<CommandPaletteProps> = ({\n open,\n onClose,\n items,\n onSelect,\n placeholder = 'Type a command or search...',\n emptyState,\n recentLabel = 'Recent',\n recentKey,\n maxRecent = 5,\n renderItem,\n portal = true,\n maxHeight = 400,\n width = 640,\n className,\n style,\n testId,\n id,\n ...rest\n}) => {\n const autoId = useId();\n const inputId = id ?? `command-palette-${autoId}`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLUListElement>(null);\n\n const [query, setQuery] = useState('');\n const [activeIndex, setActiveIndex] = useState(0);\n // Defer the query that drives fuzzy filtering so the input stays responsive\n // while a large command list re-filters and re-sorts. The immediate `query`\n // still drives the input value.\n const deferredQuery = useDeferredValue(query);\n\n const { recentIds, pushRecent } = useRecentItems({\n storageKey: recentKey,\n maxRecent,\n });\n\n // Reset state on each open transition. This reacts to the `open` prop\n // toggling rather than synchronizing continuous state, so the one-shot\n // setState in the effect is intentional.\n useEffect(() => {\n if (open) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setQuery('');\n setActiveIndex(0);\n }\n }, [open]);\n\n // Focus the input on open.\n useEffect(() => {\n if (open) {\n const t = setTimeout(() => {\n inputRef.current?.focus();\n }, 0);\n return () => {\n clearTimeout(t);\n };\n }\n return undefined;\n }, [open]);\n\n // Resolve groups + filtering.\n const { rows, selectableItems } = useMemo(() => {\n const groupOrder: string[] = [];\n const groupMap = new Map<string, CommandItem[]>();\n\n if (deferredQuery === '' && recentIds.length > 0) {\n const byId = new Map(items.map(it => [it.id, it]));\n const recent: CommandItem[] = [];\n for (const id of recentIds) {\n const it = byId.get(id);\n if (it && !it.disabled) recent.push(it);\n }\n if (recent.length > 0) {\n groupOrder.push(RECENT_GROUP);\n groupMap.set(RECENT_GROUP, recent);\n }\n }\n\n const filtered = fuzzyFilter(deferredQuery, items, getItemSearchStrings);\n const filteredItems = filtered.map(r => r.item);\n\n for (const item of filteredItems) {\n const g = item.group ?? DEFAULT_GROUP;\n if (!groupMap.has(g)) {\n groupOrder.push(g);\n groupMap.set(g, []);\n }\n groupMap.get(g)?.push(item);\n }\n\n const rows: ResolvedRow[] = [];\n const flat: CommandItem[] = [];\n let nextIndex = 0;\n\n for (const groupKey of groupOrder) {\n const list = groupMap.get(groupKey);\n if (!list || list.length === 0) continue;\n const groupLabel =\n groupKey === RECENT_GROUP\n ? recentLabel\n : groupKey === DEFAULT_GROUP\n ? ''\n : groupKey;\n if (groupLabel) {\n rows.push({\n type: 'header',\n group: groupKey,\n groupLabel,\n });\n }\n for (const item of list) {\n const index = nextIndex++;\n rows.push({\n type: 'item',\n group: groupKey,\n groupLabel,\n item,\n index,\n });\n flat.push(item);\n }\n }\n\n return { rows, selectableItems: flat };\n }, [items, deferredQuery, recentIds, recentLabel]);\n\n // Clamp the active index during render when the result list shrinks. This is\n // React's documented adjust-state-during-render pattern — it avoids an extra\n // commit, and the guards make it self-terminating.\n if (selectableItems.length === 0) {\n if (activeIndex !== 0) setActiveIndex(0);\n } else if (activeIndex >= selectableItems.length) {\n setActiveIndex(selectableItems.length - 1);\n }\n\n const moveActive = useCallback(\n (delta: number) => {\n setActiveIndex(prev => {\n const len = selectableItems.length;\n if (len === 0) return 0;\n let next = prev + delta;\n if (next < 0) next = len - 1;\n if (next >= len) next = 0;\n return next;\n });\n },\n [selectableItems.length]\n );\n\n const runSelect = useCallback(\n (item: CommandItem) => {\n if (item.disabled) return;\n pushRecent(item.id);\n if (item.onSelect) {\n item.onSelect();\n } else {\n onSelect?.(item);\n }\n onClose();\n },\n [onClose, onSelect, pushRecent]\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n return;\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n moveActive(1);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n moveActive(-1);\n return;\n }\n if (e.key === 'Home') {\n e.preventDefault();\n setActiveIndex(0);\n return;\n }\n if (e.key === 'End') {\n e.preventDefault();\n setActiveIndex(Math.max(0, selectableItems.length - 1));\n return;\n }\n if (e.key === 'Enter') {\n e.preventDefault();\n const item = selectableItems[activeIndex];\n if (item) runSelect(item);\n }\n },\n [activeIndex, moveActive, onClose, runSelect, selectableItems]\n );\n\n // Scroll active item into view when it changes.\n useEffect(() => {\n if (!open) return;\n const list = listRef.current;\n if (!list) return;\n const el = list.querySelector<HTMLElement>(\n `[data-row-index=\"${String(activeIndex)}\"]`\n );\n if (el && typeof el.scrollIntoView === 'function') {\n el.scrollIntoView({ block: 'nearest' });\n }\n }, [activeIndex, open]);\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget) {\n onClose();\n }\n },\n [onClose]\n );\n\n if (!open) return null;\n\n const widthCss = typeof width === 'number' ? `${String(width)}px` : width;\n const activeId =\n activeIndex < selectableItems.length\n ? `${listboxId}-item-${String(activeIndex)}`\n : undefined;\n\n const content = (\n <>\n {/* Decorative backdrop: pointer users can click to dismiss, while\n keyboard users dismiss via Escape handled on the dialog panel. */}\n <div\n aria-hidden=\"true\"\n className={overlayStyle}\n data-testid={testId ? `${testId}-overlay` : undefined}\n onClick={handleOverlayClick}\n />\n {/* The dialog panel needs a keydown handler for Escape-to-close and\n listbox navigation; this is required dialog behavior, not a static\n element repurposed as interactive. */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Command palette\"\n className={cx(panelStyle, className)}\n style={{ width: widthCss, ...style }}\n data-testid={testId}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n <div className={inputWrapperStyle}>\n <span className={searchIconStyle} aria-hidden=\"true\">\n <SearchIcon />\n </span>\n <input\n ref={inputRef}\n id={inputId}\n type=\"text\"\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={listboxId}\n aria-activedescendant={activeId}\n aria-autocomplete=\"list\"\n className={inputStyle}\n placeholder={placeholder}\n value={query}\n onChange={e => {\n setQuery(e.target.value);\n setActiveIndex(0);\n }}\n data-testid={testId ? `${testId}-input` : undefined}\n />\n </div>\n\n {selectableItems.length === 0 ? (\n <div\n className={emptyStyle}\n data-testid={testId ? `${testId}-empty` : undefined}\n >\n {emptyState ?? `No matches for \"${deferredQuery}\"`}\n </div>\n ) : (\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n className={listStyle}\n style={{ maxHeight }}\n >\n {rows.map((row, rowIndex) => {\n if (row.type === 'header') {\n return (\n <li\n key={`hdr-${row.group}-${String(rowIndex)}`}\n role=\"presentation\"\n className={groupHeaderStyle}\n >\n {row.groupLabel}\n </li>\n );\n }\n const item = row.item;\n const itemIndex = row.index;\n if (!item || itemIndex === undefined) return null;\n const isSelected = itemIndex === activeIndex;\n const itemDomId = `${listboxId}-item-${String(itemIndex)}`;\n return (\n // Keyboard selection is handled centrally via the\n // aria-activedescendant model on the dialog (Enter selects the\n // active option); the click is a pointer-only convenience.\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events\n <li\n key={`${row.group}-${item.id}`}\n id={itemDomId}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={item.disabled ? true : undefined}\n className={cx(itemStyle, isSelected && itemSelectedStyle)}\n data-disabled={item.disabled ? 'true' : undefined}\n data-row-index={itemIndex}\n data-testid={testId ? `${testId}-item-${item.id}` : undefined}\n onMouseEnter={() => {\n if (!item.disabled) setActiveIndex(itemIndex);\n }}\n onClick={() => {\n runSelect(item);\n }}\n >\n {renderItem ? (\n renderItem(item, {\n selected: isSelected,\n query: deferredQuery,\n })\n ) : (\n <DefaultItemContent item={item} />\n )}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n </>\n );\n\n if (portal && typeof document !== 'undefined') {\n return createPortal(content, document.body);\n }\n return content;\n};\n\nCommandPalette.displayName = 'CommandPalette';\n\nconst DefaultItemContent: React.FC<{ item: CommandItem }> = ({ item }) => (\n <>\n {item.icon && <span className={itemIconStyle}>{item.icon}</span>}\n <span className={itemTextStyle}>\n <span className={itemLabelStyle}>{item.label}</span>\n {item.description && (\n <span className={itemDescriptionStyle}>{item.description}</span>\n )}\n </span>\n {item.shortcut && (\n <span className={itemShortcutStyle}>\n <Kbd size=\"sm\">{item.shortcut}</Kbd>\n </span>\n )}\n </>\n);\n"],"names":[],"mappings":";;;;;;;;;;;AAuCA;AACA;AAUA;AACE;;AACsB;;;AAEtB;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACI;AAoBL;AACA;AACA;AAEA;AACA;;;;;;AAOA;AAEA;AACE;;AAED;;;;;;;;;;AAWD;;;;AAKI;AACE;;AAEF;;AAEA;;AAEF;AACF;;;;AAKE;;;;AAKE;;AAEE;AAAwB;;AAE1B;AACE;AACA;;;;AAKJ;AAEA;AACE;;AAEE;AACA;;;;;;;AASJ;;AAEE;;AACA;AAEI;;AAEE;;;;AAIF;AACA;;AAED;;AAEH;AACE;;AAEE;AACA;;;;AAID;AACD;;;AAIJ;;;;;AAMF;;;;AAEO;AACL;;AAGF;;AAGM;;AACe;AACf;;AACc;;;AAEd;AACF;AACF;AAIF;;;AAGI;AACA;;;;AAGE;;AAEF;;AAKJ;AAEI;;AAEE;;;AAGF;;;;;AAKA;;AAEE;;;AAGF;;;;;AAKA;;AAEE;;;AAGF;;AAEE;AACA;;;AAEJ;;;AAMA;;AACA;AACA;;AACA;;;;AAMF;AAEA;;AAGM;;AAEJ;AAIF;AAAW;AAEX;AACA;;;;AA8CY;;AAEF;AAqBE;;;AAWA;AACA;AACA;AAAsC;AACtC;;;;;;;AAOE;;;AAYE;;;AAOI;AACA;;AAOV;AAOV;;;AAGA;AACF;AAEA;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fuzzySearch.js","sources":["
|
|
1
|
+
{"version":3,"file":"fuzzySearch.js","sources":["src/components/feedback/CommandPalette/fuzzySearch.ts"],"sourcesContent":["/**\n * Score how well `query` matches `target`. Higher = better. Zero = no match.\n *\n * Heuristics, in priority order:\n * 1. Exact case-insensitive match (1000).\n * 2. Target starts with the query (800 + length bonus).\n * 3. Target contains the query as a contiguous substring (500 + position bonus).\n * 4. Subsequence match: every query char appears in target in order; bonuses\n * for consecutive matches, word-boundary matches, and earlier positions.\n *\n * @example\n * ```ts\n * fuzzyScore('opn', 'open file') // > 0, subsequence match\n * fuzzyScore('zzz', 'open file') // 0\n * ```\n */\nexport function fuzzyScore(query: string, target: string): number {\n if (query === '') return 1;\n if (target === '') return 0;\n\n const q = query.toLowerCase();\n const t = target.toLowerCase();\n\n if (q === t) return 1000;\n if (t.startsWith(q)) return 800 + Math.max(0, 50 - target.length);\n const containsAt = t.indexOf(q);\n if (containsAt !== -1)\n return 500 - containsAt + Math.max(0, 30 - target.length);\n\n let score = 0;\n let qi = 0;\n let lastMatch = -2;\n for (let ti = 0; ti < t.length && qi < q.length; ti++) {\n const ch = t[ti];\n if (ch === q[qi]) {\n // Earlier matches contribute more.\n score += Math.max(1, 30 - ti);\n if (lastMatch === ti - 1) {\n score += 25;\n }\n // Bonus when the match begins a \"word\" (after space/-/_/.)\n const prev = ti > 0 ? t[ti - 1] : '';\n if (\n ti === 0 ||\n prev === ' ' ||\n prev === '-' ||\n prev === '_' ||\n prev === '.'\n ) {\n score += 20;\n }\n lastMatch = ti;\n qi++;\n }\n }\n\n if (qi !== q.length) return 0;\n return score;\n}\n\nexport interface FuzzyMatch<T> {\n item: T;\n score: number;\n}\n\n/**\n * Score a list of items against a query. Items with score 0 are dropped, the\n * rest are sorted by descending score. The `getStrings` accessor returns one\n * or more strings per item to score against; the highest-scoring string wins.\n */\nexport function fuzzyFilter<T>(\n query: string,\n items: readonly T[],\n getStrings: (item: T) => readonly string[]\n): FuzzyMatch<T>[] {\n if (query === '') {\n return items.map(item => ({ item, score: 1 }));\n }\n const result: FuzzyMatch<T>[] = [];\n for (const item of items) {\n const strings = getStrings(item);\n let best = 0;\n for (const s of strings) {\n const score = fuzzyScore(query, s);\n if (score > best) best = score;\n }\n if (best > 0) {\n result.push({ item, score: best });\n }\n }\n result.sort((a, b) => b.score - a.score);\n return result;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRecentItems.js","sources":["
|
|
1
|
+
{"version":3,"file":"useRecentItems.js","sources":["src/components/feedback/CommandPalette/useRecentItems.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\n\ninterface UseRecentItemsOptions {\n /** localStorage key (component does not bind/store unless this is set). */\n storageKey?: string;\n /** Maximum number of recent ids to keep. @default 5 */\n maxRecent?: number;\n}\n\ninterface UseRecentItemsReturn {\n recentIds: string[];\n pushRecent: (id: string) => void;\n clearRecent: () => void;\n}\n\nfunction readStorage(key: string): string[] {\n if (typeof window === 'undefined') return [];\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return [];\n const parsed: unknown = JSON.parse(raw);\n if (Array.isArray(parsed)) {\n return parsed.filter((v): v is string => typeof v === 'string');\n }\n return [];\n } catch {\n return [];\n }\n}\n\nfunction writeStorage(key: string, ids: string[]): void {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(key, JSON.stringify(ids));\n } catch {\n // localStorage may be unavailable (private mode, full quota); silently ignore.\n }\n}\n\n/**\n * Track recently selected command ids. Backed by localStorage when\n * `storageKey` is provided; otherwise the list is in-memory only.\n */\nexport function useRecentItems({\n storageKey,\n maxRecent = 5,\n}: UseRecentItemsOptions): UseRecentItemsReturn {\n const [recentIds, setRecentIds] = useState<string[]>(() =>\n storageKey ? readStorage(storageKey).slice(0, maxRecent) : []\n );\n\n // Re-read storage if the key changes after mount. External-system sync\n // (localStorage), so setState in the effect is the correct pattern.\n useEffect(() => {\n if (storageKey) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setRecentIds(readStorage(storageKey).slice(0, maxRecent));\n } else {\n setRecentIds([]);\n }\n }, [storageKey, maxRecent]);\n\n const pushRecent = useCallback(\n (id: string) => {\n setRecentIds(prev => {\n const next = [id, ...prev.filter(v => v !== id)].slice(0, maxRecent);\n if (storageKey) writeStorage(storageKey, next);\n return next;\n });\n },\n [storageKey, maxRecent]\n );\n\n const clearRecent = useCallback(() => {\n setRecentIds([]);\n if (storageKey) writeStorage(storageKey, []);\n }, [storageKey]);\n\n return { recentIds, pushRecent, clearRecent };\n}\n"],"names":[],"mappings":";;;AAiBA;;AACqC;AACnC;;AAEE;AAAU;;AAEV;AACE;;AAEF;;AACA;AACA;;AAEJ;AAEA;;;AAEE;AACE;;AACA;;;AAGJ;AAEA;;;AAGG;AACG;AAIJ;;;;;;AASI;;;;;AAIJ;AAEA;;;AAIM;AAAgB;AAChB;AACF;AACF;AAIF;;AAEE;AAAgB;AAClB;AAEA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.js","sources":["
|
|
1
|
+
{"version":3,"file":"Dialog.js","sources":["src/components/feedback/Dialog/Dialog.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useId,\n useRef,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useFocusTrap } from '@/hooks/useFocusTrap';\nimport { useMergedRef } from '@/hooks/useMergedRef';\nimport { cx } from '@/utils/cx';\n\nimport { overlayRecipe, dialogPanelRecipe } from './Dialog.css';\nimport { useDialogAnimation } from './useDialogAnimation';\n\nimport type { DialogContextValue, DialogProps } from './Dialog.types';\n\n// --- Context ---\n\nconst DialogContext = /*#__PURE__*/ createContext<DialogContextValue | null>(\n null\n);\n\nexport function useDialogContext(): DialogContextValue {\n const ctx = useContext(DialogContext);\n if (!ctx) {\n throw new Error(\n 'Dialog sub-components must be used within a <Dialog> component'\n );\n }\n return ctx;\n}\n\n// --- Component ---\n\n/**\n * Dialog component for modal overlays in editor interfaces.\n *\n * Renders an accessible modal dialog with overlay, focus trap,\n * and keyboard support. Compound component pattern: use with\n * DialogHeader, DialogBody, DialogFooter, and DialogClose.\n *\n * @example\n * ```tsx\n * <Dialog open={isOpen} onClose={() => setIsOpen(false)} title=\"Confirm\">\n * <DialogHeader>Confirm Action</DialogHeader>\n * <DialogBody>Are you sure?</DialogBody>\n * <DialogFooter align=\"right\">\n * <Button onClick={() => setIsOpen(false)}>Cancel</Button>\n * <Button variant=\"filled\">Confirm</Button>\n * </DialogFooter>\n * </Dialog>\n * ```\n */\nexport const Dialog: React.FC<DialogProps> = ({\n open,\n onClose,\n size = 'md',\n title,\n description,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showOverlay = true,\n trapFocus = true,\n initialFocusRef,\n portal = true,\n children,\n className,\n style,\n testId,\n id,\n ref,\n ...rest\n}) => {\n const autoId = useId();\n const titleId = `dialog-title-${autoId}`;\n const descriptionId = `dialog-desc-${autoId}`;\n const panelRef = useRef<HTMLDivElement>(null);\n\n const { mounted, closing } = useDialogAnimation({\n open,\n panelRef,\n initialFocusRef,\n });\n\n const handleFocusTrap = useFocusTrap({\n containerRef: panelRef,\n enabled: trapFocus,\n });\n\n const setPanelRef = useMergedRef<HTMLDivElement>(panelRef, ref);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n // Escape handler — inline instead of global document listener\n if (closeOnEscape && e.key === 'Escape') {\n e.stopPropagation();\n onClose();\n return;\n }\n // Focus trap\n handleFocusTrap(e);\n },\n [closeOnEscape, onClose, handleFocusTrap]\n );\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (closeOnOverlayClick && e.target === e.currentTarget) {\n onClose();\n }\n },\n [closeOnOverlayClick, onClose]\n );\n\n if (!mounted) return null;\n\n const contextValue: DialogContextValue = {\n onClose,\n titleId,\n descriptionId,\n };\n\n const dialogContent = (\n <DialogContext.Provider value={contextValue}>\n {showOverlay && (\n // Decorative backdrop: pointer users can click to dismiss, while\n // keyboard users dismiss via Escape handled on the dialog panel.\n <div\n aria-hidden=\"true\"\n className={overlayRecipe({ closing })}\n onClick={handleOverlayClick}\n data-testid={testId ? `${testId}-overlay` : undefined}\n />\n )}\n {/* The dialog panel needs a keydown handler for Escape-to-close and\n focus trapping; this is required modal-dialog behavior, not a static\n element repurposed as interactive. */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <div\n ref={setPanelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={description ? descriptionId : undefined}\n tabIndex={-1}\n className={cx(dialogPanelRecipe({ size, closing }), className)}\n style={style}\n id={id}\n data-testid={testId}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {/* Hidden elements for ARIA references when using title/description props */}\n {title && (\n <span id={titleId} style={{ display: 'none' }}>\n {title}\n </span>\n )}\n {description && (\n <span id={descriptionId} style={{ display: 'none' }}>\n {description}\n </span>\n )}\n {children}\n </div>\n </DialogContext.Provider>\n );\n\n if (portal) {\n return createPortal(dialogContent, document.body);\n }\n\n return dialogContent;\n};\n\nDialog.displayName = 'Dialog';\n"],"names":[],"mappings":";;;;;;;;;;AAoBA;AAEA;;AAKE;;AAEE;;AAIF;AACF;AAEA;AAEA;;;;;;;;;;;;;;;;;;AAkBG;AACI;AAoBL;AACA;AACA;AACA;AAEA;;;;AAIC;;AAGC;AACA;AACD;;AAID;;;;AAKM;;;;;;AASN;;AAGM;;AAEJ;AAIF;AAAc;AAEd;;;;;AAMA;;;AAKM;;;;AA6CN;AACF;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DialogBody.js","sources":["
|
|
1
|
+
{"version":3,"file":"DialogBody.js","sources":["src/components/feedback/Dialog/DialogBody.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { dialogBodyStyle } from './Dialog.css';\n\nimport type { DialogBodyProps } from './Dialog.types';\n\n// --- Component ---\n\n/**\n * DialogBody renders the scrollable content area of a Dialog.\n *\n * @example\n * ```tsx\n * <DialogBody>\n * <p>Dialog content goes here.</p>\n * </DialogBody>\n * ```\n */\nexport const DialogBody: React.FC<DialogBodyProps> = ({\n children,\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n return (\n <div\n ref={ref}\n className={cx(dialogBodyStyle, className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n {children}\n </div>\n );\n};\n\nDialogBody.displayName = 'DialogBody';\n"],"names":[],"mappings":";;;;;AAUA;AAEA;;;;;;;;;AASG;;;AAoBH;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DialogClose.js","sources":["
|
|
1
|
+
{"version":3,"file":"DialogClose.js","sources":["src/components/feedback/Dialog/DialogClose.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { useDialogContext } from './Dialog';\n\ninterface DialogCloseProps {\n /** The element to wrap with close behavior */\n children: React.ReactElement<{ onClick?: React.MouseEventHandler }>;\n}\n\n/**\n * DialogClose wraps a child element and adds onClick to close the dialog.\n *\n * @example\n * ```tsx\n * <DialogClose>\n * <Button>Cancel</Button>\n * </DialogClose>\n * ```\n */\nexport const DialogClose: React.FC<DialogCloseProps> = ({ children }) => {\n const { onClose } = useDialogContext();\n\n return React.cloneElement(children, {\n onClick: (e: React.MouseEvent) => {\n children.props.onClick?.(e);\n onClose();\n },\n });\n};\n\nDialogClose.displayName = 'DialogClose';\n"],"names":[],"mappings":";;;;AAWA;;;;;;;;;AASG;;AAED;AAEA;AACE;;AAEE;;AAEH;AACH;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DialogFooter.js","sources":["
|
|
1
|
+
{"version":3,"file":"DialogFooter.js","sources":["src/components/feedback/Dialog/DialogFooter.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { dialogFooterRecipe } from './Dialog.css';\n\nimport type { DialogFooterProps } from './Dialog.types';\n\n// --- Component ---\n\n/**\n * DialogFooter renders the action area at the bottom of a Dialog.\n *\n * @example\n * ```tsx\n * <DialogFooter align=\"right\">\n * <Button onClick={onCancel}>Cancel</Button>\n * <Button variant=\"filled\" onClick={onConfirm}>Confirm</Button>\n * </DialogFooter>\n * ```\n */\nexport const DialogFooter: React.FC<DialogFooterProps> = ({\n children,\n align = 'right',\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n return (\n <div\n ref={ref}\n className={cx(dialogFooterRecipe({ align }), className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n {children}\n </div>\n );\n};\n\nDialogFooter.displayName = 'DialogFooter';\n"],"names":[],"mappings":";;;;;AAUA;AAEA;;;;;;;;;;AAUG;AACI;AASL;AAWF;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DialogHeader.js","sources":["
|
|
1
|
+
{"version":3,"file":"DialogHeader.js","sources":["src/components/feedback/Dialog/DialogHeader.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { CloseIcon } from '@/components/Icons/CloseIcon';\nimport { cx } from '@/utils/cx';\n\nimport { useDialogContext } from './Dialog';\nimport {\n dialogHeaderStyle,\n dialogHeaderContentStyle,\n dialogTitleStyle,\n dialogDescriptionStyle,\n dialogCloseButtonStyle,\n} from './Dialog.css';\n\nimport type { DialogHeaderProps } from './Dialog.types';\n\n// --- Component ---\n\n/**\n * DialogHeader renders the title, optional description, and close button\n * for a Dialog.\n *\n * @example\n * ```tsx\n * <DialogHeader description=\"This cannot be undone\">\n * Delete Item\n * </DialogHeader>\n * ```\n */\nexport const DialogHeader: React.FC<DialogHeaderProps> = ({\n children,\n showClose = true,\n description,\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const { onClose, titleId, descriptionId } = useDialogContext();\n\n return (\n <div\n ref={ref}\n className={cx(dialogHeaderStyle, className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n <div className={dialogHeaderContentStyle}>\n <div id={titleId} className={dialogTitleStyle}>\n {children}\n </div>\n {description && (\n <div id={descriptionId} className={dialogDescriptionStyle}>\n {description}\n </div>\n )}\n </div>\n {showClose && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close dialog\"\n className={dialogCloseButtonStyle}\n data-testid={testId ? `${testId}-close` : undefined}\n >\n <CloseIcon size=\"sm\" decorative />\n </button>\n )}\n </div>\n );\n};\n\nDialogHeader.displayName = 'DialogHeader';\n"],"names":[],"mappings":";;;;;;;AAkBA;AAEA;;;;;;;;;;AAUG;AACI;;;AA2CP;AAEA;;"}
|