dishui 0.0.106 → 0.0.110
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/dist/.release-verified.json +3 -2
- package/dist/components/Accordion/index.js.map +1 -1
- package/dist/components/Avatar/index.js.map +1 -1
- package/dist/components/Badge/index.js.map +1 -1
- package/dist/components/Button/index.js.map +1 -1
- package/dist/components/ButtonMenu/index.js.map +1 -1
- package/dist/components/Card/Metric2.js.map +1 -1
- package/dist/components/Card/Ticket2.js.map +1 -1
- package/dist/components/Card/index.js.map +1 -1
- package/dist/components/Chart/Chart.js.map +1 -1
- package/dist/components/Checkbox/index.js.map +1 -1
- package/dist/components/ChoicePicker/index.js.map +1 -1
- package/dist/components/Column/index.js.map +1 -1
- package/dist/components/ConfirmDialog/index.js.map +1 -1
- package/dist/components/DateTimeInput/index.js.map +1 -1
- package/dist/components/Demo/index.js.map +1 -1
- package/dist/components/Demo/source.js.map +1 -1
- package/dist/components/Divider/index.js.map +1 -1
- package/dist/components/EmptyState/index.js.map +1 -1
- package/dist/components/Field/index.js.map +1 -1
- package/dist/components/FileUploader/DropZone.js.map +1 -1
- package/dist/components/FileUploader/FileItem.js.map +1 -1
- package/dist/components/FileUploader/FileUploader.js.map +1 -1
- package/dist/components/FileUploader/UploadQueue.js.map +1 -1
- package/dist/components/GroupButton/index.js.map +1 -1
- package/dist/components/GroupedTable/index.js.map +1 -1
- package/dist/components/Icon/index.js.map +1 -1
- package/dist/components/Image/index.js.map +1 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/Label/index.js.map +1 -1
- package/dist/components/Layout/index.js.map +1 -1
- package/dist/components/LayoutSuite/DashboardSuite.js.map +1 -1
- package/dist/components/LayoutSuite/index.js.map +1 -1
- package/dist/components/List/index.js.map +1 -1
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Markdown/codeThemes.js.map +1 -1
- package/dist/components/Markdown/markdownComponents.js.map +1 -1
- package/dist/components/Markdown/parseCodeBlockMeta.js.map +1 -1
- package/dist/components/Markdown/useMarkdownSource.js.map +1 -1
- package/dist/components/Markdown/useMarkmapRenderer.js.map +1 -1
- package/dist/components/Markdown/useMermaidRenderer.js.map +1 -1
- package/dist/components/Modal/focusScope.js.map +1 -1
- package/dist/components/Modal/index.js.map +1 -1
- package/dist/components/MultipleChoice/index.js.map +1 -1
- package/dist/components/NavigationList/index.js.map +1 -1
- package/dist/components/Page/Float2.js.map +1 -1
- package/dist/components/Page/index.js.map +1 -1
- package/dist/components/Page/resize2.js.map +1 -1
- package/dist/components/PageHeader/index.js.map +1 -1
- package/dist/components/PagePanel/index.js.map +1 -1
- package/dist/components/ProtectedRoute/index.js.map +1 -1
- package/dist/components/Resizable/ResizeGrip.js.map +1 -1
- package/dist/components/Resizable/index.js.map +1 -1
- package/dist/components/Resizable/useResize.js.map +1 -1
- package/dist/components/Row/index.js.map +1 -1
- package/dist/components/Section/index.js.map +1 -1
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/SidebarToggleButton/index.js.map +1 -1
- package/dist/components/Skeleton/index.js.map +1 -1
- package/dist/components/Slider/index.js.map +1 -1
- package/dist/components/Switch/index.js.map +1 -1
- package/dist/components/Tab/index.js.map +1 -1
- package/dist/components/Table/index.js.map +1 -1
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Tabs/tabNavigation.js.map +1 -1
- package/dist/components/Text/index.js.map +1 -1
- package/dist/components/Textarea/index.js.map +1 -1
- package/dist/components/Tooltip/index.js.map +1 -1
- package/dist/components/Tree/Tree.js.map +1 -1
- package/dist/components/ai/AgentChat/index.js.map +1 -1
- package/dist/components/ai/AgentRunCard/index.js.map +1 -1
- package/dist/components/ai/AgentScreen/index.js.map +1 -1
- package/dist/components/ai/ApprovalCard/index.js.map +1 -1
- package/dist/components/ai/CodeView/index.js.map +1 -1
- package/dist/components/ai/CommandSearch/index.js.map +1 -1
- package/dist/components/ai/ContextCards/index.js.map +1 -1
- package/dist/components/ai/DiffTable/index.js.map +1 -1
- package/dist/components/ai/FilterTable/index.js.map +1 -1
- package/dist/components/ai/FineTuneCard/index.js.map +1 -1
- package/dist/components/ai/InsightCards/index.js.map +1 -1
- package/dist/components/ai/IssueCard/index.js.map +1 -1
- package/dist/components/ai/LoadingState/index.js.map +1 -1
- package/dist/components/ai/PromptBar/index.js.map +1 -1
- package/dist/components/ai/RecommendationCard/index.js.map +1 -1
- package/dist/components/ai/RecordsTable/index.js.map +1 -1
- package/dist/components/ai/SelectionActions/index.js.map +1 -1
- package/dist/components/ai/StateFlow/index.js.map +1 -1
- package/dist/components/ai/StreamingText/index.js.map +1 -1
- package/dist/components/ai/TaskRows/index.js.map +1 -1
- package/dist/components/ai/Thinking/index.js.map +1 -1
- package/dist/components/ai/ToolChips/index.js.map +1 -1
- package/dist/components/ai/shared.js.map +1 -1
- package/dist/components/pro/ArticleContextMenu/index.js.map +1 -1
- package/dist/components/pro/ArticleControl/ArticleControl2.js.map +1 -1
- package/dist/components/pro/ArticleControl/buildHeadingTree.js.map +1 -1
- package/dist/components/pro/ArticleControl/useArticleToc.js.map +1 -1
- package/dist/components/pro/Calendar/Calendar.js +165 -160
- package/dist/components/pro/Calendar/Calendar.js.map +1 -1
- package/dist/components/pro/Calendar/hooks/useCalendarDrag.js.map +1 -1
- package/dist/components/pro/Calendar/hooks/useCalendarState.js +31 -31
- package/dist/components/pro/Calendar/hooks/useCalendarState.js.map +1 -1
- package/dist/components/pro/Calendar/utils.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/CodeEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/markdownExtensions.js.map +1 -1
- package/dist/components/pro/CodeEditor/extensions/contextMenu.js.map +1 -1
- package/dist/components/pro/CodeEditor/extensions/index.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/index.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/javascript.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/python.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/snippets.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/FootnotePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/HistoryPanel.js +65 -59
- package/dist/components/pro/CodeEditor/panels/HistoryPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/TableOfContentsPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/VariablePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/themes.js.map +1 -1
- package/dist/components/pro/CodeEditor/utils.js.map +1 -1
- package/dist/components/pro/DataTable/DataTable.js.map +1 -1
- package/dist/components/pro/DataTable/Filters.js.map +1 -1
- package/dist/components/pro/DataTable/PivotPanel.js.map +1 -1
- package/dist/components/pro/DataTable/PivotView.js.map +1 -1
- package/dist/components/pro/DataTable/components/BulkActions.js.map +1 -1
- package/dist/components/pro/DataTable/components/ColumnSelector.js.map +1 -1
- package/dist/components/pro/DataTable/components/FilterBar.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableBody.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableHeader.js.map +1 -1
- package/dist/components/pro/DataTable/components/TablePagination.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableToolbar.js.map +1 -1
- package/dist/components/pro/DataTable/extension.js.map +1 -1
- package/dist/components/pro/DataTable/pivotEngine.js.map +1 -1
- package/dist/components/pro/DataTable/pivotExtension.js.map +1 -1
- package/dist/components/pro/DataTable/rowDetailExtension.js.map +1 -1
- package/dist/components/pro/DataTable/tanstack-table.js.map +1 -1
- package/dist/components/pro/DataTable/treeDataUtils.js.map +1 -1
- package/dist/components/pro/DataTable/useColumnDragReorder.js.map +1 -1
- package/dist/components/pro/DataTable/useDataTableModel.js +119 -104
- package/dist/components/pro/DataTable/useDataTableModel.js.map +1 -1
- package/dist/components/pro/DataTable/utils.js.map +1 -1
- package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
- package/dist/components/pro/FileExplorer/CreateDialog.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileExplorer.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileIcon.js.map +1 -1
- package/dist/components/pro/FileExplorer/GridView.js.map +1 -1
- package/dist/components/pro/FileExplorer/ListView.js.map +1 -1
- package/dist/components/pro/FileExplorer/Toolbar.js.map +1 -1
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/pro/Flow/archify/build.js.map +1 -1
- package/dist/components/pro/Flow/archify/convert.js.map +1 -1
- package/dist/components/pro/Flow/archify/palette.js.map +1 -1
- package/dist/components/pro/Flow/archify/presets.js.map +1 -1
- package/dist/components/pro/Flow/archify/spec.js.map +1 -1
- package/dist/components/pro/Flow/canvas/AlignmentGuides.js.map +1 -1
- package/dist/components/pro/Flow/canvas/GridRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/MiniMapRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/PixiApp.js.map +1 -1
- package/dist/components/pro/Flow/canvas/SelectionOverlay.js.map +1 -1
- package/dist/components/pro/Flow/canvas/ViewportContainer.js.map +1 -1
- package/dist/components/pro/Flow/constants.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowAIContext.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowContext.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
- package/dist/components/pro/Flow/edges/EdgeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/edges/edgeMarkers.js.map +1 -1
- package/dist/components/pro/Flow/edges/edgePaths.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useAlignmentGuides.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useClipboard.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useCompactMode.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useDeleteSelection.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useDraggablePanel.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useEdgeMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useFlowImportExport.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useKeyboardShortcuts.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLineMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useMindMap.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useNodeMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePdfExport.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePixiApp.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useResizablePanel.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useSelection.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useUndoRedo.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useViewport.js.map +1 -1
- package/dist/components/pro/Flow/lines/FreehandTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/GeometricLineTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/LineRenderer.js.map +1 -1
- package/dist/components/pro/Flow/lines/lineSmoothing.js.map +1 -1
- package/dist/components/pro/Flow/nodes/DataNodeView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/Flow/nodes/Model3DView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeHandles.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeText.js.map +1 -1
- package/dist/components/pro/Flow/nodes/aiClient.js.map +1 -1
- package/dist/components/pro/Flow/nodes/animAnchors.js.map +1 -1
- package/dist/components/pro/Flow/nodes/chartGeometryBudget.js.map +1 -1
- package/dist/components/pro/Flow/nodes/dataUtils.js.map +1 -1
- package/dist/components/pro/Flow/nodes/demandRender.js.map +1 -1
- package/dist/components/pro/Flow/nodes/nodeUtils.js.map +1 -1
- package/dist/components/pro/Flow/nodes/shapes.js.map +1 -1
- package/dist/components/pro/Flow/nodes/svgIconTexture.js.map +1 -1
- package/dist/components/pro/Flow/nodes/svgIcons.js.map +1 -1
- package/dist/components/pro/Flow/panels/AIPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/Breadcrumb.js.map +1 -1
- package/dist/components/pro/Flow/panels/ContextMenu.js.map +1 -1
- package/dist/components/pro/Flow/panels/DataInspectorSection.js.map +1 -1
- package/dist/components/pro/Flow/panels/ExpandableTextarea.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowToolbar.js.map +1 -1
- package/dist/components/pro/Flow/panels/InspectorPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/PresentationMode.js.map +1 -1
- package/dist/components/pro/Flow/panels/ProjectTitle.js.map +1 -1
- package/dist/components/pro/Flow/panels/ShapePalette.js.map +1 -1
- package/dist/components/pro/Flow/panels/toolbarIcons.js.map +1 -1
- package/dist/components/pro/Flow/types.js.map +1 -1
- package/dist/components/pro/Flow/utils/dashedLine.js.map +1 -1
- package/dist/components/pro/Flow/utils/gradient.js.map +1 -1
- package/dist/components/pro/Flow/utils/pdfExport.js.map +1 -1
- package/dist/components/pro/Flow/utils/themeColor.js.map +1 -1
- package/dist/components/pro/Gantt/Gantt.js.map +1 -1
- package/dist/components/pro/Gantt/hooks/useGanttDrag.js.map +1 -1
- package/dist/components/pro/Gantt/hooks/useGanttState.js +79 -71
- package/dist/components/pro/Gantt/hooks/useGanttState.js.map +1 -1
- package/dist/components/pro/Gantt/utils.js.map +1 -1
- package/dist/components/pro/Kanban/Kanban.js.map +1 -1
- package/dist/components/pro/Kanban/Swimlanes.js.map +1 -1
- package/dist/components/pro/Kanban/useKanbanDrag.js.map +1 -1
- package/dist/components/pro/Kanban/utils.js.map +1 -1
- package/dist/components/pro/RichTextEditor/AIRewrite.d.ts +1 -0
- package/dist/components/pro/RichTextEditor/AIRewrite.js +109 -88
- package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BlockHandle.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPanel.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPopover.js.map +1 -1
- package/dist/components/pro/RichTextEditor/ContextMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/RichTextEditor.js +133 -132
- package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
- package/dist/components/pro/RichTextEditor/SlashMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CommentMark.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/FloatingDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/FontSize.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/MarkdownBlock.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/ResizableImage.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/legacyMermaid.js.map +1 -1
- package/dist/components/pro/RichTextEditor/types.d.ts +2 -0
- package/dist/components/pro/RichTextEditor/types.js.map +1 -1
- package/dist/components/pro/Sprite/Molly/animations.d.ts +6 -0
- package/dist/components/pro/Sprite/Molly/animations.js +52 -0
- package/dist/components/pro/Sprite/Molly/animations.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/events.d.ts +2 -0
- package/dist/components/pro/Sprite/Molly/events.js +46 -0
- package/dist/components/pro/Sprite/Molly/events.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/index.d.ts +1 -0
- package/dist/components/pro/Sprite/Molly/index.js +12 -0
- package/dist/components/pro/Sprite/Molly/index.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.d.ts +3 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.js +48 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/locomotion.d.ts +4 -0
- package/dist/components/pro/Sprite/Molly/locomotion.js +51 -0
- package/dist/components/pro/Sprite/Molly/locomotion.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/model.d.ts +23 -0
- package/dist/components/pro/Sprite/Molly/model.js +614 -0
- package/dist/components/pro/Sprite/Molly/model.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/rig.d.ts +25 -0
- package/dist/components/pro/Sprite/Molly/rig.js +102 -0
- package/dist/components/pro/Sprite/Molly/rig.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/animations.d.ts +6 -0
- package/dist/components/pro/Sprite/Ruby/animations.js +58 -0
- package/dist/components/pro/Sprite/Ruby/animations.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/events.d.ts +2 -0
- package/dist/components/pro/Sprite/Ruby/events.js +46 -0
- package/dist/components/pro/Sprite/Ruby/events.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/index.d.ts +1 -0
- package/dist/components/pro/Sprite/Ruby/index.js +12 -0
- package/dist/components/pro/Sprite/Ruby/index.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.d.ts +4 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.js +41 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/model.d.ts +23 -0
- package/dist/components/pro/Sprite/Ruby/model.js +605 -0
- package/dist/components/pro/Sprite/Ruby/model.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/rig.d.ts +26 -0
- package/dist/components/pro/Sprite/Ruby/rig.js +112 -0
- package/dist/components/pro/Sprite/Ruby/rig.js.map +1 -0
- package/dist/components/pro/Sprite/Sprite.d.ts +23 -0
- package/dist/components/pro/Sprite/Sprite2.js +224 -0
- package/dist/components/pro/Sprite/Sprite2.js.map +1 -0
- package/dist/components/pro/Sprite/actions.d.ts +3 -0
- package/dist/components/pro/Sprite/actions.js +16 -0
- package/dist/components/pro/Sprite/actions.js.map +1 -0
- package/dist/components/pro/Sprite/createSpriteScene.d.ts +4 -0
- package/dist/components/pro/Sprite/createSpriteScene.js +14 -0
- package/dist/components/pro/Sprite/createSpriteScene.js.map +1 -0
- package/dist/components/pro/Sprite/index.d.ts +4 -0
- package/dist/components/pro/Sprite/shared/animation.d.ts +33 -0
- package/dist/components/pro/Sprite/shared/animation.js +39 -0
- package/dist/components/pro/Sprite/shared/animation.js.map +1 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.d.ts +14 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.js +127 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.js.map +1 -0
- package/dist/components/pro/Sprite/shared/events.d.ts +5 -0
- package/dist/components/pro/Sprite/shared/events.js +21 -0
- package/dist/components/pro/Sprite/shared/events.js.map +1 -0
- package/dist/components/pro/Sprite/shared/face.d.ts +15 -0
- package/dist/components/pro/Sprite/shared/face.js +33 -0
- package/dist/components/pro/Sprite/shared/face.js.map +1 -0
- package/dist/components/pro/Sprite/shared/faceSurface.d.ts +6 -0
- package/dist/components/pro/Sprite/shared/faceSurface.js +66 -0
- package/dist/components/pro/Sprite/shared/faceSurface.js.map +1 -0
- package/dist/components/pro/Sprite/shared/gait.d.ts +5 -0
- package/dist/components/pro/Sprite/shared/gait.js +19 -0
- package/dist/components/pro/Sprite/shared/gait.js.map +1 -0
- package/dist/components/pro/Sprite/shared/geometry.d.ts +8 -0
- package/dist/components/pro/Sprite/shared/geometry.js +59 -0
- package/dist/components/pro/Sprite/shared/geometry.js.map +1 -0
- package/dist/components/pro/Sprite/shared/magicEffect.d.ts +27 -0
- package/dist/components/pro/Sprite/shared/magicEffect.js +61 -0
- package/dist/components/pro/Sprite/shared/magicEffect.js.map +1 -0
- package/dist/components/pro/Sprite/shared/playback.d.ts +8 -0
- package/dist/components/pro/Sprite/shared/playback.js +74 -0
- package/dist/components/pro/Sprite/shared/playback.js.map +1 -0
- package/dist/components/pro/Sprite/shared/rig.d.ts +6 -0
- package/dist/components/pro/Sprite/shared/rig.js +48 -0
- package/dist/components/pro/Sprite/shared/rig.js.map +1 -0
- package/dist/components/pro/Sprite/shared/scene.d.ts +9 -0
- package/dist/components/pro/Sprite/shared/scene.js +21 -0
- package/dist/components/pro/Sprite/shared/scene.js.map +1 -0
- package/dist/components/pro/Sprite/shared/sculptor.d.ts +14 -0
- package/dist/components/pro/Sprite/shared/sculptor.js +78 -0
- package/dist/components/pro/Sprite/shared/sculptor.js.map +1 -0
- package/dist/components/pro/Sprite/shared/speechBubble.d.ts +15 -0
- package/dist/components/pro/Sprite/shared/speechBubble.js +129 -0
- package/dist/components/pro/Sprite/shared/speechBubble.js.map +1 -0
- package/dist/components/pro/Sprite/types.d.ts +40 -0
- package/dist/contexts/AppContext.js.map +1 -1
- package/dist/contexts/authentication.js.map +1 -1
- package/dist/data-table.js +3 -3
- package/dist/data-table.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/hooks/useClickOutside.js.map +1 -1
- package/dist/hooks/useContainerElement.js.map +1 -1
- package/dist/hooks/useControllableState.js.map +1 -1
- package/dist/hooks/useCopyToClipboard.js.map +1 -1
- package/dist/hooks/useDebounce.js.map +1 -1
- package/dist/hooks/useDraggable.js.map +1 -1
- package/dist/hooks/useTableColumns.js.map +1 -1
- package/dist/hooks/useTableData.js.map +1 -1
- package/dist/index-nostyled.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +116 -115
- package/dist/layouts/MainLayout.js.map +1 -1
- package/dist/layouts/Navbar.js.map +1 -1
- package/dist/layouts/Sidebar.js.map +1 -1
- package/dist/layouts/headbar/Headbar.js.map +1 -1
- package/dist/layouts/headbar/HeadbarLangSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarSearch.js.map +1 -1
- package/dist/layouts/headbar/HeadbarThemeSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarTitle.js.map +1 -1
- package/dist/sprite.js +2 -0
- package/dist/styles/themeStyles.js.map +1 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/flexLayout.js.map +1 -1
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/menu.js.map +1 -1
- package/dist/utils/rehype-code-meta.js.map +1 -1
- package/dist/utils/remark-code-meta.js.map +1 -1
- package/dist/utils/routerBuilder.js.map +1 -1
- package/dist/utils/safeStorage.js.map +1 -1
- package/dist/utils/selection.js +29 -20
- package/dist/utils/selection.js.map +1 -1
- package/dist/utils/themes.js.map +1 -1
- package/package.json +15 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFlowImportExport.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useFlowImportExport.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowDocument, FlowCanvasData, FlowNode, FlowEdge } from '../types';\nimport { createEmptyDocument, ROOT_CANVAS_ID } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\nimport { isArchifySpec, archifyToFlowDocument } from '../archify';\n\nconst PERSIST_VERSION = 1;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDocument(value: unknown): value is FlowDocument {\n if (!isRecord(value) || !isRecord(value.canvases) || !Array.isArray(value.stack)) return false;\n return value.stack.every(id => typeof id === 'string')\n && (value.title === undefined || typeof value.title === 'string')\n && Object.values(value.canvases).every(canvas => isRecord(canvas)\n && Array.isArray(canvas.nodes) && Array.isArray(canvas.edges) && Array.isArray(canvas.lines));\n}\n\ninterface SerializedDocument {\n version: number;\n canvases: Record<string, FlowCanvasData>;\n stack: string[];\n title?: string;\n}\n\n/**\n * Sanitize a project title into a safe file base name, falling back to a\n * default when empty. Shared by all export paths so filenames stay consistent.\n */\nexport function safeFileBaseName(title: string | undefined, fallback = 'flowchart'): string {\n const name = (title ?? '').trim().replace(/[\\\\/:*?\"<>|]+/g, '-').replace(/\\s+/g, ' ').trim();\n return name || fallback;\n}\n\n/**\n * Serialize/deserialize FlowDocument to/from JSON.\n * Also includes Mermaid flowchart import.\n */\nexport function useFlowImportExport() {\n const { doc, setDoc, setSelection } = useFlowContext();\n\n const replaceDocument = useCallback((next: FlowDocument) => {\n setDoc(next);\n setSelection({ nodeIds: new Set(), edgeIds: new Set(), lineIds: new Set() });\n }, [setDoc, setSelection]);\n\n const exportJSON = useCallback((): string => {\n const serialized: SerializedDocument = {\n version: PERSIST_VERSION,\n canvases: doc.canvases,\n stack: doc.stack,\n title: doc.title,\n };\n return JSON.stringify(serialized, null, 2);\n }, [doc]);\n\n const importJSON = useCallback((json: string) => {\n try {\n const parsed = JSON.parse(json) as unknown;\n // Auto-detect an Archify spec and convert it to a Flow document so the\n // same \"import JSON\" entry point accepts both native and Archify files.\n if (isArchifySpec(parsed)) {\n replaceDocument(archifyToFlowDocument(parsed));\n return;\n }\n if (!isDocument(parsed)) throw new Error('Invalid document format');\n const doc = parsed;\n replaceDocument({\n canvases: doc.canvases,\n stack: doc.stack,\n title: doc.title,\n });\n } catch (err) {\n console.error('Failed to import JSON:', err);\n }\n }, [replaceDocument]);\n\n /**\n * Import an Archify diagram spec (architecture / workflow / sequence /\n * dataflow / lifecycle), converting it to a native Flow document. Accepts a\n * JSON string or an already-parsed object. Returns true on success.\n */\n const importArchify = useCallback((input: string | object): boolean => {\n try {\n const spec = typeof input === 'string' ? JSON.parse(input) : input;\n if (!isArchifySpec(spec)) return false;\n replaceDocument(archifyToFlowDocument(spec));\n return true;\n } catch (err) {\n console.error('Failed to import Archify spec:', err);\n return false;\n }\n }, [replaceDocument]);\n\n const importMermaid = useCallback((mermaidCode: string) => {\n const doc = parseMermaidFlowchart(mermaidCode);\n replaceDocument(doc);\n }, [replaceDocument]);\n\n const exportMarkdown = useCallback((): string => {\n return documentToMarkdown(doc);\n }, [doc]);\n\n const clearDocument = useCallback(() => {\n replaceDocument(createEmptyDocument());\n }, [replaceDocument]);\n\n return { exportJSON, importJSON, importArchify, importMermaid, exportMarkdown, clearDocument };\n}\n\n/**\n * Render a FlowDocument as a Markdown outline describing the node hierarchy\n * and connections. Groups become sections with their children nested beneath;\n * edges are listed as a connection summary per canvas. Sub-canvases (drill-down\n * nodes) are rendered as nested sections.\n */\nfunction documentToMarkdown(doc: FlowDocument): string {\n const lines: string[] = [];\n\n const nodeLabel = (n: FlowNode): string => {\n const raw = (n.data.label ?? '').trim();\n if (raw) return raw.replace(/\\s+/g, ' ');\n if (n.type === 'mediaNode') return `(${n.data.mediaKind ?? 'media'})`;\n return `(${n.type})`;\n };\n\n const renderCanvas = (canvasId: string, headingLevel: number, seenCanvases: Set<string>) => {\n const canvas = doc.canvases[canvasId];\n if (!canvas) return;\n\n // Index nodes by id and by parent for hierarchy traversal.\n const byId = new Map(canvas.nodes.map((n) => [n.id, n]));\n const childrenOf = new Map<string | undefined, FlowNode[]>();\n for (const n of canvas.nodes) {\n const key = n.parentId && byId.has(n.parentId) ? n.parentId : undefined;\n const list = childrenOf.get(key) ?? [];\n list.push(n);\n childrenOf.set(key, list);\n }\n\n const emitNode = (node: FlowNode, depth: number) => {\n const indent = ' '.repeat(depth);\n const isGroup = node.data.isGroup || node.type === 'flowGroup' || node.type === 'geometryGroup';\n const bullet = isGroup ? `**${nodeLabel(node)}**` : nodeLabel(node);\n lines.push(`${indent}- ${bullet}`);\n\n // Media content (markdown/data/etc.) as a short description line.\n if (node.type === 'mediaNode' && node.data.mediaKind) {\n const kind = node.data.mediaKind;\n if (kind === 'data' && node.data.dataConfig) {\n const cfg = node.data.dataConfig;\n const cols = cfg.columns.map((c) => c.label ?? c.key).join(', ');\n lines.push(`${indent} - 数据表:${cfg.rows.length} 行 · 列(${cols})`);\n } else if (node.data.mediaContent && kind !== 'image' && kind !== 'model3d') {\n const preview = node.data.mediaContent.trim().split('\\n')[0].slice(0, 80);\n if (preview) lines.push(`${indent} - ${kind}: ${preview}`);\n }\n }\n\n // Recurse into visual group children.\n const kids = childrenOf.get(node.id) ?? [];\n for (const kid of kids) emitNode(kid, depth + 1);\n\n // Recurse into a drill-down sub-canvas, if any.\n const childCanvasId = node.data.childCanvasId;\n if (childCanvasId && doc.canvases[childCanvasId] && !seenCanvases.has(childCanvasId)) {\n seenCanvases.add(childCanvasId);\n renderCanvas(childCanvasId, headingLevel + 1, seenCanvases);\n }\n };\n\n const roots = childrenOf.get(undefined) ?? [];\n for (const root of roots) emitNode(root, 0);\n\n // Connections summary.\n if (canvas.edges.length > 0) {\n lines.push('');\n lines.push(`${'#'.repeat(Math.min(6, headingLevel + 1))} 连接关系`);\n for (const edge of canvas.edges) {\n const from = byId.get(edge.source);\n const to = byId.get(edge.target);\n if (!from || !to) continue;\n const edgeLabel = edge.data.labelText ? ` (${edge.data.labelText})` : '';\n lines.push(`- ${nodeLabel(from)} → ${nodeLabel(to)}${edgeLabel}`);\n }\n }\n };\n\n const seen = new Set<string>([ROOT_CANVAS_ID]);\n lines.push('# 流程图');\n lines.push('');\n renderCanvas(ROOT_CANVAS_ID, 1, seen);\n\n // Any orphan canvases not reached via drill-down nodes.\n for (const canvasId of Object.keys(doc.canvases)) {\n if (seen.has(canvasId)) continue;\n seen.add(canvasId);\n lines.push('');\n lines.push(`## 画布 ${canvasId}`);\n lines.push('');\n renderCanvas(canvasId, 2, seen);\n }\n\n return lines.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim() + '\\n';\n}\n\n/**\n * Simple Mermaid flowchart parser.\n * Handles basic `graph TD` / `flowchart LR` syntax with node definitions and edges.\n */\nfunction parseMermaidFlowchart(code: string): FlowDocument {\n const lines = code.split('\\n').map(l => l.trim()).filter(l => l && !l.startsWith('%%'));\n\n const nodeMap = new Map<string, { id: string; label: string }>();\n const edges: Array<{ from: string; to: string; label?: string }> = [];\n\n let isHorizontal = false;\n\n for (const line of lines) {\n // Detect direction\n if (/^(graph|flowchart)\\s+(LR|RL)/i.test(line)) {\n isHorizontal = true;\n continue;\n }\n if (/^(graph|flowchart)\\s+/i.test(line)) continue;\n\n // Edge patterns: A --> B, A -->|label| B, A --- B\n const edgeMatch = line.match(/^(\\w+)(?:\\[([^\\]]*)\\])?\\s*(-->|---|-\\.->|==>)(?:\\|([^|]*)\\|)?\\s*(\\w+)(?:\\[([^\\]]*)\\])?/);\n if (edgeMatch) {\n const [, fromId, fromLabel, , edgeLabel, toId, toLabel] = edgeMatch;\n if (fromLabel !== undefined) nodeMap.set(fromId, { id: fromId, label: fromLabel });\n if (toLabel !== undefined) nodeMap.set(toId, { id: toId, label: toLabel });\n if (!nodeMap.has(fromId)) nodeMap.set(fromId, { id: fromId, label: fromId });\n if (!nodeMap.has(toId)) nodeMap.set(toId, { id: toId, label: toId });\n edges.push({ from: fromId, to: toId, label: edgeLabel?.trim() });\n continue;\n }\n\n // Standalone node: A[Label]\n const nodeMatch = line.match(/^(\\w+)\\[([^\\]]*)\\]/);\n if (nodeMatch) {\n const [, id, label] = nodeMatch;\n nodeMap.set(id, { id, label });\n }\n }\n\n // Layout: simple grid placement\n const nodeIds = [...nodeMap.keys()];\n const cols = isHorizontal ? nodeIds.length : Math.ceil(Math.sqrt(nodeIds.length));\n const spacing = { x: 200, y: 120 };\n\n const nodes: FlowNode[] = nodeIds.map((id, i) => {\n const col = i % cols;\n const row = Math.floor(i / cols);\n const data = nodeMap.get(id)!;\n return {\n id: data.id,\n type: 'shapeRoundRect' as const,\n position: { x: 100 + col * spacing.x, y: 100 + row * spacing.y },\n width: 140,\n height: 60,\n data: {\n label: data.label,\n fill: 'theme:base-100',\n stroke: 'theme:base-content',\n strokeWidth: 2,\n color: 'theme:base-content',\n fontSize: 14,\n },\n };\n });\n\n const flowEdges: FlowEdge[] = edges.map((e) => ({\n id: generateId('edge'),\n source: e.from,\n target: e.to,\n type: 'smoothstep' as const,\n data: {\n strokeColor: 'theme:base-content',\n strokeWidth: 2,\n lineStyle: 'solid' as const,\n markerEnd: 'arrowclosed' as const,\n labelText: e.label,\n },\n }));\n\n return {\n canvases: {\n [ROOT_CANVAS_ID]: { nodes, edges: flowEdges, lines: [] },\n },\n stack: [ROOT_CANVAS_ID],\n };\n}\n\n/**\n * Export the PixiJS canvas as PNG data URL.\n */\nexport async function exportCanvasToPNG(canvas: HTMLCanvasElement): Promise<string> {\n return canvas.toDataURL('image/png');\n}\n\n/**\n * Download a data URL as a file.\n */\nexport function downloadDataURL(dataUrl: string, filename: string): void {\n const a = document.createElement('a');\n a.href = dataUrl;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useFlowImportExport.js","names":["PERSIST_VERSION","isRecord","value","isDocument","id","canvas","safeFileBaseName","title","fallback","useFlowImportExport","useFlowContext","replaceDocument","useCallback","setDoc","next","setSelection","serialized","doc","parsed","json","isArchifySpec","archifyToFlowDocument","err","spec","input","parseMermaidFlowchart","mermaidCode","documentToMarkdown","createEmptyDocument","lines","nodeLabel","raw","n","renderCanvas","canvasId","byId","childrenOf","key","list","emitNode","indent","depth","bullet","node","kind","cfg","cols","c","preview","kids","kid","childCanvasId","seenCanvases","headingLevel","roots","root","edge","from","to","edgeLabel","seen","ROOT_CANVAS_ID","code","l","nodeMap","edges","isHorizontal","line","edgeMatch","fromId","fromLabel","toId","toLabel","nodeMatch","label","nodeIds","spacing","nodes","col","i","row","data","flowEdges","generateId","exportCanvasToPNG","downloadDataURL","a","dataUrl","filename"],"sources":["../../../../../src/components/pro/Flow/hooks/useFlowImportExport.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowDocument, FlowCanvasData, FlowNode, FlowEdge } from '../types';\nimport { createEmptyDocument, ROOT_CANVAS_ID } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\nimport { isArchifySpec, archifyToFlowDocument } from '../archify';\n\nconst PERSIST_VERSION = 1;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDocument(value: unknown): value is FlowDocument {\n if (!isRecord(value) || !isRecord(value.canvases) || !Array.isArray(value.stack)) return false;\n return value.stack.every(id => typeof id === 'string')\n && (value.title === undefined || typeof value.title === 'string')\n && Object.values(value.canvases).every(canvas => isRecord(canvas)\n && Array.isArray(canvas.nodes) && Array.isArray(canvas.edges) && Array.isArray(canvas.lines));\n}\n\ninterface SerializedDocument {\n version: number;\n canvases: Record<string, FlowCanvasData>;\n stack: string[];\n title?: string;\n}\n\n/**\n * Sanitize a project title into a safe file base name, falling back to a\n * default when empty. Shared by all export paths so filenames stay consistent.\n */\nexport function safeFileBaseName(title: string | undefined, fallback = 'flowchart'): string {\n const name = (title ?? '').trim().replace(/[\\\\/:*?\"<>|]+/g, '-').replace(/\\s+/g, ' ').trim();\n return name || fallback;\n}\n\n/**\n * Serialize/deserialize FlowDocument to/from JSON.\n * Also includes Mermaid flowchart import.\n */\nexport function useFlowImportExport() {\n const { doc, setDoc, setSelection } = useFlowContext();\n\n const replaceDocument = useCallback((next: FlowDocument) => {\n setDoc(next);\n setSelection({ nodeIds: new Set(), edgeIds: new Set(), lineIds: new Set() });\n }, [setDoc, setSelection]);\n\n const exportJSON = useCallback((): string => {\n const serialized: SerializedDocument = {\n version: PERSIST_VERSION,\n canvases: doc.canvases,\n stack: doc.stack,\n title: doc.title,\n };\n return JSON.stringify(serialized, null, 2);\n }, [doc]);\n\n const importJSON = useCallback((json: string) => {\n try {\n const parsed = JSON.parse(json) as unknown;\n // Auto-detect an Archify spec and convert it to a Flow document so the\n // same \"import JSON\" entry point accepts both native and Archify files.\n if (isArchifySpec(parsed)) {\n replaceDocument(archifyToFlowDocument(parsed));\n return;\n }\n if (!isDocument(parsed)) throw new Error('Invalid document format');\n const doc = parsed;\n replaceDocument({\n canvases: doc.canvases,\n stack: doc.stack,\n title: doc.title,\n });\n } catch (err) {\n console.error('Failed to import JSON:', err);\n }\n }, [replaceDocument]);\n\n /**\n * Import an Archify diagram spec (architecture / workflow / sequence /\n * dataflow / lifecycle), converting it to a native Flow document. Accepts a\n * JSON string or an already-parsed object. Returns true on success.\n */\n const importArchify = useCallback((input: string | object): boolean => {\n try {\n const spec = typeof input === 'string' ? JSON.parse(input) : input;\n if (!isArchifySpec(spec)) return false;\n replaceDocument(archifyToFlowDocument(spec));\n return true;\n } catch (err) {\n console.error('Failed to import Archify spec:', err);\n return false;\n }\n }, [replaceDocument]);\n\n const importMermaid = useCallback((mermaidCode: string) => {\n const doc = parseMermaidFlowchart(mermaidCode);\n replaceDocument(doc);\n }, [replaceDocument]);\n\n const exportMarkdown = useCallback((): string => {\n return documentToMarkdown(doc);\n }, [doc]);\n\n const clearDocument = useCallback(() => {\n replaceDocument(createEmptyDocument());\n }, [replaceDocument]);\n\n return { exportJSON, importJSON, importArchify, importMermaid, exportMarkdown, clearDocument };\n}\n\n/**\n * Render a FlowDocument as a Markdown outline describing the node hierarchy\n * and connections. Groups become sections with their children nested beneath;\n * edges are listed as a connection summary per canvas. Sub-canvases (drill-down\n * nodes) are rendered as nested sections.\n */\nfunction documentToMarkdown(doc: FlowDocument): string {\n const lines: string[] = [];\n\n const nodeLabel = (n: FlowNode): string => {\n const raw = (n.data.label ?? '').trim();\n if (raw) return raw.replace(/\\s+/g, ' ');\n if (n.type === 'mediaNode') return `(${n.data.mediaKind ?? 'media'})`;\n return `(${n.type})`;\n };\n\n const renderCanvas = (canvasId: string, headingLevel: number, seenCanvases: Set<string>) => {\n const canvas = doc.canvases[canvasId];\n if (!canvas) return;\n\n // Index nodes by id and by parent for hierarchy traversal.\n const byId = new Map(canvas.nodes.map((n) => [n.id, n]));\n const childrenOf = new Map<string | undefined, FlowNode[]>();\n for (const n of canvas.nodes) {\n const key = n.parentId && byId.has(n.parentId) ? n.parentId : undefined;\n const list = childrenOf.get(key) ?? [];\n list.push(n);\n childrenOf.set(key, list);\n }\n\n const emitNode = (node: FlowNode, depth: number) => {\n const indent = ' '.repeat(depth);\n const isGroup = node.data.isGroup || node.type === 'flowGroup' || node.type === 'geometryGroup';\n const bullet = isGroup ? `**${nodeLabel(node)}**` : nodeLabel(node);\n lines.push(`${indent}- ${bullet}`);\n\n // Media content (markdown/data/etc.) as a short description line.\n if (node.type === 'mediaNode' && node.data.mediaKind) {\n const kind = node.data.mediaKind;\n if (kind === 'data' && node.data.dataConfig) {\n const cfg = node.data.dataConfig;\n const cols = cfg.columns.map((c) => c.label ?? c.key).join(', ');\n lines.push(`${indent} - 数据表:${cfg.rows.length} 行 · 列(${cols})`);\n } else if (node.data.mediaContent && kind !== 'image' && kind !== 'model3d') {\n const preview = node.data.mediaContent.trim().split('\\n')[0].slice(0, 80);\n if (preview) lines.push(`${indent} - ${kind}: ${preview}`);\n }\n }\n\n // Recurse into visual group children.\n const kids = childrenOf.get(node.id) ?? [];\n for (const kid of kids) emitNode(kid, depth + 1);\n\n // Recurse into a drill-down sub-canvas, if any.\n const childCanvasId = node.data.childCanvasId;\n if (childCanvasId && doc.canvases[childCanvasId] && !seenCanvases.has(childCanvasId)) {\n seenCanvases.add(childCanvasId);\n renderCanvas(childCanvasId, headingLevel + 1, seenCanvases);\n }\n };\n\n const roots = childrenOf.get(undefined) ?? [];\n for (const root of roots) emitNode(root, 0);\n\n // Connections summary.\n if (canvas.edges.length > 0) {\n lines.push('');\n lines.push(`${'#'.repeat(Math.min(6, headingLevel + 1))} 连接关系`);\n for (const edge of canvas.edges) {\n const from = byId.get(edge.source);\n const to = byId.get(edge.target);\n if (!from || !to) continue;\n const edgeLabel = edge.data.labelText ? ` (${edge.data.labelText})` : '';\n lines.push(`- ${nodeLabel(from)} → ${nodeLabel(to)}${edgeLabel}`);\n }\n }\n };\n\n const seen = new Set<string>([ROOT_CANVAS_ID]);\n lines.push('# 流程图');\n lines.push('');\n renderCanvas(ROOT_CANVAS_ID, 1, seen);\n\n // Any orphan canvases not reached via drill-down nodes.\n for (const canvasId of Object.keys(doc.canvases)) {\n if (seen.has(canvasId)) continue;\n seen.add(canvasId);\n lines.push('');\n lines.push(`## 画布 ${canvasId}`);\n lines.push('');\n renderCanvas(canvasId, 2, seen);\n }\n\n return lines.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim() + '\\n';\n}\n\n/**\n * Simple Mermaid flowchart parser.\n * Handles basic `graph TD` / `flowchart LR` syntax with node definitions and edges.\n */\nfunction parseMermaidFlowchart(code: string): FlowDocument {\n const lines = code.split('\\n').map(l => l.trim()).filter(l => l && !l.startsWith('%%'));\n\n const nodeMap = new Map<string, { id: string; label: string }>();\n const edges: Array<{ from: string; to: string; label?: string }> = [];\n\n let isHorizontal = false;\n\n for (const line of lines) {\n // Detect direction\n if (/^(graph|flowchart)\\s+(LR|RL)/i.test(line)) {\n isHorizontal = true;\n continue;\n }\n if (/^(graph|flowchart)\\s+/i.test(line)) continue;\n\n // Edge patterns: A --> B, A -->|label| B, A --- B\n const edgeMatch = line.match(/^(\\w+)(?:\\[([^\\]]*)\\])?\\s*(-->|---|-\\.->|==>)(?:\\|([^|]*)\\|)?\\s*(\\w+)(?:\\[([^\\]]*)\\])?/);\n if (edgeMatch) {\n const [, fromId, fromLabel, , edgeLabel, toId, toLabel] = edgeMatch;\n if (fromLabel !== undefined) nodeMap.set(fromId, { id: fromId, label: fromLabel });\n if (toLabel !== undefined) nodeMap.set(toId, { id: toId, label: toLabel });\n if (!nodeMap.has(fromId)) nodeMap.set(fromId, { id: fromId, label: fromId });\n if (!nodeMap.has(toId)) nodeMap.set(toId, { id: toId, label: toId });\n edges.push({ from: fromId, to: toId, label: edgeLabel?.trim() });\n continue;\n }\n\n // Standalone node: A[Label]\n const nodeMatch = line.match(/^(\\w+)\\[([^\\]]*)\\]/);\n if (nodeMatch) {\n const [, id, label] = nodeMatch;\n nodeMap.set(id, { id, label });\n }\n }\n\n // Layout: simple grid placement\n const nodeIds = [...nodeMap.keys()];\n const cols = isHorizontal ? nodeIds.length : Math.ceil(Math.sqrt(nodeIds.length));\n const spacing = { x: 200, y: 120 };\n\n const nodes: FlowNode[] = nodeIds.map((id, i) => {\n const col = i % cols;\n const row = Math.floor(i / cols);\n const data = nodeMap.get(id)!;\n return {\n id: data.id,\n type: 'shapeRoundRect' as const,\n position: { x: 100 + col * spacing.x, y: 100 + row * spacing.y },\n width: 140,\n height: 60,\n data: {\n label: data.label,\n fill: 'theme:base-100',\n stroke: 'theme:base-content',\n strokeWidth: 2,\n color: 'theme:base-content',\n fontSize: 14,\n },\n };\n });\n\n const flowEdges: FlowEdge[] = edges.map((e) => ({\n id: generateId('edge'),\n source: e.from,\n target: e.to,\n type: 'smoothstep' as const,\n data: {\n strokeColor: 'theme:base-content',\n strokeWidth: 2,\n lineStyle: 'solid' as const,\n markerEnd: 'arrowclosed' as const,\n labelText: e.label,\n },\n }));\n\n return {\n canvases: {\n [ROOT_CANVAS_ID]: { nodes, edges: flowEdges, lines: [] },\n },\n stack: [ROOT_CANVAS_ID],\n };\n}\n\n/**\n * Export the PixiJS canvas as PNG data URL.\n */\nexport async function exportCanvasToPNG(canvas: HTMLCanvasElement): Promise<string> {\n return canvas.toDataURL('image/png');\n}\n\n/**\n * Download a data URL as a file.\n */\nexport function downloadDataURL(dataUrl: string, filename: string): void {\n const a = document.createElement('a');\n a.href = dataUrl;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useKeyboardShortcuts.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useKeyboardShortcuts.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\n\ninterface KeyboardShortcutActions {\n undo: () => void;\n redo: () => void;\n copy: () => void;\n cut: () => void;\n paste: () => void;\n duplicate: () => void;\n deleteSelected: () => void;\n selectAll: () => void;\n group?: () => void;\n geoGroup?: () => void;\n ungroup?: () => void;\n bringToFront?: () => void;\n bringForward?: () => void;\n sendBackward?: () => void;\n sendToBack?: () => void;\n}\n\n/**\n * Registers global keyboard shortcuts for the flow editor.\n */\nexport function useKeyboardShortcuts(actions: KeyboardShortcutActions) {\n const { setSelection } = useFlowContext();\n const { editingNodeId, setToolMode, setPendingPlacement } = useFlowUI();\n\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n // Don't intercept when editing text in inputs\n if (e.defaultPrevented || e.isComposing) return;\n const target = e.target as HTMLElement | null;\n if (target?.tagName === 'INPUT' || target?.tagName === 'TEXTAREA' || target?.tagName === 'SELECT' || target?.isContentEditable) return;\n if (editingNodeId) return;\n\n const meta = e.metaKey || e.ctrlKey;\n\n if (meta && e.key === 'z' && !e.shiftKey) {\n e.preventDefault();\n actions.undo();\n } else if (meta && (e.key === 'Z' || (e.key === 'z' && e.shiftKey))) {\n e.preventDefault();\n actions.redo();\n } else if (meta && e.key === 'c') {\n e.preventDefault();\n actions.copy();\n } else if (meta && e.key === 'x') {\n e.preventDefault();\n actions.cut();\n } else if (meta && e.key === 'v') {\n // Let the native paste event inspect image clipboard data. Structured\n // Flow clipboard fallback is handled there when no image is present.\n return;\n } else if (meta && e.key === 'a') {\n e.preventDefault();\n actions.selectAll();\n } else if (meta && (e.key === 'd' || e.key === 'D')) {\n e.preventDefault();\n actions.duplicate();\n } else if (meta && e.altKey && (e.key === 'g' || e.key === 'G')) {\n e.preventDefault();\n actions.geoGroup?.();\n } else if (meta && e.key === 'g' && !e.shiftKey && !e.altKey) {\n e.preventDefault();\n actions.group?.();\n } else if (meta && (e.key === 'G' || (e.key === 'g' && e.shiftKey)) && !e.altKey) {\n e.preventDefault();\n actions.ungroup?.();\n } else if (meta && e.shiftKey && e.key === 'ArrowUp') {\n e.preventDefault();\n actions.bringToFront?.();\n } else if (meta && !e.shiftKey && e.key === 'ArrowUp') {\n e.preventDefault();\n actions.bringForward?.();\n } else if (meta && !e.shiftKey && e.key === 'ArrowDown') {\n e.preventDefault();\n actions.sendBackward?.();\n } else if (meta && e.shiftKey && e.key === 'ArrowDown') {\n e.preventDefault();\n actions.sendToBack?.();\n } else if (e.key === 'Delete' || e.key === 'Backspace') {\n e.preventDefault();\n actions.deleteSelected();\n } else if (e.key === 'Escape') {\n setSelection({ nodeIds: new Set(), edgeIds: new Set(), lineIds: new Set() });\n setToolMode('select');\n setPendingPlacement(null);\n }\n };\n\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [actions, editingNodeId, setSelection, setToolMode, setPendingPlacement]);\n}\n"],"mappings":";;;;AAyBA,
|
|
1
|
+
{"version":3,"file":"useKeyboardShortcuts.js","names":["useKeyboardShortcuts","useFlowContext","useFlowUI","handler","target","editingNodeId","meta","actions","setSelection","setToolMode","setPendingPlacement"],"sources":["../../../../../src/components/pro/Flow/hooks/useKeyboardShortcuts.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\n\ninterface KeyboardShortcutActions {\n undo: () => void;\n redo: () => void;\n copy: () => void;\n cut: () => void;\n paste: () => void;\n duplicate: () => void;\n deleteSelected: () => void;\n selectAll: () => void;\n group?: () => void;\n geoGroup?: () => void;\n ungroup?: () => void;\n bringToFront?: () => void;\n bringForward?: () => void;\n sendBackward?: () => void;\n sendToBack?: () => void;\n}\n\n/**\n * Registers global keyboard shortcuts for the flow editor.\n */\nexport function useKeyboardShortcuts(actions: KeyboardShortcutActions) {\n const { setSelection } = useFlowContext();\n const { editingNodeId, setToolMode, setPendingPlacement } = useFlowUI();\n\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n // Don't intercept when editing text in inputs\n if (e.defaultPrevented || e.isComposing) return;\n const target = e.target as HTMLElement | null;\n if (target?.tagName === 'INPUT' || target?.tagName === 'TEXTAREA' || target?.tagName === 'SELECT' || target?.isContentEditable) return;\n if (editingNodeId) return;\n\n const meta = e.metaKey || e.ctrlKey;\n\n if (meta && e.key === 'z' && !e.shiftKey) {\n e.preventDefault();\n actions.undo();\n } else if (meta && (e.key === 'Z' || (e.key === 'z' && e.shiftKey))) {\n e.preventDefault();\n actions.redo();\n } else if (meta && e.key === 'c') {\n e.preventDefault();\n actions.copy();\n } else if (meta && e.key === 'x') {\n e.preventDefault();\n actions.cut();\n } else if (meta && e.key === 'v') {\n // Let the native paste event inspect image clipboard data. Structured\n // Flow clipboard fallback is handled there when no image is present.\n return;\n } else if (meta && e.key === 'a') {\n e.preventDefault();\n actions.selectAll();\n } else if (meta && (e.key === 'd' || e.key === 'D')) {\n e.preventDefault();\n actions.duplicate();\n } else if (meta && e.altKey && (e.key === 'g' || e.key === 'G')) {\n e.preventDefault();\n actions.geoGroup?.();\n } else if (meta && e.key === 'g' && !e.shiftKey && !e.altKey) {\n e.preventDefault();\n actions.group?.();\n } else if (meta && (e.key === 'G' || (e.key === 'g' && e.shiftKey)) && !e.altKey) {\n e.preventDefault();\n actions.ungroup?.();\n } else if (meta && e.shiftKey && e.key === 'ArrowUp') {\n e.preventDefault();\n actions.bringToFront?.();\n } else if (meta && !e.shiftKey && e.key === 'ArrowUp') {\n e.preventDefault();\n actions.bringForward?.();\n } else if (meta && !e.shiftKey && e.key === 'ArrowDown') {\n e.preventDefault();\n actions.sendBackward?.();\n } else if (meta && e.shiftKey && e.key === 'ArrowDown') {\n e.preventDefault();\n actions.sendToBack?.();\n } else if (e.key === 'Delete' || e.key === 'Backspace') {\n e.preventDefault();\n actions.deleteSelected();\n } else if (e.key === 'Escape') {\n setSelection({ nodeIds: new Set(), edgeIds: new Set(), lineIds: new Set() });\n setToolMode('select');\n setPendingPlacement(null);\n }\n };\n\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [actions, editingNodeId, setSelection, setToolMode, setPendingPlacement]);\n}\n"],"mappings":";;;;AAyBA,SAAgBA,EAAqB,GAAkC;CACrE,IAAM,EAAE,oBAAiBC,EAAe,GAClC,EAAE,kBAAe,gBAAa,2BAAwBC,EAAU;CAEtE,QAAgB;EACd,IAAMC,KAAW,MAAqB;GAEpC,IAAI,EAAE,oBAAoB,EAAE,aAAa;GACzC,IAAMC,IAAS,EAAE;GAEjB,IADIA,GAAQ,YAAY,WAAWA,GAAQ,YAAY,cAAcA,GAAQ,YAAY,YAAYA,GAAQ,qBACzGC,GAAe;GAEnB,IAAMC,IAAO,EAAE,WAAW,EAAE;GAE5B,IAAIA,KAAQ,EAAE,QAAQ,OAAO,CAAC,EAAE,UAE9B,AADA,EAAE,eAAe,GACjBC,EAAQ,KAAK;QACR,IAAID,MAAS,EAAE,QAAQ,OAAQ,EAAE,QAAQ,OAAO,EAAE,WAEvD,AADA,EAAE,eAAe,GACjBC,EAAQ,KAAK;QACR,IAAID,KAAQ,EAAE,QAAQ,KAE3B,AADA,EAAE,eAAe,GACjBC,EAAQ,KAAK;QACR,IAAID,KAAQ,EAAE,QAAQ,KAE3B,AADA,EAAE,eAAe,GACjBC,EAAQ,IAAI;QACP,IAAID,KAAQ,EAAE,QAAQ,KAG3B;QACK,AAAIA,KAAQ,EAAE,QAAQ,OAC3B,EAAE,eAAe,GACjBC,EAAQ,UAAU,KACTD,MAAS,EAAE,QAAQ,OAAO,EAAE,QAAQ,QAC7C,EAAE,eAAe,GACjBC,EAAQ,UAAU,KACTD,KAAQ,EAAE,WAAW,EAAE,QAAQ,OAAO,EAAE,QAAQ,QACzD,EAAE,eAAe,GACjBC,EAAQ,WAAW,KACVD,KAAQ,EAAE,QAAQ,OAAO,CAAC,EAAE,YAAY,CAAC,EAAE,UACpD,EAAE,eAAe,GACjBC,EAAQ,QAAQ,KACPD,MAAS,EAAE,QAAQ,OAAQ,EAAE,QAAQ,OAAO,EAAE,aAAc,CAAC,EAAE,UACxE,EAAE,eAAe,GACjBC,EAAQ,UAAU,KACTD,KAAQ,EAAE,YAAY,EAAE,QAAQ,aACzC,EAAE,eAAe,GACjBC,EAAQ,eAAe,KACdD,KAAQ,CAAC,EAAE,YAAY,EAAE,QAAQ,aAC1C,EAAE,eAAe,GACjBC,EAAQ,eAAe,KACdD,KAAQ,CAAC,EAAE,YAAY,EAAE,QAAQ,eAC1C,EAAE,eAAe,GACjBC,EAAQ,eAAe,KACdD,KAAQ,EAAE,YAAY,EAAE,QAAQ,eACzC,EAAE,eAAe,GACjBC,EAAQ,aAAa,KACZ,EAAE,QAAQ,YAAY,EAAE,QAAQ,eACzC,EAAE,eAAe,GACjBA,EAAQ,eAAe,KACd,EAAE,QAAQ,aACnBC,EAAa;IAAE,yBAAS,IAAI,IAAI;IAAG,yBAAS,IAAI,IAAI;IAAG,yBAAS,IAAI,IAAI;GAAE,CAAC,GAC3EC,EAAY,QAAQ,GACpBC,EAAoB,IAAI;EAE5B;EAGA,OADA,OAAO,iBAAiB,WAAWP,CAAO,SAC7B,OAAO,oBAAoB,WAAWA,CAAO;CAC5D,GAAG;EAACI;EAASF;EAAeG;EAAcC;EAAaC;CAAmB,CAAC;AAC7E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLayoutActions.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useLayoutActions.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowNode } from '../types';\n\ntype Align = 'left' | 'center-h' | 'right' | 'top' | 'center-v' | 'bottom';\ntype Distribute = 'horizontal' | 'vertical';\ntype LayerMode = 'front' | 'back' | 'up' | 'down';\n\nexport function useLayoutActions() {\n const { setDoc, currentCanvasId, selection, currentCanvas } = useFlowContext();\n\n const align = useCallback(\n (direction: Align) => {\n const ids = selection.nodeIds;\n if (ids.size < 2) return;\n const nodes = currentCanvas.nodes.filter(n => ids.has(n.id));\n if (nodes.length < 2) return;\n\n let target: number;\n switch (direction) {\n case 'left':\n target = Math.min(...nodes.map(n => n.position.x));\n break;\n case 'right':\n target = Math.max(...nodes.map(n => n.position.x + n.width));\n break;\n case 'center-h':\n target = nodes.reduce((s, n) => s + n.position.x + n.width / 2, 0) / nodes.length;\n break;\n case 'top':\n target = Math.min(...nodes.map(n => n.position.y));\n break;\n case 'bottom':\n target = Math.max(...nodes.map(n => n.position.y + n.height));\n break;\n case 'center-v':\n target = nodes.reduce((s, n) => s + n.position.y + n.height / 2, 0) / nodes.length;\n break;\n }\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n if (!ids.has(n.id)) return n;\n let newX = n.position.x;\n let newY = n.position.y;\n switch (direction) {\n case 'left': newX = target; break;\n case 'right': newX = target - n.width; break;\n case 'center-h': newX = target - n.width / 2; break;\n case 'top': newY = target; break;\n case 'bottom': newY = target - n.height; break;\n case 'center-v': newY = target - n.height / 2; break;\n }\n return { ...n, position: { x: newX, y: newY } };\n }),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId, selection, currentCanvas],\n );\n\n const distribute = useCallback(\n (direction: Distribute) => {\n const ids = selection.nodeIds;\n if (ids.size < 3) return;\n const nodes = currentCanvas.nodes.filter(n => ids.has(n.id));\n if (nodes.length < 3) return;\n\n const sorted = [...nodes].sort((a, b) =>\n direction === 'horizontal'\n ? a.position.x - b.position.x\n : a.position.y - b.position.y,\n );\n\n const first = sorted[0];\n const last = sorted[sorted.length - 1];\n\n if (direction === 'horizontal') {\n const totalWidth = sorted.reduce((s, n) => s + n.width, 0);\n const totalSpace = (last.position.x + last.width) - first.position.x - totalWidth;\n const gap = totalSpace / (sorted.length - 1);\n let x = first.position.x;\n\n const posMap = new Map<string, number>();\n for (const n of sorted) {\n posMap.set(n.id, x);\n x += n.width + gap;\n }\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n const newX = posMap.get(n.id);\n return newX !== undefined ? { ...n, position: { ...n.position, x: newX } } : n;\n }),\n },\n },\n };\n });\n } else {\n const totalHeight = sorted.reduce((s, n) => s + n.height, 0);\n const totalSpace = (last.position.y + last.height) - first.position.y - totalHeight;\n const gap = totalSpace / (sorted.length - 1);\n let y = first.position.y;\n\n const posMap = new Map<string, number>();\n for (const n of sorted) {\n posMap.set(n.id, y);\n y += n.height + gap;\n }\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n const newY = posMap.get(n.id);\n return newY !== undefined ? { ...n, position: { ...n.position, y: newY } } : n;\n }),\n },\n },\n };\n });\n }\n },\n [setDoc, currentCanvasId, selection, currentCanvas],\n );\n\n const changeZIndex = useCallback(\n (mode: LayerMode) => {\n const nodeIds = selection.nodeIds;\n const lineIds = selection.lineIds;\n if (nodeIds.size === 0 && lineIds.size === 0) return;\n const applyZ = (z: number, maxZ: number, minZ: number) => {\n if (mode === 'front') return maxZ + 1;\n if (mode === 'back') return minZ - 1;\n if (mode === 'up') return z + 1;\n return z - 1;\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n const nodeAllZ = canvas.nodes.map(n => n.zIndex ?? 0);\n const nodeMaxZ = nodeAllZ.length > 0 ? Math.max(...nodeAllZ) : 0;\n const nodeMinZ = nodeAllZ.length > 0 ? Math.min(...nodeAllZ) : 0;\n const lineAllZ = canvas.lines.map(l => l.zIndex ?? 0);\n const lineMaxZ = lineAllZ.length > 0 ? Math.max(...lineAllZ) : 0;\n const lineMinZ = lineAllZ.length > 0 ? Math.min(...lineAllZ) : 0;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => nodeIds.has(n.id) ? { ...n, zIndex: applyZ(n.zIndex ?? 0, nodeMaxZ, nodeMinZ) } : n),\n lines: canvas.lines.map(l => lineIds.has(l.id) ? { ...l, zIndex: applyZ(l.zIndex ?? 0, lineMaxZ, lineMinZ) } : l),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId, selection],\n );\n\n return { align, distribute, changeZIndex };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useLayoutActions.js","names":["useLayoutActions","useFlowContext","useCallback","ids","selection","nodes","currentCanvas","n","target","direction","s","canvas","prev","currentCanvasId","newX","newY","setDoc","sorted","a","b","first","last","totalWidth","gap","x","posMap","totalHeight","y","nodeIds","lineIds","applyZ","mode","maxZ","minZ","z","nodeAllZ","nodeMaxZ","nodeMinZ","lineAllZ","l","lineMaxZ","lineMinZ"],"sources":["../../../../../src/components/pro/Flow/hooks/useLayoutActions.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowNode } from '../types';\n\ntype Align = 'left' | 'center-h' | 'right' | 'top' | 'center-v' | 'bottom';\ntype Distribute = 'horizontal' | 'vertical';\ntype LayerMode = 'front' | 'back' | 'up' | 'down';\n\nexport function useLayoutActions() {\n const { setDoc, currentCanvasId, selection, currentCanvas } = useFlowContext();\n\n const align = useCallback(\n (direction: Align) => {\n const ids = selection.nodeIds;\n if (ids.size < 2) return;\n const nodes = currentCanvas.nodes.filter(n => ids.has(n.id));\n if (nodes.length < 2) return;\n\n let target: number;\n switch (direction) {\n case 'left':\n target = Math.min(...nodes.map(n => n.position.x));\n break;\n case 'right':\n target = Math.max(...nodes.map(n => n.position.x + n.width));\n break;\n case 'center-h':\n target = nodes.reduce((s, n) => s + n.position.x + n.width / 2, 0) / nodes.length;\n break;\n case 'top':\n target = Math.min(...nodes.map(n => n.position.y));\n break;\n case 'bottom':\n target = Math.max(...nodes.map(n => n.position.y + n.height));\n break;\n case 'center-v':\n target = nodes.reduce((s, n) => s + n.position.y + n.height / 2, 0) / nodes.length;\n break;\n }\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n if (!ids.has(n.id)) return n;\n let newX = n.position.x;\n let newY = n.position.y;\n switch (direction) {\n case 'left': newX = target; break;\n case 'right': newX = target - n.width; break;\n case 'center-h': newX = target - n.width / 2; break;\n case 'top': newY = target; break;\n case 'bottom': newY = target - n.height; break;\n case 'center-v': newY = target - n.height / 2; break;\n }\n return { ...n, position: { x: newX, y: newY } };\n }),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId, selection, currentCanvas],\n );\n\n const distribute = useCallback(\n (direction: Distribute) => {\n const ids = selection.nodeIds;\n if (ids.size < 3) return;\n const nodes = currentCanvas.nodes.filter(n => ids.has(n.id));\n if (nodes.length < 3) return;\n\n const sorted = [...nodes].sort((a, b) =>\n direction === 'horizontal'\n ? a.position.x - b.position.x\n : a.position.y - b.position.y,\n );\n\n const first = sorted[0];\n const last = sorted[sorted.length - 1];\n\n if (direction === 'horizontal') {\n const totalWidth = sorted.reduce((s, n) => s + n.width, 0);\n const totalSpace = (last.position.x + last.width) - first.position.x - totalWidth;\n const gap = totalSpace / (sorted.length - 1);\n let x = first.position.x;\n\n const posMap = new Map<string, number>();\n for (const n of sorted) {\n posMap.set(n.id, x);\n x += n.width + gap;\n }\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n const newX = posMap.get(n.id);\n return newX !== undefined ? { ...n, position: { ...n.position, x: newX } } : n;\n }),\n },\n },\n };\n });\n } else {\n const totalHeight = sorted.reduce((s, n) => s + n.height, 0);\n const totalSpace = (last.position.y + last.height) - first.position.y - totalHeight;\n const gap = totalSpace / (sorted.length - 1);\n let y = first.position.y;\n\n const posMap = new Map<string, number>();\n for (const n of sorted) {\n posMap.set(n.id, y);\n y += n.height + gap;\n }\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n const newY = posMap.get(n.id);\n return newY !== undefined ? { ...n, position: { ...n.position, y: newY } } : n;\n }),\n },\n },\n };\n });\n }\n },\n [setDoc, currentCanvasId, selection, currentCanvas],\n );\n\n const changeZIndex = useCallback(\n (mode: LayerMode) => {\n const nodeIds = selection.nodeIds;\n const lineIds = selection.lineIds;\n if (nodeIds.size === 0 && lineIds.size === 0) return;\n const applyZ = (z: number, maxZ: number, minZ: number) => {\n if (mode === 'front') return maxZ + 1;\n if (mode === 'back') return minZ - 1;\n if (mode === 'up') return z + 1;\n return z - 1;\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n const nodeAllZ = canvas.nodes.map(n => n.zIndex ?? 0);\n const nodeMaxZ = nodeAllZ.length > 0 ? Math.max(...nodeAllZ) : 0;\n const nodeMinZ = nodeAllZ.length > 0 ? Math.min(...nodeAllZ) : 0;\n const lineAllZ = canvas.lines.map(l => l.zIndex ?? 0);\n const lineMaxZ = lineAllZ.length > 0 ? Math.max(...lineAllZ) : 0;\n const lineMinZ = lineAllZ.length > 0 ? Math.min(...lineAllZ) : 0;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => nodeIds.has(n.id) ? { ...n, zIndex: applyZ(n.zIndex ?? 0, nodeMaxZ, nodeMinZ) } : n),\n lines: canvas.lines.map(l => lineIds.has(l.id) ? { ...l, zIndex: applyZ(l.zIndex ?? 0, lineMaxZ, lineMinZ) } : l),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId, selection],\n );\n\n return { align, distribute, changeZIndex };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLineMutations.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useLineMutations.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowLine } from '../types';\n\nexport function useLineMutations() {\n const { setDoc, currentCanvasId } = useFlowContext();\n\n const addLine = useCallback(\n (line: FlowLine) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: { ...canvas, lines: [...canvas.lines, line] },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const updateLine = useCallback(\n (id: string, updates: Partial<Omit<FlowLine, 'id'>>) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n lines: canvas.lines.map(l => (l.id === id ? { ...l, ...updates } : l)),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const deleteLines = useCallback(\n (ids: Set<string>) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n lines: canvas.lines.filter(l => !ids.has(l.id)),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n return { addLine, updateLine, deleteLines };\n}\n"],"mappings":";;;AAIA,
|
|
1
|
+
{"version":3,"file":"useLineMutations.js","names":["useLineMutations","useFlowContext","useCallback","canvas","prev","currentCanvasId","line","setDoc","l","id","updates","ids"],"sources":["../../../../../src/components/pro/Flow/hooks/useLineMutations.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowLine } from '../types';\n\nexport function useLineMutations() {\n const { setDoc, currentCanvasId } = useFlowContext();\n\n const addLine = useCallback(\n (line: FlowLine) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: { ...canvas, lines: [...canvas.lines, line] },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const updateLine = useCallback(\n (id: string, updates: Partial<Omit<FlowLine, 'id'>>) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n lines: canvas.lines.map(l => (l.id === id ? { ...l, ...updates } : l)),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const deleteLines = useCallback(\n (ids: Set<string>) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n lines: canvas.lines.filter(l => !ids.has(l.id)),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n return { addLine, updateLine, deleteLines };\n}\n"],"mappings":";;;AAIA,SAAgBA,IAAmB;CACjC,IAAM,EAAE,WAAQ,uBAAoBC,EAAe;CA2DnD,OAAO;EAAE,SAzDOC,GACb,MAAmB;GAClB,GAAO,MAAQ;IACb,IAAMC,IAASC,EAAK,SAASC;IAE7B,OADKF,IACE;KACL,GAAGC;KACH,UAAU;MACR,GAAGA,EAAK;OACPC,IAAkB;OAAE,GAAGF;OAAQ,OAAO,CAAC,GAAGA,EAAO,OAAOG,CAAI;MAAE;KACjE;IACF,IAPoBF;GAQtB,CAAC;EACH,GACA,CAACG,GAAQF,CAAe,CA2CjB;EAAS,YAxCCH,GAChB,GAAY,MAA2C;GACtD,GAAO,MAAQ;IACb,IAAMC,IAASC,EAAK,SAASC;IAE7B,OADKF,IACE;KACL,GAAGC;KACH,UAAU;MACR,GAAGA,EAAK;OACPC,IAAkB;OACjB,GAAGF;OACH,OAAOA,EAAO,MAAM,KAAI,MAAMK,EAAE,OAAOC,IAAK;QAAE,GAAGD;QAAG,GAAGE;OAAQ,IAAIF,CAAE;MACvE;KACF;IACF,IAVoBJ;GAWtB,CAAC;EACH,GACA,CAACG,GAAQF,CAAe,CAuBR;EAAY,aApBVH,GACjB,MAAqB;GACpB,GAAO,MAAQ;IACb,IAAMC,IAASC,EAAK,SAASC;IAE7B,OADKF,IACE;KACL,GAAGC;KACH,UAAU;MACR,GAAGA,EAAK;OACPC,IAAkB;OACjB,GAAGF;OACH,OAAOA,EAAO,MAAM,QAAO,MAAK,CAACQ,EAAI,IAAIH,EAAE,EAAE,CAAC;MAChD;KACF;IACF,IAVoBJ;GAWtB,CAAC;EACH,GACA,CAACG,GAAQF,CAAe,CAGI;CAAY;AAC5C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMindMap.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useMindMap.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { DEFAULT_EDGE_DATA, DEFAULT_NODE_DATA } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\nimport type { FlowCanvasData, FlowNode } from '../types';\n\nconst NODE_WIDTH = 150;\nconst NODE_HEIGHT = 52;\nconst LEVEL_GAP = 100;\nconst ROW_GAP = 26;\n\nfunction layoutMindMap(\n canvas: FlowCanvasData,\n rootId: string,\n fixed?: { id: string; position: { x: number; y: number } },\n): Map<string, { x: number; y: number }> {\n const nodes = canvas.nodes.filter(node => node.data.isMindMapNode);\n const nodeMap = new Map(nodes.map(node => [node.id, node]));\n const children = new Map<string, FlowNode[]>();\n for (const node of nodes) {\n const parentId = node.data.mindMapParentId;\n if (!parentId || !nodeMap.has(parentId)) continue;\n const list = children.get(parentId) ?? [];\n list.push(node);\n children.set(parentId, list);\n }\n\n const root = nodeMap.get(rootId);\n if (!root) return new Map();\n const positions = new Map<string, { x: number; y: number }>();\n let nextY = root.position.y;\n\n const place = (node: FlowNode, depth: number): number => {\n const kids = children.get(node.id) ?? [];\n let centerY: number;\n if (kids.length === 0) {\n centerY = nextY;\n nextY += NODE_HEIGHT + ROW_GAP;\n } else {\n const childYs = kids.map(child => place(child, depth + 1));\n centerY = (childYs[0] + childYs[childYs.length - 1]) / 2;\n }\n positions.set(node.id, {\n x: root.position.x + depth * (NODE_WIDTH + LEVEL_GAP),\n y: centerY,\n });\n return centerY;\n };\n\n place(root, 0);\n if (fixed) {\n const computed = positions.get(fixed.id);\n if (computed) {\n const dx = fixed.position.x - computed.x;\n const dy = fixed.position.y - computed.y;\n for (const [id, position] of positions) {\n positions.set(id, { x: position.x + dx, y: position.y + dy });\n }\n }\n }\n return positions;\n}\n\nexport function useMindMap() {\n const { setEditingNodeId, focusOnRect, autoFocus } = useFlowUI();\n const { currentCanvas, currentCanvasId, selection, setSelection, setDoc } = useFlowContext();\n\n const focusNode = useCallback((node: FlowNode | undefined) => {\n if (!node) return;\n window.setTimeout(() => {\n focusOnRect(node.position.x, node.position.y, node.width, node.height, {\n padding: 80,\n preserveZoom: true,\n duration: 280,\n });\n }, 0);\n }, [focusOnRect]);\n\n const createNode = useCallback((kind: 'child' | 'sibling-after' | 'sibling-before') => {\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n if (!selected) return;\n\n // A regular Flow node can become the root of a mind map on first use.\n const parentId = kind === 'child' ? selected.id : selected.data.mindMapParentId;\n if (kind !== 'child' && !parentId) return;\n const id = generateId('mind');\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: selected.position.x + NODE_WIDTH + LEVEL_GAP, y: selected.position.y + NODE_HEIGHT + ROW_GAP },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: Math.max(0, ...currentCanvas.nodes.map(item => item.zIndex ?? 0)) + 1,\n data: {\n // Colors follow the theme (`theme:*` tokens from DEFAULT_NODE_DATA) so\n // mind maps re-color automatically when the DishUI theme changes.\n ...DEFAULT_NODE_DATA,\n label: kind === 'child' ? '子主题' : '主题',\n isMindMapNode: true,\n mindMapParentId: parentId,\n },\n };\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n const rootId = (() => {\n let cursor = selected;\n const map = new Map(canvas.nodes.map(item => [item.id, item]));\n while (cursor.data.mindMapParentId && map.has(cursor.data.mindMapParentId)) {\n cursor = map.get(cursor.data.mindMapParentId)!;\n }\n return cursor.id;\n })();\n const normalizedNodes = canvas.nodes.map(item => item.id === selected.id && !item.data.isMindMapNode\n ? { ...item, data: { ...item.data, isMindMapNode: true } }\n : item);\n const selectedIndex = normalizedNodes.findIndex(item => item.id === selected.id);\n const insertIndex = kind === 'sibling-before'\n ? selectedIndex\n : selectedIndex + 1;\n normalizedNodes.splice(Math.max(0, insertIndex), 0, node);\n const nextCanvas: FlowCanvasData = {\n ...canvas,\n nodes: normalizedNodes,\n edges: [...canvas.edges, {\n id: generateId('edge'),\n source: parentId!,\n target: id,\n // Mindmap layout grows strictly left-to-right: always connect the\n // side midpoints (parent right-center → child left-center), matching\n // the pinned-handle behavior of manual connections.\n sourceHandle: 'r_0.5',\n targetHandle: 'l_0.5',\n type: 'simplebezier',\n data: { ...DEFAULT_EDGE_DATA, markerEnd: 'none', strokeWidth: 1.5 },\n }],\n };\n // Child insertion keeps the current node fixed. Sibling insertion keeps\n // their shared parent fixed, so adding nodes never makes the parent jump.\n const fixedNode = kind === 'child'\n ? selected\n : canvas.nodes.find(item => item.id === parentId) ?? selected;\n const positions = layoutMindMap(nextCanvas, rootId, {\n id: fixedNode.id,\n position: fixedNode.position,\n });\n nextCanvas.nodes = nextCanvas.nodes.map(item => {\n const pos = positions.get(item.id);\n return pos ? { ...item, position: pos } : item;\n });\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: nextCanvas } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const createRoot = useCallback(() => {\n if (selection.nodeIds.size !== 0) return false;\n const id = generateId('mind');\n const highestZ = Math.max(0, ...currentCanvas.nodes.map(node => node.zIndex ?? 0));\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: 120, y: 120 },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: highestZ + 1,\n // Root keeps a primary-colored outline for hierarchy emphasis; every\n // color is still a theme token, so it follows the active theme.\n data: { ...DEFAULT_NODE_DATA, label: '中心主题', stroke: 'theme:primary', isMindMapNode: true },\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: { ...canvas, nodes: [...canvas.nodes, node] } } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n return true;\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const focusedNodeId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n\n // Center only when keyboard focus/selection moves to another node. Node drag\n // updates currentCanvas continuously; depending on currentCanvas here would\n // make the viewport chase the mouse while an object is being repositioned.\n useEffect(() => {\n if (!autoFocus || !focusedNodeId) return;\n const node = currentCanvas.nodes.find(item => item.id === focusedNodeId);\n if (!node) return;\n const timer = window.setTimeout(() => focusNode(node), 20);\n return () => window.clearTimeout(timer);\n // currentCanvas is intentionally excluded: position-only updates must not\n // retrigger viewport focus while dragging.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoFocus, focusedNodeId, focusNode]);\n\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n // Never intercept native editing keys from form controls. In particular,\n // inspector textareas must keep Enter/Tab for multiline media source.\n if (target.matches('input, textarea, select, .flow-label-editor') || target.isContentEditable) return;\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n\n const consume = () => {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n };\n\n if (event.key === 'Tab') {\n consume();\n if (!selected) createRoot();\n else createNode('child');\n } else if (event.key === 'Enter' && selected) {\n consume();\n createNode(event.shiftKey ? 'sibling-before' : 'sibling-after');\n } else if ((event.key === ' ' || event.code === 'Space') && selected) {\n consume();\n setEditingNodeId(selected.id);\n } else if (selected && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {\n const nodeMap = new Map(currentCanvas.nodes.map(node => [node.id, node]));\n const siblings = currentCanvas.nodes.filter(node =>\n node.data.isMindMapNode && node.data.mindMapParentId === selected.data.mindMapParentId,\n );\n const siblingIndex = siblings.findIndex(node => node.id === selected.id);\n const children = currentCanvas.nodes.filter(node => node.data.mindMapParentId === selected.id);\n let targetNode: FlowNode | undefined;\n if (event.key === 'ArrowLeft' && selected.data.mindMapParentId) {\n targetNode = nodeMap.get(selected.data.mindMapParentId);\n } else if (event.key === 'ArrowRight') {\n targetNode = children[0];\n } else if (event.key === 'ArrowUp' && siblingIndex > 0) {\n targetNode = siblings[siblingIndex - 1];\n } else if (event.key === 'ArrowDown' && siblingIndex >= 0 && siblingIndex < siblings.length - 1) {\n targetNode = siblings[siblingIndex + 1];\n }\n\n // Regular Flow nodes have no mind-map relationship metadata. Fall back\n // to spatial navigation, choosing the nearest node in the requested\n // direction. The perpendicular penalty favors visually aligned nodes.\n if (!targetNode) {\n const sx = selected.position.x + selected.width / 2;\n const sy = selected.position.y + selected.height / 2;\n const candidates = currentCanvas.nodes\n .filter(node => node.id !== selected.id && node.type !== 'animAnchor')\n .map(node => {\n const dx = node.position.x + node.width / 2 - sx;\n const dy = node.position.y + node.height / 2 - sy;\n const inDirection = event.key === 'ArrowLeft' ? dx < 0\n : event.key === 'ArrowRight' ? dx > 0\n : event.key === 'ArrowUp' ? dy < 0\n : dy > 0;\n const primary = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dx)\n : Math.abs(dy);\n const perpendicular = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dy)\n : Math.abs(dx);\n return { node, inDirection, score: primary + perpendicular * 1.75 };\n })\n .filter(candidate => candidate.inDirection)\n .sort((a, b) => a.score - b.score);\n targetNode = candidates[0]?.node;\n }\n\n if (targetNode) {\n consume();\n setSelection({ nodeIds: new Set([targetNode.id]), edgeIds: new Set(), lineIds: new Set() });\n }\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [selection, currentCanvas, createNode, createRoot, setEditingNodeId, setSelection]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useMindMap.js","names":["NODE_WIDTH","NODE_HEIGHT","LEVEL_GAP","ROW_GAP","layoutMindMap","nodes","canvas","node","nodeMap","children","parentId","list","root","rootId","positions","nextY","place","kids","centerY","childYs","child","depth","fixed","computed","dx","dy","id","position","useMindMap","useFlowUI","useFlowContext","focusNode","useCallback","focusOnRect","createNode","selectedId","selection","selected","currentCanvas","kind","generateId","item","DEFAULT_NODE_DATA","setDoc","prev","currentCanvasId","cursor","map","normalizedNodes","selectedIndex","insertIndex","nextCanvas","DEFAULT_EDGE_DATA","fixedNode","pos","setSelection","createRoot","focusedNodeId","useEffect","autoFocus","timer","onKey","target","event","consume","setEditingNodeId","siblings","siblingIndex","targetNode","sx","sy","candidate","a","b"],"sources":["../../../../../src/components/pro/Flow/hooks/useMindMap.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { DEFAULT_EDGE_DATA, DEFAULT_NODE_DATA } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\nimport type { FlowCanvasData, FlowNode } from '../types';\n\nconst NODE_WIDTH = 150;\nconst NODE_HEIGHT = 52;\nconst LEVEL_GAP = 100;\nconst ROW_GAP = 26;\n\nfunction layoutMindMap(\n canvas: FlowCanvasData,\n rootId: string,\n fixed?: { id: string; position: { x: number; y: number } },\n): Map<string, { x: number; y: number }> {\n const nodes = canvas.nodes.filter(node => node.data.isMindMapNode);\n const nodeMap = new Map(nodes.map(node => [node.id, node]));\n const children = new Map<string, FlowNode[]>();\n for (const node of nodes) {\n const parentId = node.data.mindMapParentId;\n if (!parentId || !nodeMap.has(parentId)) continue;\n const list = children.get(parentId) ?? [];\n list.push(node);\n children.set(parentId, list);\n }\n\n const root = nodeMap.get(rootId);\n if (!root) return new Map();\n const positions = new Map<string, { x: number; y: number }>();\n let nextY = root.position.y;\n\n const place = (node: FlowNode, depth: number): number => {\n const kids = children.get(node.id) ?? [];\n let centerY: number;\n if (kids.length === 0) {\n centerY = nextY;\n nextY += NODE_HEIGHT + ROW_GAP;\n } else {\n const childYs = kids.map(child => place(child, depth + 1));\n centerY = (childYs[0] + childYs[childYs.length - 1]) / 2;\n }\n positions.set(node.id, {\n x: root.position.x + depth * (NODE_WIDTH + LEVEL_GAP),\n y: centerY,\n });\n return centerY;\n };\n\n place(root, 0);\n if (fixed) {\n const computed = positions.get(fixed.id);\n if (computed) {\n const dx = fixed.position.x - computed.x;\n const dy = fixed.position.y - computed.y;\n for (const [id, position] of positions) {\n positions.set(id, { x: position.x + dx, y: position.y + dy });\n }\n }\n }\n return positions;\n}\n\nexport function useMindMap() {\n const { setEditingNodeId, focusOnRect, autoFocus } = useFlowUI();\n const { currentCanvas, currentCanvasId, selection, setSelection, setDoc } = useFlowContext();\n\n const focusNode = useCallback((node: FlowNode | undefined) => {\n if (!node) return;\n window.setTimeout(() => {\n focusOnRect(node.position.x, node.position.y, node.width, node.height, {\n padding: 80,\n preserveZoom: true,\n duration: 280,\n });\n }, 0);\n }, [focusOnRect]);\n\n const createNode = useCallback((kind: 'child' | 'sibling-after' | 'sibling-before') => {\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n if (!selected) return;\n\n // A regular Flow node can become the root of a mind map on first use.\n const parentId = kind === 'child' ? selected.id : selected.data.mindMapParentId;\n if (kind !== 'child' && !parentId) return;\n const id = generateId('mind');\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: selected.position.x + NODE_WIDTH + LEVEL_GAP, y: selected.position.y + NODE_HEIGHT + ROW_GAP },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: Math.max(0, ...currentCanvas.nodes.map(item => item.zIndex ?? 0)) + 1,\n data: {\n // Colors follow the theme (`theme:*` tokens from DEFAULT_NODE_DATA) so\n // mind maps re-color automatically when the DishUI theme changes.\n ...DEFAULT_NODE_DATA,\n label: kind === 'child' ? '子主题' : '主题',\n isMindMapNode: true,\n mindMapParentId: parentId,\n },\n };\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n const rootId = (() => {\n let cursor = selected;\n const map = new Map(canvas.nodes.map(item => [item.id, item]));\n while (cursor.data.mindMapParentId && map.has(cursor.data.mindMapParentId)) {\n cursor = map.get(cursor.data.mindMapParentId)!;\n }\n return cursor.id;\n })();\n const normalizedNodes = canvas.nodes.map(item => item.id === selected.id && !item.data.isMindMapNode\n ? { ...item, data: { ...item.data, isMindMapNode: true } }\n : item);\n const selectedIndex = normalizedNodes.findIndex(item => item.id === selected.id);\n const insertIndex = kind === 'sibling-before'\n ? selectedIndex\n : selectedIndex + 1;\n normalizedNodes.splice(Math.max(0, insertIndex), 0, node);\n const nextCanvas: FlowCanvasData = {\n ...canvas,\n nodes: normalizedNodes,\n edges: [...canvas.edges, {\n id: generateId('edge'),\n source: parentId!,\n target: id,\n // Mindmap layout grows strictly left-to-right: always connect the\n // side midpoints (parent right-center → child left-center), matching\n // the pinned-handle behavior of manual connections.\n sourceHandle: 'r_0.5',\n targetHandle: 'l_0.5',\n type: 'simplebezier',\n data: { ...DEFAULT_EDGE_DATA, markerEnd: 'none', strokeWidth: 1.5 },\n }],\n };\n // Child insertion keeps the current node fixed. Sibling insertion keeps\n // their shared parent fixed, so adding nodes never makes the parent jump.\n const fixedNode = kind === 'child'\n ? selected\n : canvas.nodes.find(item => item.id === parentId) ?? selected;\n const positions = layoutMindMap(nextCanvas, rootId, {\n id: fixedNode.id,\n position: fixedNode.position,\n });\n nextCanvas.nodes = nextCanvas.nodes.map(item => {\n const pos = positions.get(item.id);\n return pos ? { ...item, position: pos } : item;\n });\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: nextCanvas } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const createRoot = useCallback(() => {\n if (selection.nodeIds.size !== 0) return false;\n const id = generateId('mind');\n const highestZ = Math.max(0, ...currentCanvas.nodes.map(node => node.zIndex ?? 0));\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: 120, y: 120 },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: highestZ + 1,\n // Root keeps a primary-colored outline for hierarchy emphasis; every\n // color is still a theme token, so it follows the active theme.\n data: { ...DEFAULT_NODE_DATA, label: '中心主题', stroke: 'theme:primary', isMindMapNode: true },\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: { ...canvas, nodes: [...canvas.nodes, node] } } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n return true;\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const focusedNodeId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n\n // Center only when keyboard focus/selection moves to another node. Node drag\n // updates currentCanvas continuously; depending on currentCanvas here would\n // make the viewport chase the mouse while an object is being repositioned.\n useEffect(() => {\n if (!autoFocus || !focusedNodeId) return;\n const node = currentCanvas.nodes.find(item => item.id === focusedNodeId);\n if (!node) return;\n const timer = window.setTimeout(() => focusNode(node), 20);\n return () => window.clearTimeout(timer);\n // currentCanvas is intentionally excluded: position-only updates must not\n // retrigger viewport focus while dragging.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoFocus, focusedNodeId, focusNode]);\n\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n // Never intercept native editing keys from form controls. In particular,\n // inspector textareas must keep Enter/Tab for multiline media source.\n if (target.matches('input, textarea, select, .flow-label-editor') || target.isContentEditable) return;\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n\n const consume = () => {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n };\n\n if (event.key === 'Tab') {\n consume();\n if (!selected) createRoot();\n else createNode('child');\n } else if (event.key === 'Enter' && selected) {\n consume();\n createNode(event.shiftKey ? 'sibling-before' : 'sibling-after');\n } else if ((event.key === ' ' || event.code === 'Space') && selected) {\n consume();\n setEditingNodeId(selected.id);\n } else if (selected && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {\n const nodeMap = new Map(currentCanvas.nodes.map(node => [node.id, node]));\n const siblings = currentCanvas.nodes.filter(node =>\n node.data.isMindMapNode && node.data.mindMapParentId === selected.data.mindMapParentId,\n );\n const siblingIndex = siblings.findIndex(node => node.id === selected.id);\n const children = currentCanvas.nodes.filter(node => node.data.mindMapParentId === selected.id);\n let targetNode: FlowNode | undefined;\n if (event.key === 'ArrowLeft' && selected.data.mindMapParentId) {\n targetNode = nodeMap.get(selected.data.mindMapParentId);\n } else if (event.key === 'ArrowRight') {\n targetNode = children[0];\n } else if (event.key === 'ArrowUp' && siblingIndex > 0) {\n targetNode = siblings[siblingIndex - 1];\n } else if (event.key === 'ArrowDown' && siblingIndex >= 0 && siblingIndex < siblings.length - 1) {\n targetNode = siblings[siblingIndex + 1];\n }\n\n // Regular Flow nodes have no mind-map relationship metadata. Fall back\n // to spatial navigation, choosing the nearest node in the requested\n // direction. The perpendicular penalty favors visually aligned nodes.\n if (!targetNode) {\n const sx = selected.position.x + selected.width / 2;\n const sy = selected.position.y + selected.height / 2;\n const candidates = currentCanvas.nodes\n .filter(node => node.id !== selected.id && node.type !== 'animAnchor')\n .map(node => {\n const dx = node.position.x + node.width / 2 - sx;\n const dy = node.position.y + node.height / 2 - sy;\n const inDirection = event.key === 'ArrowLeft' ? dx < 0\n : event.key === 'ArrowRight' ? dx > 0\n : event.key === 'ArrowUp' ? dy < 0\n : dy > 0;\n const primary = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dx)\n : Math.abs(dy);\n const perpendicular = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dy)\n : Math.abs(dx);\n return { node, inDirection, score: primary + perpendicular * 1.75 };\n })\n .filter(candidate => candidate.inDirection)\n .sort((a, b) => a.score - b.score);\n targetNode = candidates[0]?.node;\n }\n\n if (targetNode) {\n consume();\n setSelection({ nodeIds: new Set([targetNode.id]), edgeIds: new Set(), lineIds: new Set() });\n }\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [selection, currentCanvas, createNode, createRoot, setEditingNodeId, setSelection]);\n}\n"],"mappings":";;;;;;AAOA,IAAMA,IAAa,KACbC,IAAc,IACdC,IAAY,KACZC,IAAU;AAEhB,SAASC,EACP,GACA,GACA,GACuC;CACvC,IAAMC,IAAQC,EAAO,MAAM,QAAO,MAAQC,EAAK,KAAK,aAAa,GAC3DC,IAAU,IAAI,IAAIH,EAAM,KAAI,MAAQ,CAACE,EAAK,IAAIA,CAAI,CAAC,CAAC,GACpDE,oBAAW,IAAI,IAAwB;CAC7C,KAAK,IAAMF,KAAQF,GAAO;EACxB,IAAMK,IAAWH,EAAK,KAAK;EAC3B,IAAI,CAACG,KAAY,CAACF,EAAQ,IAAIE,CAAQ,GAAG;EACzC,IAAMC,IAAOF,EAAS,IAAIC,CAAQ,KAAK,CAAC;EAExC,AADAC,EAAK,KAAKJ,CAAI,GACdE,EAAS,IAAIC,GAAUC,CAAI;CAC7B;CAEA,IAAMC,IAAOJ,EAAQ,IAAIK,CAAM;CAC/B,IAAI,CAACD,GAAM,uBAAO,IAAI,IAAI;CAC1B,IAAME,oBAAY,IAAI,IAAsC,GACxDC,IAAQH,EAAK,SAAS,GAEpBI,KAAS,GAAgB,MAA0B;EACvD,IAAMC,IAAOR,EAAS,IAAIF,EAAK,EAAE,KAAK,CAAC,GACnCW;EACJ,IAAID,EAAK,WAAW,GAElB,AADA,IAAUF,GACV,KAAS;OACJ;GACL,IAAMI,IAAUF,EAAK,KAAI,MAASD,EAAMI,GAAOC,IAAQ,CAAC,CAAC;GACzD,KAAWF,EAAQ,KAAKA,EAAQA,EAAQ,SAAS,MAAM;EACzD;EAKA,OAJAL,EAAU,IAAIP,EAAK,IAAI;GACrB,GAAGK,EAAK,SAAS,IAAIS,IAAS;GAC9B,GAAGH;EACL,CAAC,GACMA;CACT;CAGA,IADAF,EAAMJ,GAAM,CAAC,GACTU,GAAO;EACT,IAAMC,IAAWT,EAAU,IAAIQ,EAAM,EAAE;EACvC,IAAIC,GAAU;GACZ,IAAMC,IAAKF,EAAM,SAAS,IAAIC,EAAS,GACjCE,IAAKH,EAAM,SAAS,IAAIC,EAAS;GACvC,KAAK,IAAM,CAACG,GAAIC,MAAab,GAC3B,EAAU,IAAIY,GAAI;IAAE,GAAGC,EAAS,IAAIH;IAAI,GAAGG,EAAS,IAAIF;GAAG,CAAC;EAEhE;CACF;CACA,OAAOX;AACT;AAEA,SAAgBc,IAAa;CAC3B,IAAM,EAAE,qBAAkB,gBAAa,iBAAcC,EAAU,GACzD,EAAE,kBAAe,oBAAiB,cAAW,iBAAc,cAAWC,EAAe,GAErFC,IAAYC,GAAa,MAA+B;EAC5D,AAAKzB,KACL,OAAO,iBAAiB;GACtB,EAAYA,EAAK,SAAS,GAAGA,EAAK,SAAS,GAAGA,EAAK,OAAOA,EAAK,QAAQ;IACrE,SAAS;IACT,cAAc;IACd,UAAU;GACZ,CAAC;EACH,GAAG,CAAC;CACN,GAAG,CAAC0B,CAAW,CAAC,GAEVC,IAAaF,GAAa,MAAuD;EACrF,IAAMG,IAAaC,EAAU,QAAQ,SAAS,IAAI,CAAC,GAAGA,EAAU,OAAO,CAAC,CAAC,KAAK,MACxEC,IAAWF,IAAaG,EAAc,MAAM,MAAK,MAAQ/B,EAAK,OAAO4B,CAAU,IAAI,KAAA;EACzF,IAAI,CAACE,GAAU;EAGf,IAAM3B,IAAW6B,MAAS,UAAUF,EAAS,KAAKA,EAAS,KAAK;EAChE,IAAIE,MAAS,WAAW,CAAC7B,GAAU;EACnC,IAAMgB,IAAKc,EAAW,MAAM,GACtBjC,IAAiB;GACrB;GACA,MAAM;GACN,UAAU;IAAE,GAAG8B,EAAS,SAAS,IAAIrC,IAAaE;IAAW,GAAGmC,EAAS,SAAS,IAAIpC,IAAcE;GAAQ;GAC5G,OAAOH;GACP,QAAQC;GACR,QAAQ,KAAK,IAAI,GAAG,GAAGqC,EAAc,MAAM,KAAI,MAAQG,EAAK,UAAU,CAAC,CAAC,IAAI;GAC5E,MAAM;IAGJ,GAAGC;IACH,OAAOH,MAAS,UAAU,QAAQ;IAClC,eAAe;IACf,iBAAiB7B;GACnB;EACF;EAoDA,AAlDAiC,GAAO,MAAQ;GACb,IAAMrC,IAASsC,EAAK,SAASC;GAC7B,IAAI,CAACvC,GAAQ,OAAOsC;GACpB,IAAM/B,WAAgB;IACpB,IAAIiC,IAAST,GACPU,IAAM,IAAI,IAAIzC,EAAO,MAAM,KAAI,MAAQ,CAACmC,EAAK,IAAIA,CAAI,CAAC,CAAC;IAC7D,OAAOK,EAAO,KAAK,mBAAmBC,EAAI,IAAID,EAAO,KAAK,eAAe,IACvE,IAASC,EAAI,IAAID,EAAO,KAAK,eAAe;IAE9C,OAAOA,EAAO;GAChB,EAAA,CAAG,GACGE,IAAkB1C,EAAO,MAAM,KAAI,MAAQmC,EAAK,OAAOJ,EAAS,MAAM,CAACI,EAAK,KAAK,gBACnF;IAAE,GAAGA;IAAM,MAAM;KAAE,GAAGA,EAAK;KAAM,eAAe;IAAK;GAAE,IACvDA,CAAI,GACFQ,IAAgBD,EAAgB,WAAU,MAAQP,EAAK,OAAOJ,EAAS,EAAE,GACzEa,IAAcX,MAAS,mBACzBU,IACAA,IAAgB;GACpB,EAAgB,OAAO,KAAK,IAAI,GAAGC,CAAW,GAAG,GAAG3C,CAAI;GACxD,IAAM4C,IAA6B;IACjC,GAAG7C;IACH,OAAO0C;IACP,OAAO,CAAC,GAAG1C,EAAO,OAAO;KACvB,IAAIkC,EAAW,MAAM;KACrB,QAAQ9B;KACR,QAAQgB;KAIR,cAAc;KACd,cAAc;KACd,MAAM;KACN,MAAM;MAAE,GAAG0B;MAAmB,WAAW;MAAQ,aAAa;KAAI;IACpE,CAAC;GACH,GAGMC,IAAYd,MAAS,UACvBF,IACA/B,EAAO,MAAM,MAAK,MAAQmC,EAAK,OAAO/B,CAAQ,KAAK2B,GACjDvB,IAAYV,EAAc+C,GAAYtC,GAAQ;IAClD,IAAIwC,EAAU;IACd,UAAUA,EAAU;GACtB,CAAC;GAKD,OAJA,EAAW,QAAQF,EAAW,MAAM,KAAI,MAAQ;IAC9C,IAAMG,IAAMxC,EAAU,IAAI2B,EAAK,EAAE;IACjC,OAAOa,IAAM;KAAE,GAAGb;KAAM,UAAUa;IAAI,IAAIb;GAC5C,CAAC,GACM;IAAE,GAAGG;IAAM,UAAU;KAAE,GAAGA,EAAK;MAAWC,IAAkBM;IAAW;GAAE;EAClF,CAAC,GACDI,EAAa;GAAE,yBAAS,IAAI,IAAI,CAAC7B,CAAE,CAAC;GAAG,yBAAS,IAAI,IAAI;GAAG,yBAAS,IAAI,IAAI;EAAE,CAAC;CACjF,GAAG;EAACU;EAAWE;EAAeO;EAAiBF;EAAQY;CAAY,CAAC,GAE9DC,IAAaxB,QAAkB;EACnC,IAAII,EAAU,QAAQ,SAAS,GAAG,OAAO;EACzC,IAAMV,IAAKc,EAAW,MAAM,GAEtBjC,IAAiB;GACrB;GACA,MAAM;GACN,UAAU;IAAE,GAAG;IAAK,GAAG;GAAI;GAC3B,OAAOP;GACP,QAAQC;GACR,QAPe,KAAK,IAAI,GAAG,GAAGqC,EAAc,MAAM,KAAI,MAAQ/B,EAAK,UAAU,CAAC,CAOtE,IAAW;GAGnB,MAAM;IAAE,GAAGmC;IAAmB,OAAO;IAAQ,QAAQ;IAAiB,eAAe;GAAK;EAC5F;EAOA,OANAC,GAAO,MAAQ;GACb,IAAMrC,IAASsC,EAAK,SAASC;GAE7B,OADKvC,IACE;IAAE,GAAGsC;IAAM,UAAU;KAAE,GAAGA,EAAK;MAAWC,IAAkB;MAAE,GAAGvC;MAAQ,OAAO,CAAC,GAAGA,EAAO,OAAOC,CAAI;KAAE;IAAE;GAAE,IAD/FqC;EAEtB,CAAC,GACDW,EAAa;GAAE,yBAAS,IAAI,IAAI,CAAC7B,CAAE,CAAC;GAAG,yBAAS,IAAI,IAAI;GAAG,yBAAS,IAAI,IAAI;EAAE,CAAC,GACxE;CACT,GAAG;EAACU;EAAWE;EAAeO;EAAiBF;EAAQY;CAAY,CAAC,GAE9DE,IAAgBrB,EAAU,QAAQ,SAAS,IAAI,CAAC,GAAGA,EAAU,OAAO,CAAC,CAAC,KAAK;CAgBjF,AAXAsB,QAAgB;EACd,IAAI,CAACC,KAAa,CAACF,GAAe;EAClC,IAAMlD,IAAO+B,EAAc,MAAM,MAAK,MAAQG,EAAK,OAAOgB,CAAa;EACvE,IAAI,CAAClD,GAAM;EACX,IAAMqD,IAAQ,OAAO,iBAAiB7B,EAAUxB,CAAI,GAAG,EAAE;EACzD,aAAa,OAAO,aAAaqD,CAAK;CAIxC,GAAG;EAACD;EAAWF;EAAe1B;CAAS,CAAC,GAExC2B,QAAgB;EACd,IAAMG,KAAS,MAAyB;GACtC,IAAMC,IAASC,EAAM;GAGrB,IAAID,EAAO,QAAQ,6CAA6C,KAAKA,EAAO,mBAAmB;GAC/F,IAAM3B,IAAaC,EAAU,QAAQ,SAAS,IAAI,CAAC,GAAGA,EAAU,OAAO,CAAC,CAAC,KAAK,MACxEC,IAAWF,IAAaG,EAAc,MAAM,MAAK,MAAQ/B,EAAK,OAAO4B,CAAU,IAAI,KAAA,GAEnF6B,UAAgB;IAGpB,AAFAD,EAAM,eAAe,GACrBA,EAAM,gBAAgB,GACtBA,EAAM,yBAAyB;GACjC;GAEA,IAAIA,EAAM,QAAQ,OAEhB,AADAC,EAAQ,GACH3B,IACAH,EAAW,OAAO,IADRsB,EAAW;QAErB,IAAIO,EAAM,QAAQ,WAAW1B,GAElC,AADA2B,EAAQ,GACR9B,EAAW6B,EAAM,WAAW,mBAAmB,eAAe;QACzD,KAAKA,EAAM,QAAQ,OAAOA,EAAM,SAAS,YAAY1B,GAE1D,AADA2B,EAAQ,GACRC,EAAiB5B,EAAS,EAAE;QACvB,IAAIA,KAAY;IAAC;IAAW;IAAa;IAAa;GAAY,CAAC,CAAC,SAAS0B,EAAM,GAAG,GAAG;IAC9F,IAAMvD,IAAU,IAAI,IAAI8B,EAAc,MAAM,KAAI,MAAQ,CAAC/B,EAAK,IAAIA,CAAI,CAAC,CAAC,GAClE2D,IAAW5B,EAAc,MAAM,QAAO,MAC1C/B,EAAK,KAAK,iBAAiBA,EAAK,KAAK,oBAAoB8B,EAAS,KAAK,eACzE,GACM8B,IAAeD,EAAS,WAAU,MAAQ3D,EAAK,OAAO8B,EAAS,EAAE,GACjE5B,IAAW6B,EAAc,MAAM,QAAO,MAAQ/B,EAAK,KAAK,oBAAoB8B,EAAS,EAAE,GACzF+B;IAcJ,IAbIL,EAAM,QAAQ,eAAe1B,EAAS,KAAK,kBAC7C,IAAa7B,EAAQ,IAAI6B,EAAS,KAAK,eAAe,IAC7C0B,EAAM,QAAQ,eACvB,IAAatD,EAAS,KACbsD,EAAM,QAAQ,aAAaI,IAAe,IACnD,IAAaD,EAASC,IAAe,KAC5BJ,EAAM,QAAQ,eAAeI,KAAgB,KAAKA,IAAeD,EAAS,SAAS,MAC5F,IAAaA,EAASC,IAAe,KAMnC,CAACC,GAAY;KACf,IAAMC,IAAKhC,EAAS,SAAS,IAAIA,EAAS,QAAQ,GAC5CiC,IAAKjC,EAAS,SAAS,IAAIA,EAAS,SAAS;KAoBnD,IAnBmBC,EAAc,MAC9B,QAAO,MAAQ/B,EAAK,OAAO8B,EAAS,MAAM9B,EAAK,SAAS,YAAY,CAAC,CACrE,KAAI,MAAQ;MACX,IAAMiB,IAAKjB,EAAK,SAAS,IAAIA,EAAK,QAAQ,IAAI8D,GACxC5C,IAAKlB,EAAK,SAAS,IAAIA,EAAK,SAAS,IAAI+D;MAW/C,OAAO;OAAE;OAAM,aAVKP,EAAM,QAAQ,cAAcvC,IAAK,IACjDuC,EAAM,QAAQ,eAAevC,IAAK,IAChCuC,EAAM,QAAQ,YAAYtC,IAAK,IAC7BA,IAAK;OAOe,QANZsC,EAAM,QAAQ,eAAeA,EAAM,QAAQ,eACvD,KAAK,IAAIvC,CAAE,IACX,KAAK,IAAIC,CAAE,MACOsC,EAAM,QAAQ,eAAeA,EAAM,QAAQ,eAC7D,KAAK,IAAItC,CAAE,IACX,KAAK,IAAID,CAAE,KAC8C;MAAK;KACpE,CAAC,CAAC,CACD,QAAO,MAAa+C,EAAU,WAAW,CAAC,CAC1C,MAAM,GAAG,MAAMC,EAAE,QAAQC,EAAE,KACjB,CAAA,CAAW,EAAE,EAAE;IAC9B;IAEA,AAAIL,MACFJ,EAAQ,GACRT,EAAa;KAAE,yBAAS,IAAI,IAAI,CAACa,EAAW,EAAE,CAAC;KAAG,yBAAS,IAAI,IAAI;KAAG,yBAAS,IAAI,IAAI;IAAE,CAAC;GAE9F;EACF;EAEA,OADA,OAAO,iBAAiB,WAAWP,GAAO,EAAI,SACjC,OAAO,oBAAoB,WAAWA,GAAO,EAAI;CAChE,GAAG;EAACzB;EAAWE;EAAeJ;EAAYsB;EAAYS;EAAkBV;CAAY,CAAC;AACvF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNodeMutations.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useNodeMutations.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowNode, FlowNodeData, FlowShapeType } from '../types';\nimport { DEFAULT_NODE_DATA } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\n\n/**\n * Provides CRUD operations for nodes on the current canvas.\n */\nexport function useNodeMutations() {\n const { setDoc, currentCanvasId } = useFlowContext();\n\n const addNode = useCallback(\n (type: FlowShapeType, x: number, y: number, width: number, height: number, data?: Partial<FlowNodeData>): string => {\n const id = generateId('node');\n const node: FlowNode = {\n id,\n type,\n position: { x, y },\n width,\n height,\n data: { ...DEFAULT_NODE_DATA, ...data },\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: [...canvas.nodes, node],\n },\n },\n };\n });\n return id;\n },\n [setDoc, currentCanvasId],\n );\n\n const updateNode = useCallback(\n (id: string, updates: Partial<Pick<FlowNode, 'position' | 'width' | 'height' | 'zIndex'>> & { data?: Partial<FlowNodeData> }) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n if (n.id !== id) return n;\n return {\n ...n,\n ...('position' in updates ? { position: updates.position! } : {}),\n ...('width' in updates ? { width: updates.width! } : {}),\n ...('height' in updates ? { height: updates.height! } : {}),\n ...('zIndex' in updates ? { zIndex: updates.zIndex } : {}),\n data: updates.data ? { ...n.data, ...updates.data } : n.data,\n };\n }),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const moveNodes = useCallback(\n (ids: Set<string>, dx: number, dy: number) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n if (!ids.has(n.id)) return n;\n return { ...n, position: { x: n.position.x + dx, y: n.position.y + dy } };\n }),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const deleteNodes = useCallback(\n (ids: Set<string>) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.filter(n => !ids.has(n.id)),\n edges: canvas.edges.filter(e => !ids.has(e.source) && !ids.has(e.target)),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n return { addNode, updateNode, moveNodes, deleteNodes };\n}\n"],"mappings":";;;;;AASA,
|
|
1
|
+
{"version":3,"file":"useNodeMutations.js","names":["useNodeMutations","useFlowContext","useCallback","id","generateId","node","DEFAULT_NODE_DATA","data","setDoc","canvas","prev","currentCanvasId","updates","ids","n","dx","dy","e"],"sources":["../../../../../src/components/pro/Flow/hooks/useNodeMutations.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowNode, FlowNodeData, FlowShapeType } from '../types';\nimport { DEFAULT_NODE_DATA } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\n\n/**\n * Provides CRUD operations for nodes on the current canvas.\n */\nexport function useNodeMutations() {\n const { setDoc, currentCanvasId } = useFlowContext();\n\n const addNode = useCallback(\n (type: FlowShapeType, x: number, y: number, width: number, height: number, data?: Partial<FlowNodeData>): string => {\n const id = generateId('node');\n const node: FlowNode = {\n id,\n type,\n position: { x, y },\n width,\n height,\n data: { ...DEFAULT_NODE_DATA, ...data },\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: [...canvas.nodes, node],\n },\n },\n };\n });\n return id;\n },\n [setDoc, currentCanvasId],\n );\n\n const updateNode = useCallback(\n (id: string, updates: Partial<Pick<FlowNode, 'position' | 'width' | 'height' | 'zIndex'>> & { data?: Partial<FlowNodeData> }) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n if (n.id !== id) return n;\n return {\n ...n,\n ...('position' in updates ? { position: updates.position! } : {}),\n ...('width' in updates ? { width: updates.width! } : {}),\n ...('height' in updates ? { height: updates.height! } : {}),\n ...('zIndex' in updates ? { zIndex: updates.zIndex } : {}),\n data: updates.data ? { ...n.data, ...updates.data } : n.data,\n };\n }),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const moveNodes = useCallback(\n (ids: Set<string>, dx: number, dy: number) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.map(n => {\n if (!ids.has(n.id)) return n;\n return { ...n, position: { x: n.position.x + dx, y: n.position.y + dy } };\n }),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n const deleteNodes = useCallback(\n (ids: Set<string>) => {\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return {\n ...prev,\n canvases: {\n ...prev.canvases,\n [currentCanvasId]: {\n ...canvas,\n nodes: canvas.nodes.filter(n => !ids.has(n.id)),\n edges: canvas.edges.filter(e => !ids.has(e.source) && !ids.has(e.target)),\n },\n },\n };\n });\n },\n [setDoc, currentCanvasId],\n );\n\n return { addNode, updateNode, moveNodes, deleteNodes };\n}\n"],"mappings":";;;;;AASA,SAAgBA,IAAmB;CACjC,IAAM,EAAE,WAAQ,uBAAoBC,EAAe;CA0GnD,OAAO;EAAE,SAxGOC,GACb,GAAqB,GAAW,GAAW,GAAe,GAAgB,MAAyC;GAClH,IAAMC,IAAKC,EAAW,MAAM,GACtBC,IAAiB;IACrB;IACA;IACA,UAAU;KAAE;KAAG;IAAE;IACjB;IACA;IACA,MAAM;KAAE,GAAGC;KAAmB,GAAGC;IAAK;GACxC;GAeA,OAdAC,GAAO,MAAQ;IACb,IAAMC,IAASC,EAAK,SAASC;IAE7B,OADKF,IACE;KACL,GAAGC;KACH,UAAU;MACR,GAAGA,EAAK;OACPC,IAAkB;OACjB,GAAGF;OACH,OAAO,CAAC,GAAGA,EAAO,OAAOJ,CAAI;MAC/B;KACF;IACF,IAVoBK;GAWtB,CAAC,GACMP;EACT,GACA,CAACK,GAAQG,CAAe,CA6EjB;EAAS,YA1ECT,GAChB,GAAY,MAAoH;GAC/H,GAAO,MAAQ;IACb,IAAMO,IAASC,EAAK,SAASC;IAE7B,OADKF,IACE;KACL,GAAGC;KACH,UAAU;MACR,GAAGA,EAAK;OACPC,IAAkB;OACjB,GAAGF;OACH,OAAOA,EAAO,MAAM,KAAI,MAClB,EAAE,OAAON,IACN;QACL,GAAG;QACH,GAAI,cAAcS,IAAU,EAAE,UAAUA,EAAQ,SAAU,IAAI,CAAC;QAC/D,GAAI,WAAWA,IAAU,EAAE,OAAOA,EAAQ,MAAO,IAAI,CAAC;QACtD,GAAI,YAAYA,IAAU,EAAE,QAAQA,EAAQ,OAAQ,IAAI,CAAC;QACzD,GAAI,YAAYA,IAAU,EAAE,QAAQA,EAAQ,OAAO,IAAI,CAAC;QACxD,MAAMA,EAAQ,OAAO;SAAE,GAAG,EAAE;SAAM,GAAGA,EAAQ;QAAK,IAAI,EAAE;OAC1D,IARwB,CASzB;MACH;KACF;IACF,IApBoBF;GAqBtB,CAAC;EACH,GACA,CAACF,GAAQG,CAAe,CA+CR;EAAY,WA5CZT,GACf,GAAkB,GAAY,MAAe;GAC5C,GAAO,MAAQ;IACb,IAAMO,IAASC,EAAK,SAASC;IAE7B,OADKF,IACE;KACL,GAAGC;KACH,UAAU;MACR,GAAGA,EAAK;OACPC,IAAkB;OACjB,GAAGF;OACH,OAAOA,EAAO,MAAM,KAAI,MACjBI,EAAI,IAAIC,EAAE,EAAE,IACV;QAAE,GAAGA;QAAG,UAAU;SAAE,GAAGA,EAAE,SAAS,IAAIC;SAAI,GAAGD,EAAE,SAAS,IAAIE;QAAG;OAAE,IAD7CF,CAE5B;MACH;KACF;IACF,IAboBJ;GActB,CAAC;EACH,GACA,CAACF,GAAQG,CAAe,CAwBI;EAAW,aArBrBT,GACjB,MAAqB;GACpB,GAAO,MAAQ;IACb,IAAMO,IAASC,EAAK,SAASC;IAE7B,OADKF,IACE;KACL,GAAGC;KACH,UAAU;MACR,GAAGA,EAAK;OACPC,IAAkB;OACjB,GAAGF;OACH,OAAOA,EAAO,MAAM,QAAO,MAAK,CAACI,EAAI,IAAIC,EAAE,EAAE,CAAC;OAC9C,OAAOL,EAAO,MAAM,QAAO,MAAK,CAACI,EAAI,IAAII,EAAE,MAAM,KAAK,CAACJ,EAAI,IAAII,EAAE,MAAM,CAAC;MAC1E;KACF;IACF,IAXoBP;GAYtB,CAAC;EACH,GACA,CAACF,GAAQG,CAAe,CAGe;CAAY;AACvD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePdfExport.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/usePdfExport.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { getDocumentSlides, anchorFocusRect, canvasStackTo } from '../nodes/animAnchors';\nimport { buildPdfFromJpegs, pngDataUrlToJpeg } from '../utils/pdfExport';\nimport { downloadDataURL, safeFileBaseName } from './useFlowImportExport';\nimport type { PixiApp } from '../canvas/PixiApp';\n\n/** Await the next N animation frames (lets Pixi settle after a state change). */\nfunction nextFrames(n: number): Promise<void> {\n return new Promise((resolve) => {\n let left = n;\n const step = () => {\n if (--left <= 0) resolve();\n else requestAnimationFrame(step);\n };\n requestAnimationFrame(step);\n });\n}\n\n/**\n * Exports the document's animation anchors as a single PDF. Each page is cropped\n * exactly to an anchor's world rectangle (not the whole viewport), so the PDF\n * contains only the framed slide content. Pages follow the global anchor order,\n * navigating across sub-canvases as needed.\n *\n * Returns a callback; it no-ops (returns false) when there are no anchors.\n */\nexport function usePdfExport(pixiRef: React.RefObject<PixiApp | null>) {\n const { doc, setDocTransient } = useFlowContext();\n const { showAnchors, setShowAnchors } = useFlowUI();\n\n return useCallback(async (): Promise<boolean> => {\n const slides = getDocumentSlides(doc);\n if (slides.length === 0) return false;\n\n const pixi = pixiRef.current;\n if (!pixi || pixi.destroyed) return false;\n\n // Snapshot state we temporarily change, so we can restore it afterward.\n const originalStack = doc.stack.slice();\n const anchorsWereVisible = showAnchors;\n\n // Hide anchor frames so they don't appear in the exported pages. The region\n // capture drives the world transform directly and restores it each time, so\n // the interactive viewport is untouched.\n setShowAnchors(false);\n await nextFrames(2);\n\n // Render slides at a crisp scale so text/shapes stay sharp in the PDF.\n const CAPTURE_SCALE = 2;\n const pages: Awaited<ReturnType<typeof pngDataUrlToJpeg>>[] = [];\n try {\n for (const slide of slides) {\n // Switch to the slide's canvas if it lives on a nested sub-canvas.\n // Transient updates: slide paging must not pollute the undo history.\n const stack = canvasStackTo(doc, slide.canvasId);\n if (stack) {\n setDocTransient((prev) => ({ ...prev, stack }));\n // Canvas swap needs a couple frames to re-render the new node set.\n await nextFrames(3);\n }\n\n const r = anchorFocusRect(slide.node);\n const shot = await pixi.exportRegionPNG(r, {\n scale: CAPTURE_SCALE,\n backgroundColor: 0xffffff,\n });\n if (shot) pages.push(await pngDataUrlToJpeg(shot.dataUrl));\n }\n } finally {\n // Restore prior editor state regardless of success/failure.\n setShowAnchors(anchorsWereVisible);\n setDocTransient((prev) => ({ ...prev, stack: originalStack }));\n }\n\n if (pages.length === 0) return false;\n const blob = buildPdfFromJpegs(pages);\n const url = URL.createObjectURL(blob);\n downloadDataURL(url, `${safeFileBaseName(doc.title)}.pdf`);\n URL.revokeObjectURL(url);\n return true;\n }, [doc, setDocTransient, showAnchors, setShowAnchors, pixiRef]);\n}\n"],"mappings":";;;;;;;AASA,
|
|
1
|
+
{"version":3,"file":"usePdfExport.js","names":["nextFrames","left","n","step","resolve","usePdfExport","useFlowContext","useFlowUI","useCallback","slides","getDocumentSlides","doc","pixi","pixiRef","originalStack","anchorsWereVisible","showAnchors","setShowAnchors","pages","slide","stack","canvasStackTo","setDocTransient","prev","r","anchorFocusRect","shot","pngDataUrlToJpeg","blob","buildPdfFromJpegs","url","downloadDataURL","safeFileBaseName"],"sources":["../../../../../src/components/pro/Flow/hooks/usePdfExport.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { getDocumentSlides, anchorFocusRect, canvasStackTo } from '../nodes/animAnchors';\nimport { buildPdfFromJpegs, pngDataUrlToJpeg } from '../utils/pdfExport';\nimport { downloadDataURL, safeFileBaseName } from './useFlowImportExport';\nimport type { PixiApp } from '../canvas/PixiApp';\n\n/** Await the next N animation frames (lets Pixi settle after a state change). */\nfunction nextFrames(n: number): Promise<void> {\n return new Promise((resolve) => {\n let left = n;\n const step = () => {\n if (--left <= 0) resolve();\n else requestAnimationFrame(step);\n };\n requestAnimationFrame(step);\n });\n}\n\n/**\n * Exports the document's animation anchors as a single PDF. Each page is cropped\n * exactly to an anchor's world rectangle (not the whole viewport), so the PDF\n * contains only the framed slide content. Pages follow the global anchor order,\n * navigating across sub-canvases as needed.\n *\n * Returns a callback; it no-ops (returns false) when there are no anchors.\n */\nexport function usePdfExport(pixiRef: React.RefObject<PixiApp | null>) {\n const { doc, setDocTransient } = useFlowContext();\n const { showAnchors, setShowAnchors } = useFlowUI();\n\n return useCallback(async (): Promise<boolean> => {\n const slides = getDocumentSlides(doc);\n if (slides.length === 0) return false;\n\n const pixi = pixiRef.current;\n if (!pixi || pixi.destroyed) return false;\n\n // Snapshot state we temporarily change, so we can restore it afterward.\n const originalStack = doc.stack.slice();\n const anchorsWereVisible = showAnchors;\n\n // Hide anchor frames so they don't appear in the exported pages. The region\n // capture drives the world transform directly and restores it each time, so\n // the interactive viewport is untouched.\n setShowAnchors(false);\n await nextFrames(2);\n\n // Render slides at a crisp scale so text/shapes stay sharp in the PDF.\n const CAPTURE_SCALE = 2;\n const pages: Awaited<ReturnType<typeof pngDataUrlToJpeg>>[] = [];\n try {\n for (const slide of slides) {\n // Switch to the slide's canvas if it lives on a nested sub-canvas.\n // Transient updates: slide paging must not pollute the undo history.\n const stack = canvasStackTo(doc, slide.canvasId);\n if (stack) {\n setDocTransient((prev) => ({ ...prev, stack }));\n // Canvas swap needs a couple frames to re-render the new node set.\n await nextFrames(3);\n }\n\n const r = anchorFocusRect(slide.node);\n const shot = await pixi.exportRegionPNG(r, {\n scale: CAPTURE_SCALE,\n backgroundColor: 0xffffff,\n });\n if (shot) pages.push(await pngDataUrlToJpeg(shot.dataUrl));\n }\n } finally {\n // Restore prior editor state regardless of success/failure.\n setShowAnchors(anchorsWereVisible);\n setDocTransient((prev) => ({ ...prev, stack: originalStack }));\n }\n\n if (pages.length === 0) return false;\n const blob = buildPdfFromJpegs(pages);\n const url = URL.createObjectURL(blob);\n downloadDataURL(url, `${safeFileBaseName(doc.title)}.pdf`);\n URL.revokeObjectURL(url);\n return true;\n }, [doc, setDocTransient, showAnchors, setShowAnchors, pixiRef]);\n}\n"],"mappings":";;;;;;;AASA,SAASA,EAAW,GAA0B;CAC5C,OAAO,IAAI,SAAS,MAAY;EAC9B,IAAIC,IAAOC,GACLC,UAAa;GACjB,AAAI,EAAEF,KAAQ,IAAGG,EAAQ,IACpB,sBAAsBD,CAAI;EACjC;EACA,sBAAsBA,CAAI;CAC5B,CAAC;AACH;AAUA,SAAgBE,EAAa,GAA0C;CACrE,IAAM,EAAE,QAAK,uBAAoBC,EAAe,GAC1C,EAAE,gBAAa,sBAAmBC,EAAU;CAElD,OAAOC,EAAY,YAA8B;EAC/C,IAAMC,IAASC,EAAkBC,CAAG;EACpC,IAAIF,EAAO,WAAW,GAAG,OAAO;EAEhC,IAAMG,IAAOC,EAAQ;EACrB,IAAI,CAACD,KAAQA,EAAK,WAAW,OAAO;EAGpC,IAAME,IAAgBH,EAAI,MAAM,MAAM,GAChCI,IAAqBC;EAM3B,AADAC,EAAe,EAAK,GACpB,MAAMjB,EAAW,CAAC;EAGlB,IACMkB,IAAwD,CAAC;EAC/D,IAAI;GACF,KAAK,IAAMC,KAASV,GAAQ;IAG1B,IAAMW,IAAQC,EAAcV,GAAKQ,EAAM,QAAQ;IAC/C,AAAIC,MACFE,GAAiB,OAAU;KAAE,GAAGC;KAAM;IAAM,EAAE,GAE9C,MAAMvB,EAAW,CAAC;IAGpB,IAAMwB,IAAIC,EAAgBN,EAAM,IAAI,GAC9BO,IAAO,MAAMd,EAAK,gBAAgBY,GAAG;KACzC,OAAO;KACP,iBAAiB;IACnB,CAAC;IACD,AAAIE,KAAMR,EAAM,KAAK,MAAMS,EAAiBD,EAAK,OAAO,CAAC;GAC3D;EACF,UAAU;GAGR,AADAT,EAAeF,CAAkB,GACjCO,GAAiB,OAAU;IAAE,GAAGC;IAAM,OAAOT;GAAc,EAAE;EAC/D;EAEA,IAAII,EAAM,WAAW,GAAG,OAAO;EAC/B,IAAMU,IAAOC,EAAkBX,CAAK,GAC9BY,IAAM,IAAI,gBAAgBF,CAAI;EAGpC,OAFAG,EAAgBD,GAAK,GAAGE,EAAiBrB,EAAI,KAAK,EAAE,KAAK,GACzD,IAAI,gBAAgBmB,CAAG,GAChB;CACT,GAAG;EAACnB;EAAKW;EAAiBN;EAAaC;EAAgBJ;CAAO,CAAC;AACjE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePixiApp.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/usePixiApp.ts"],"sourcesContent":["import { useRef, useEffect, useCallback } from 'react';\nimport { PixiApp } from '../canvas/PixiApp';\nimport { resolveFlowThemeColors, refreshThemePalette, type FlowThemeColors } from '../utils/themeColor';\n\n/**\n * Manages PixiJS Application lifecycle: creates on mount, destroys on unmount,\n * handles container resize.\n *\n * The canvas backdrop follows the active DishUI theme (`--color-base-200`). We\n * resolve the theme color from the container's inherited CSS scope at init and\n * again whenever the ancestor `data-theme` attribute changes.\n */\nexport function usePixiApp(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onThemeColors?: (colors: FlowThemeColors) => void,\n) {\n const pixiRef = useRef<PixiApp | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const readyRef = useRef(false);\n const onThemeColorsRef = useRef(onThemeColors);\n onThemeColorsRef.current = onThemeColors;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n // Touch devices: the canvas owns all touch gestures (drag/pinch/pan); the\n // browser must not scroll or fire pointercancel mid-gesture.\n canvas.style.touchAction = 'none';\n container.appendChild(canvas);\n canvasRef.current = canvas;\n\n const pixi = new PixiApp();\n pixiRef.current = pixi;\n\n const rect = container.getBoundingClientRect();\n refreshThemePalette(container);\n const themeColors = resolveFlowThemeColors(container);\n pixi.init({\n canvas,\n width: rect.width,\n height: rect.height,\n backgroundColor: themeColors.background,\n }).then(() => {\n if (!cancelled) {\n readyRef.current = true;\n onThemeColorsRef.current?.(themeColors);\n }\n });\n\n const ro = new ResizeObserver((entries) => {\n if (pixi.destroyed) return;\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n if (width > 0 && height > 0) {\n pixi.resize(width, height);\n }\n }\n });\n ro.observe(container);\n\n // React to DishUI theme switches (the theme is applied via a `data-theme`\n // attribute on an ancestor element). Re-resolve the themed colors and push\n // them to the canvas + subscriber.\n const applyTheme = () => {\n if (pixi.destroyed) return;\n refreshThemePalette(container);\n const colors = resolveFlowThemeColors(container);\n pixi.setBackgroundColor(colors.background);\n onThemeColorsRef.current?.(colors);\n };\n const themeObserver = new MutationObserver(applyTheme);\n themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n // Also watch the closest themed ancestor, if any.\n const themedAncestor = container.closest('[data-theme]');\n if (themedAncestor) {\n themeObserver.observe(themedAncestor, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n }\n\n return () => {\n cancelled = true;\n ro.disconnect();\n themeObserver.disconnect();\n pixi.destroy();\n pixiRef.current = null;\n readyRef.current = false;\n if (canvas.parentNode) canvas.parentNode.removeChild(canvas);\n };\n }, [containerRef]);\n\n return { pixiRef, canvasRef, readyRef };\n}\n"],"mappings":";;;;AAYA,
|
|
1
|
+
{"version":3,"file":"usePixiApp.js","names":["usePixiApp","pixiRef","useRef","canvasRef","readyRef","onThemeColorsRef","onThemeColors","useEffect","container","containerRef","cancelled","canvas","pixi","PixiApp","rect","themeColors","resolveFlowThemeColors","ro","entry","entries","width","height","themeObserver","colors","themedAncestor"],"sources":["../../../../../src/components/pro/Flow/hooks/usePixiApp.ts"],"sourcesContent":["import { useRef, useEffect, useCallback } from 'react';\nimport { PixiApp } from '../canvas/PixiApp';\nimport { resolveFlowThemeColors, refreshThemePalette, type FlowThemeColors } from '../utils/themeColor';\n\n/**\n * Manages PixiJS Application lifecycle: creates on mount, destroys on unmount,\n * handles container resize.\n *\n * The canvas backdrop follows the active DishUI theme (`--color-base-200`). We\n * resolve the theme color from the container's inherited CSS scope at init and\n * again whenever the ancestor `data-theme` attribute changes.\n */\nexport function usePixiApp(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onThemeColors?: (colors: FlowThemeColors) => void,\n) {\n const pixiRef = useRef<PixiApp | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const readyRef = useRef(false);\n const onThemeColorsRef = useRef(onThemeColors);\n onThemeColorsRef.current = onThemeColors;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n // Touch devices: the canvas owns all touch gestures (drag/pinch/pan); the\n // browser must not scroll or fire pointercancel mid-gesture.\n canvas.style.touchAction = 'none';\n container.appendChild(canvas);\n canvasRef.current = canvas;\n\n const pixi = new PixiApp();\n pixiRef.current = pixi;\n\n const rect = container.getBoundingClientRect();\n refreshThemePalette(container);\n const themeColors = resolveFlowThemeColors(container);\n pixi.init({\n canvas,\n width: rect.width,\n height: rect.height,\n backgroundColor: themeColors.background,\n }).then(() => {\n if (!cancelled) {\n readyRef.current = true;\n onThemeColorsRef.current?.(themeColors);\n }\n });\n\n const ro = new ResizeObserver((entries) => {\n if (pixi.destroyed) return;\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n if (width > 0 && height > 0) {\n pixi.resize(width, height);\n }\n }\n });\n ro.observe(container);\n\n // React to DishUI theme switches (the theme is applied via a `data-theme`\n // attribute on an ancestor element). Re-resolve the themed colors and push\n // them to the canvas + subscriber.\n const applyTheme = () => {\n if (pixi.destroyed) return;\n refreshThemePalette(container);\n const colors = resolveFlowThemeColors(container);\n pixi.setBackgroundColor(colors.background);\n onThemeColorsRef.current?.(colors);\n };\n const themeObserver = new MutationObserver(applyTheme);\n themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n // Also watch the closest themed ancestor, if any.\n const themedAncestor = container.closest('[data-theme]');\n if (themedAncestor) {\n themeObserver.observe(themedAncestor, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n }\n\n return () => {\n cancelled = true;\n ro.disconnect();\n themeObserver.disconnect();\n pixi.destroy();\n pixiRef.current = null;\n readyRef.current = false;\n if (canvas.parentNode) canvas.parentNode.removeChild(canvas);\n };\n }, [containerRef]);\n\n return { pixiRef, canvasRef, readyRef };\n}\n"],"mappings":";;;;AAYA,SAAgBA,EACd,GACA,GACA;CACA,IAAMC,IAAUC,EAAuB,IAAI,GACrCC,IAAYD,EAAiC,IAAI,GACjDE,IAAWF,EAAO,EAAK,GACvBG,IAAmBH,EAAOI,CAAa;CAmF7C,OAlFA,EAAiB,UAAUA,GAE3BC,QAAgB;EACd,IAAMC,IAAYC,EAAa;EAC/B,IAAI,CAACD,GAAW;EAEhB,IAAIE,IAAY,IAEVC,IAAS,SAAS,cAAc,QAAQ;EAQ9C,AAPA,EAAO,MAAM,UAAU,SACvB,EAAO,MAAM,QAAQ,QACrB,EAAO,MAAM,SAAS,QAGtB,EAAO,MAAM,cAAc,QAC3BH,EAAU,YAAYG,CAAM,GAC5B,EAAU,UAAUA;EAEpB,IAAMC,IAAO,IAAIC,EAAQ;EACzB,EAAQ,UAAUD;EAElB,IAAME,IAAON,EAAU,sBAAsB;EAC7C,EAAoBA,CAAS;EAC7B,IAAMO,IAAcC,EAAuBR,CAAS;EACpD,EAAK,KAAK;GACR;GACA,OAAOM,EAAK;GACZ,QAAQA,EAAK;GACb,iBAAiBC,EAAY;EAC/B,CAAC,CAAC,CAAC,WAAW;GACZ,AAAKL,MACH,EAAS,UAAU,IACnBL,EAAiB,UAAUU,CAAW;EAE1C,CAAC;EAED,IAAME,IAAK,IAAI,gBAAgB,MAAY;GACzC,IAAI,GAAK,WACT,KAAK,IAAMC,KAASC,GAAS;IAC3B,IAAM,EAAE,UAAO,cAAWD,EAAM;IAChC,AAAIE,IAAQ,KAAKC,IAAS,KACxBT,EAAK,OAAOQ,GAAOC,CAAM;GAE7B;EACF,CAAC;EACD,EAAG,QAAQb,CAAS;EAYpB,IAAMc,IAAgB,IAAI,uBAPD;GACvB,IAAIV,EAAK,WAAW;GACpB,EAAoBJ,CAAS;GAC7B,IAAMe,IAASP,EAAuBR,CAAS;GAE/C,AADAI,EAAK,mBAAmBW,EAAO,UAAU,GACzClB,EAAiB,UAAUkB,CAAM;EACnC,CACqD;EACrD,EAAc,QAAQ,SAAS,iBAAiB;GAC9C,YAAY;GACZ,iBAAiB,CAAC,cAAc,OAAO;EACzC,CAAC;EAED,IAAMC,IAAiBhB,EAAU,QAAQ,cAAc;EAQvD,OAPIgB,KACFF,EAAc,QAAQE,GAAgB;GACpC,YAAY;GACZ,iBAAiB,CAAC,cAAc,OAAO;EACzC,CAAC,SAGU;GAOX,AANA,IAAY,IACZP,EAAG,WAAW,GACdK,EAAc,WAAW,GACzBV,EAAK,QAAQ,GACb,EAAQ,UAAU,MAClB,EAAS,UAAU,IACfD,EAAO,cAAYA,EAAO,WAAW,YAAYA,CAAM;EAC7D;CACF,GAAG,CAACF,CAAY,CAAC,GAEV;EAAE;EAAS;EAAW;CAAS;AACxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResizablePanel.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useResizablePanel.ts"],"sourcesContent":["import { useState, useRef, useCallback, type PointerEvent as ReactPointerEvent } from 'react';\n\nexport type ResizeEdge = 'left' | 'right';\n\n/**\n * Enables horizontal width resizing for an overlay panel by dragging its edge.\n * Returns a width override (null = use CSS default), and props to spread onto\n * a resize-handle element placed on the panel's resize edge.\n */\nexport function useResizablePanel(edge: ResizeEdge, defaultWidth: number, minWidth = 120, maxWidth = 600) {\n const [width, setWidth] = useState<number | null>(null);\n const dragState = useRef<{ startX: number; startWidth: number } | null>(null);\n\n const onPointerDown = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const panel = (e.currentTarget as HTMLElement).closest('[data-flow-panel]') as HTMLElement | null;\n const startWidth = panel ? panel.getBoundingClientRect().width : (width ?? defaultWidth);\n dragState.current = { startX: e.clientX, startWidth };\n }, [width, defaultWidth]);\n\n const onPointerMove = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n if (!dragState.current) return;\n const dx = e.clientX - dragState.current.startX;\n const dir = edge === 'right' ? 1 : -1;\n const newWidth = Math.max(minWidth, Math.min(maxWidth, dragState.current.startWidth + dx * dir));\n setWidth(newWidth);\n }, [edge, minWidth, maxWidth]);\n\n const onPointerUp = useCallback(() => {\n dragState.current = null;\n }, []);\n\n return {\n panelWidth: width ?? defaultWidth,\n resizeHandleProps: { onPointerDown, onPointerMove, onPointerUp },\n };\n}\n"],"mappings":";;AASA,
|
|
1
|
+
{"version":3,"file":"useResizablePanel.js","names":["useResizablePanel","width","setWidth","useState","dragState","useRef","onPointerDown","useCallback","panel","startWidth","defaultWidth","onPointerMove","dx","dir","edge","newWidth","minWidth","maxWidth","onPointerUp"],"sources":["../../../../../src/components/pro/Flow/hooks/useResizablePanel.ts"],"sourcesContent":["import { useState, useRef, useCallback, type PointerEvent as ReactPointerEvent } from 'react';\n\nexport type ResizeEdge = 'left' | 'right';\n\n/**\n * Enables horizontal width resizing for an overlay panel by dragging its edge.\n * Returns a width override (null = use CSS default), and props to spread onto\n * a resize-handle element placed on the panel's resize edge.\n */\nexport function useResizablePanel(edge: ResizeEdge, defaultWidth: number, minWidth = 120, maxWidth = 600) {\n const [width, setWidth] = useState<number | null>(null);\n const dragState = useRef<{ startX: number; startWidth: number } | null>(null);\n\n const onPointerDown = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const panel = (e.currentTarget as HTMLElement).closest('[data-flow-panel]') as HTMLElement | null;\n const startWidth = panel ? panel.getBoundingClientRect().width : (width ?? defaultWidth);\n dragState.current = { startX: e.clientX, startWidth };\n }, [width, defaultWidth]);\n\n const onPointerMove = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n if (!dragState.current) return;\n const dx = e.clientX - dragState.current.startX;\n const dir = edge === 'right' ? 1 : -1;\n const newWidth = Math.max(minWidth, Math.min(maxWidth, dragState.current.startWidth + dx * dir));\n setWidth(newWidth);\n }, [edge, minWidth, maxWidth]);\n\n const onPointerUp = useCallback(() => {\n dragState.current = null;\n }, []);\n\n return {\n panelWidth: width ?? defaultWidth,\n resizeHandleProps: { onPointerDown, onPointerMove, onPointerUp },\n };\n}\n"],"mappings":";;AASA,SAAgBA,EAAkB,GAAkB,GAAsB,IAAW,KAAK,IAAW,KAAK;CACxG,IAAM,CAACC,GAAOC,KAAYC,EAAwB,IAAI,GAChDC,IAAYC,EAAsD,IAAI,GAEtEC,IAAgBC,GAAa,MAAsC;EAGvE,AAFA,EAAE,eAAe,GACjB,EAAE,gBAAgB,GAClB,EAAG,OAAuB,kBAAkB,EAAE,SAAS;EACvD,IAAMC,IAAS,EAAE,cAA8B,QAAQ,mBAAmB,GACpEC,IAAaD,IAAQA,EAAM,sBAAsB,CAAC,CAAC,QAASP,KAASS;EAC3E,EAAU,UAAU;GAAE,QAAQ,EAAE;GAAS;EAAW;CACtD,GAAG,CAACT,GAAOS,CAAY,CAAC,GAElBC,IAAgBJ,GAAa,MAAsC;EACvE,IAAI,CAACH,EAAU,SAAS;EACxB,IAAMQ,IAAK,EAAE,UAAUR,EAAU,QAAQ,QACnCS,IAAMC,MAAS,UAAU,IAAI,IAC7BC,IAAW,KAAK,IAAIC,GAAU,KAAK,IAAIC,GAAUb,EAAU,QAAQ,aAAaQ,IAAKC,CAAG,CAAC;EAC/F,EAASE,CAAQ;CACnB,GAAG;EAACD;EAAME;EAAUC;CAAQ,CAAC,GAEvBC,IAAcX,QAAkB;EACpC,EAAU,UAAU;CACtB,GAAG,CAAC,CAAC;CAEL,OAAO;EACL,YAAYN,KAASS;EACrB,mBAAmB;GAAE;GAAe;GAAe;EAAY;CACjE;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSelection.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useSelection.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { SelectionState } from '../types';\n\nconst EMPTY_SET = new Set<string>();\n\nexport function useSelection() {\n const { selection, setSelection, currentCanvas } = useFlowContext();\n\n const selectNode = useCallback(\n (id: string, additive = false) => {\n setSelection(prev => {\n const nodeIds = new Set(additive ? prev.nodeIds : []);\n nodeIds.add(id);\n return { nodeIds, edgeIds: additive ? prev.edgeIds : EMPTY_SET, lineIds: additive ? prev.lineIds : EMPTY_SET };\n });\n },\n [setSelection],\n );\n\n const selectEdge = useCallback(\n (id: string, additive = false) => {\n setSelection(prev => {\n const edgeIds = new Set(additive ? prev.edgeIds : []);\n edgeIds.add(id);\n return { nodeIds: additive ? prev.nodeIds : EMPTY_SET, edgeIds, lineIds: additive ? prev.lineIds : EMPTY_SET };\n });\n },\n [setSelection],\n );\n\n const selectLine = useCallback(\n (id: string, additive = false) => {\n setSelection(prev => {\n const lineIds = new Set(additive ? prev.lineIds : []);\n lineIds.add(id);\n return { nodeIds: additive ? prev.nodeIds : EMPTY_SET, edgeIds: additive ? prev.edgeIds : EMPTY_SET, lineIds };\n });\n },\n [setSelection],\n );\n\n const selectMultipleNodes = useCallback(\n (ids: string[]) => {\n setSelection({ nodeIds: new Set(ids), edgeIds: EMPTY_SET, lineIds: EMPTY_SET });\n },\n [setSelection],\n );\n\n const clearSelection = useCallback(() => {\n setSelection({ nodeIds: EMPTY_SET, edgeIds: EMPTY_SET, lineIds: EMPTY_SET });\n }, [setSelection]);\n\n const toggleNode = useCallback(\n (id: string) => {\n setSelection(prev => {\n const nodeIds = new Set(prev.nodeIds);\n if (nodeIds.has(id)) nodeIds.delete(id);\n else nodeIds.add(id);\n return { ...prev, nodeIds };\n });\n },\n [setSelection],\n );\n\n const isNodeSelected = useCallback(\n (id: string) => selection.nodeIds.has(id),\n [selection.nodeIds],\n );\n\n const isEdgeSelected = useCallback(\n (id: string) => selection.edgeIds.has(id),\n [selection.edgeIds],\n );\n\n const isLineSelected = useCallback(\n (id: string) => selection.lineIds.has(id),\n [selection.lineIds],\n );\n\n const selectedNodes = currentCanvas.nodes.filter(n => selection.nodeIds.has(n.id));\n const selectedEdges = currentCanvas.edges.filter(e => selection.edgeIds.has(e.id));\n const selectedLines = currentCanvas.lines.filter(l => selection.lineIds.has(l.id));\n\n const hasSelection = selection.nodeIds.size > 0 || selection.edgeIds.size > 0 || selection.lineIds.size > 0;\n\n return {\n selection,\n selectNode,\n selectEdge,\n selectLine,\n selectMultipleNodes,\n clearSelection,\n toggleNode,\n isNodeSelected,\n isEdgeSelected,\n isLineSelected,\n selectedNodes,\n selectedEdges,\n selectedLines,\n hasSelection,\n };\n}\n"],"mappings":";;;AAIA,
|
|
1
|
+
{"version":3,"file":"useSelection.js","names":["EMPTY_SET","useSelection","useFlowContext","useCallback","nodeIds","additive","prev","id","setSelection","edgeIds","lineIds","ids","selection","currentCanvas","n","l"],"sources":["../../../../../src/components/pro/Flow/hooks/useSelection.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { SelectionState } from '../types';\n\nconst EMPTY_SET = new Set<string>();\n\nexport function useSelection() {\n const { selection, setSelection, currentCanvas } = useFlowContext();\n\n const selectNode = useCallback(\n (id: string, additive = false) => {\n setSelection(prev => {\n const nodeIds = new Set(additive ? prev.nodeIds : []);\n nodeIds.add(id);\n return { nodeIds, edgeIds: additive ? prev.edgeIds : EMPTY_SET, lineIds: additive ? prev.lineIds : EMPTY_SET };\n });\n },\n [setSelection],\n );\n\n const selectEdge = useCallback(\n (id: string, additive = false) => {\n setSelection(prev => {\n const edgeIds = new Set(additive ? prev.edgeIds : []);\n edgeIds.add(id);\n return { nodeIds: additive ? prev.nodeIds : EMPTY_SET, edgeIds, lineIds: additive ? prev.lineIds : EMPTY_SET };\n });\n },\n [setSelection],\n );\n\n const selectLine = useCallback(\n (id: string, additive = false) => {\n setSelection(prev => {\n const lineIds = new Set(additive ? prev.lineIds : []);\n lineIds.add(id);\n return { nodeIds: additive ? prev.nodeIds : EMPTY_SET, edgeIds: additive ? prev.edgeIds : EMPTY_SET, lineIds };\n });\n },\n [setSelection],\n );\n\n const selectMultipleNodes = useCallback(\n (ids: string[]) => {\n setSelection({ nodeIds: new Set(ids), edgeIds: EMPTY_SET, lineIds: EMPTY_SET });\n },\n [setSelection],\n );\n\n const clearSelection = useCallback(() => {\n setSelection({ nodeIds: EMPTY_SET, edgeIds: EMPTY_SET, lineIds: EMPTY_SET });\n }, [setSelection]);\n\n const toggleNode = useCallback(\n (id: string) => {\n setSelection(prev => {\n const nodeIds = new Set(prev.nodeIds);\n if (nodeIds.has(id)) nodeIds.delete(id);\n else nodeIds.add(id);\n return { ...prev, nodeIds };\n });\n },\n [setSelection],\n );\n\n const isNodeSelected = useCallback(\n (id: string) => selection.nodeIds.has(id),\n [selection.nodeIds],\n );\n\n const isEdgeSelected = useCallback(\n (id: string) => selection.edgeIds.has(id),\n [selection.edgeIds],\n );\n\n const isLineSelected = useCallback(\n (id: string) => selection.lineIds.has(id),\n [selection.lineIds],\n );\n\n const selectedNodes = currentCanvas.nodes.filter(n => selection.nodeIds.has(n.id));\n const selectedEdges = currentCanvas.edges.filter(e => selection.edgeIds.has(e.id));\n const selectedLines = currentCanvas.lines.filter(l => selection.lineIds.has(l.id));\n\n const hasSelection = selection.nodeIds.size > 0 || selection.edgeIds.size > 0 || selection.lineIds.size > 0;\n\n return {\n selection,\n selectNode,\n selectEdge,\n selectLine,\n selectMultipleNodes,\n clearSelection,\n toggleNode,\n isNodeSelected,\n isEdgeSelected,\n isLineSelected,\n selectedNodes,\n selectedEdges,\n selectedLines,\n hasSelection,\n };\n}\n"],"mappings":";;;AAIA,IAAMA,oBAAY,IAAI,IAAY;AAElC,SAAgBC,IAAe;CAC7B,IAAM,EAAE,cAAW,iBAAc,qBAAkBC,EAAe;CA+ElE,OAAO;EACL;EACA,YA/EiBC,GAChB,GAAY,IAAW,OAAU;GAChC,GAAa,MAAQ;IACnB,IAAMC,IAAU,IAAI,IAAIC,IAAWC,EAAK,UAAU,CAAC,CAAC;IAEpD,OADAF,EAAQ,IAAIG,CAAE,GACP;KAAE;KAAS,SAASF,IAAWC,EAAK,UAAUN;KAAW,SAASK,IAAWC,EAAK,UAAUN;IAAU;GAC/G,CAAC;EACH,GACA,CAACQ,CAAY,CAuEb;EACA,YArEiBL,GAChB,GAAY,IAAW,OAAU;GAChC,GAAa,MAAQ;IACnB,IAAMM,IAAU,IAAI,IAAIJ,IAAWC,EAAK,UAAU,CAAC,CAAC;IAEpD,OADAG,EAAQ,IAAIF,CAAE,GACP;KAAE,SAASF,IAAWC,EAAK,UAAUN;KAAW;KAAS,SAASK,IAAWC,EAAK,UAAUN;IAAU;GAC/G,CAAC;EACH,GACA,CAACQ,CAAY,CA6Db;EACA,YA3DiBL,GAChB,GAAY,IAAW,OAAU;GAChC,GAAa,MAAQ;IACnB,IAAMO,IAAU,IAAI,IAAIL,IAAWC,EAAK,UAAU,CAAC,CAAC;IAEpD,OADAI,EAAQ,IAAIH,CAAE,GACP;KAAE,SAASF,IAAWC,EAAK,UAAUN;KAAW,SAASK,IAAWC,EAAK,UAAUN;KAAW;IAAQ;GAC/G,CAAC;EACH,GACA,CAACQ,CAAY,CAmDb;EACA,qBAjD0BL,GACzB,MAAkB;GACjB,EAAa;IAAE,SAAS,IAAI,IAAIQ,CAAG;IAAG,SAASX;IAAW,SAASA;GAAU,CAAC;EAChF,GACA,CAACQ,CAAY,CA6Cb;EACA,gBA3CqBL,QAAkB;GACvC,EAAa;IAAE,SAASH;IAAW,SAASA;IAAW,SAASA;GAAU,CAAC;EAC7E,GAAG,CAACQ,CAAY,CAyCd;EACA,YAxCiBL,GAChB,MAAe;GACd,GAAa,MAAQ;IACnB,IAAMC,IAAU,IAAI,IAAIE,EAAK,OAAO;IAGpC,OAFIF,EAAQ,IAAIG,CAAE,IAAGH,EAAQ,OAAOG,CAAE,IACjCH,EAAQ,IAAIG,CAAE,GACZ;KAAE,GAAGD;KAAM;IAAQ;GAC5B,CAAC;EACH,GACA,CAACE,CAAY,CA+Bb;EACA,gBA7BqBL,GACpB,MAAeS,EAAU,QAAQ,IAAIL,CAAE,GACxC,CAACK,EAAU,OAAO,CA2BlB;EACA,gBAzBqBT,GACpB,MAAeS,EAAU,QAAQ,IAAIL,CAAE,GACxC,CAACK,EAAU,OAAO,CAuBlB;EACA,gBArBqBT,GACpB,MAAeS,EAAU,QAAQ,IAAIL,CAAE,GACxC,CAACK,EAAU,OAAO,CAmBlB;EACA,eAjBoBC,EAAc,MAAM,QAAO,MAAKD,EAAU,QAAQ,IAAIE,EAAE,EAAE,CAiB9E;EACA,eAjBoBD,EAAc,MAAM,QAAO,MAAKD,EAAU,QAAQ,IAAI,EAAE,EAAE,CAiB9E;EACA,eAjBoBC,EAAc,MAAM,QAAO,MAAKD,EAAU,QAAQ,IAAIG,EAAE,EAAE,CAiB9E;EACA,cAhBmBH,EAAU,QAAQ,OAAO,KAAKA,EAAU,QAAQ,OAAO,KAAKA,EAAU,QAAQ,OAAO;CAiB1G;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUndoRedo.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useUndoRedo.ts"],"sourcesContent":["import { useRef, useCallback, useLayoutEffect, useState } from 'react';\nimport type { FlowDocument } from '../types';\n\nconst MAX_HISTORY = 50;\nconst COALESCE_MS = 300;\n\n/**\n * Time-coalesced undo/redo for FlowDocument.\n * Commands capture history outside React updaters, which React may replay.\n */\nexport function useUndoRedo(\n doc: FlowDocument,\n setDoc: React.Dispatch<React.SetStateAction<FlowDocument>>,\n) {\n const historyRef = useRef<FlowDocument[]>([]);\n const futureRef = useRef<FlowDocument[]>([]);\n const lastPushRef = useRef(-Infinity);\n const currentRef = useRef(doc);\n const [availability, setAvailability] = useState({ canUndo: false, canRedo: false });\n\n // Track externally committed documents too, without writing refs in render.\n useLayoutEffect(() => {\n currentRef.current = doc;\n }, [doc]);\n\n const commit = useCallback((next: FlowDocument) => {\n // Advance immediately so commands in one React batch see preceding commands.\n currentRef.current = next;\n setDoc(next);\n setAvailability({\n canUndo: historyRef.current.length > 0,\n canRedo: futureRef.current.length > 0,\n });\n }, [setDoc]);\n\n const wrappedSetDoc: React.Dispatch<React.SetStateAction<FlowDocument>> = useCallback(\n (action) => {\n const prev = currentRef.current;\n const next = typeof action === 'function' ? action(prev) : action;\n if (next === prev) return;\n\n const now = Date.now();\n // A coalesced edit retains the snapshot from before the first edit.\n if (now - lastPushRef.current >= COALESCE_MS || historyRef.current.length === 0) {\n historyRef.current.push(prev);\n if (historyRef.current.length > MAX_HISTORY) historyRef.current.shift();\n }\n lastPushRef.current = now;\n futureRef.current = [];\n commit(next);\n },\n [commit],\n );\n\n const undo = useCallback(() => {\n if (historyRef.current.length === 0) return;\n const prev = historyRef.current.pop()!;\n futureRef.current.push(currentRef.current);\n lastPushRef.current = -Infinity;\n commit(prev);\n }, [commit]);\n\n const redo = useCallback(() => {\n if (futureRef.current.length === 0) return;\n const next = futureRef.current.pop()!;\n historyRef.current.push(currentRef.current);\n lastPushRef.current = -Infinity;\n commit(next);\n }, [commit]);\n\n // Navigation-style updates (e.g. PDF export paging through sub-canvases):\n // commit the document without recording history or clearing redo, so\n // temporary canvas switches stay transparent to undo.\n const setDocTransient: React.Dispatch<React.SetStateAction<FlowDocument>> = useCallback(\n (action) => {\n const prev = currentRef.current;\n const next = typeof action === 'function' ? action(prev) : action;\n if (next === prev) return;\n commit(next);\n },\n [commit],\n );\n\n return { wrappedSetDoc, setDocTransient, undo, redo, ...availability };\n}\n"],"mappings":";;AAGA,
|
|
1
|
+
{"version":3,"file":"useUndoRedo.js","names":["MAX_HISTORY","COALESCE_MS","useUndoRedo","historyRef","useRef","futureRef","lastPushRef","currentRef","doc","availability","setAvailability","useState","commit","useCallback","next","setDoc","wrappedSetDoc","prev","action","now","undo","redo"],"sources":["../../../../../src/components/pro/Flow/hooks/useUndoRedo.ts"],"sourcesContent":["import { useRef, useCallback, useLayoutEffect, useState } from 'react';\nimport type { FlowDocument } from '../types';\n\nconst MAX_HISTORY = 50;\nconst COALESCE_MS = 300;\n\n/**\n * Time-coalesced undo/redo for FlowDocument.\n * Commands capture history outside React updaters, which React may replay.\n */\nexport function useUndoRedo(\n doc: FlowDocument,\n setDoc: React.Dispatch<React.SetStateAction<FlowDocument>>,\n) {\n const historyRef = useRef<FlowDocument[]>([]);\n const futureRef = useRef<FlowDocument[]>([]);\n const lastPushRef = useRef(-Infinity);\n const currentRef = useRef(doc);\n const [availability, setAvailability] = useState({ canUndo: false, canRedo: false });\n\n // Track externally committed documents too, without writing refs in render.\n useLayoutEffect(() => {\n currentRef.current = doc;\n }, [doc]);\n\n const commit = useCallback((next: FlowDocument) => {\n // Advance immediately so commands in one React batch see preceding commands.\n currentRef.current = next;\n setDoc(next);\n setAvailability({\n canUndo: historyRef.current.length > 0,\n canRedo: futureRef.current.length > 0,\n });\n }, [setDoc]);\n\n const wrappedSetDoc: React.Dispatch<React.SetStateAction<FlowDocument>> = useCallback(\n (action) => {\n const prev = currentRef.current;\n const next = typeof action === 'function' ? action(prev) : action;\n if (next === prev) return;\n\n const now = Date.now();\n // A coalesced edit retains the snapshot from before the first edit.\n if (now - lastPushRef.current >= COALESCE_MS || historyRef.current.length === 0) {\n historyRef.current.push(prev);\n if (historyRef.current.length > MAX_HISTORY) historyRef.current.shift();\n }\n lastPushRef.current = now;\n futureRef.current = [];\n commit(next);\n },\n [commit],\n );\n\n const undo = useCallback(() => {\n if (historyRef.current.length === 0) return;\n const prev = historyRef.current.pop()!;\n futureRef.current.push(currentRef.current);\n lastPushRef.current = -Infinity;\n commit(prev);\n }, [commit]);\n\n const redo = useCallback(() => {\n if (futureRef.current.length === 0) return;\n const next = futureRef.current.pop()!;\n historyRef.current.push(currentRef.current);\n lastPushRef.current = -Infinity;\n commit(next);\n }, [commit]);\n\n // Navigation-style updates (e.g. PDF export paging through sub-canvases):\n // commit the document without recording history or clearing redo, so\n // temporary canvas switches stay transparent to undo.\n const setDocTransient: React.Dispatch<React.SetStateAction<FlowDocument>> = useCallback(\n (action) => {\n const prev = currentRef.current;\n const next = typeof action === 'function' ? action(prev) : action;\n if (next === prev) return;\n commit(next);\n },\n [commit],\n );\n\n return { wrappedSetDoc, setDocTransient, undo, redo, ...availability };\n}\n"],"mappings":";;AAGA,IAAMA,IAAc,IACdC,IAAc;AAMpB,SAAgBC,EACd,GACA,GACA;CACA,IAAMC,IAAaC,EAAuB,CAAC,CAAC,GACtCC,IAAYD,EAAuB,CAAC,CAAC,GACrCE,IAAcF,EAAO,SAAS,GAC9BG,IAAaH,EAAOI,CAAG,GACvB,CAACC,GAAcC,KAAmBC,EAAS;EAAE,SAAS;EAAO,SAAS;CAAM,CAAC;CAGnF,QAAsB;EACpB,EAAW,UAAUH;CACvB,GAAG,CAACA,CAAG,CAAC;CAER,IAAMI,IAASC,GAAa,MAAuB;EAIjD,AAFA,EAAW,UAAUC,GACrBC,EAAOD,CAAI,GACXJ,EAAgB;GACd,SAASP,EAAW,QAAQ,SAAS;GACrC,SAASE,EAAU,QAAQ,SAAS;EACtC,CAAC;CACH,GAAG,CAACU,CAAM,CAAC,GAELC,IAAoEH,GACvE,MAAW;EACV,IAAMI,IAAOV,EAAW,SAClBO,IAAO,OAAOI,KAAW,aAAaA,EAAOD,CAAI,IAAIC;EAC3D,IAAIJ,MAASG,GAAM;EAEnB,IAAME,IAAM,KAAK,IAAI;EAQrB,CANIA,IAAMb,EAAY,WAAWL,KAAeE,EAAW,QAAQ,WAAW,OAC5EA,EAAW,QAAQ,KAAKc,CAAI,GACxBd,EAAW,QAAQ,SAASH,KAAaG,EAAW,QAAQ,MAAM,IAExE,EAAY,UAAUgB,GACtB,EAAU,UAAU,CAAC,GACrBP,EAAOE,CAAI;CACb,GACA,CAACF,CAAM,CACT,GAEMQ,IAAOP,QAAkB;EAC7B,IAAIV,EAAW,QAAQ,WAAW,GAAG;EACrC,IAAMc,IAAOd,EAAW,QAAQ,IAAI;EAGpC,AAFAE,EAAU,QAAQ,KAAKE,EAAW,OAAO,GACzC,EAAY,UAAU,WACtBK,EAAOK,CAAI;CACb,GAAG,CAACL,CAAM,CAAC,GAELS,IAAOR,QAAkB;EAC7B,IAAIR,EAAU,QAAQ,WAAW,GAAG;EACpC,IAAMS,IAAOT,EAAU,QAAQ,IAAI;EAGnC,AAFAF,EAAW,QAAQ,KAAKI,EAAW,OAAO,GAC1C,EAAY,UAAU,WACtBK,EAAOE,CAAI;CACb,GAAG,CAACF,CAAM,CAAC;CAeX,OAAO;EAAE;EAAe,iBAVoDC,GACzE,MAAW;GACV,IAAMI,IAAOV,EAAW,SAClBO,IAAO,OAAOI,KAAW,aAAaA,EAAOD,CAAI,IAAIC;GAC3D,AAAIJ,MAASG,KACbL,EAAOE,CAAI;EACb,GACA,CAACF,CAAM,CAGe;EAAiB;EAAM;EAAM,GAAGH;CAAa;AACvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useViewport.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useViewport.ts"],"sourcesContent":["import { useRef, useEffect, useCallback } from 'react';\nimport { ViewportManager } from '../canvas/ViewportContainer';\nimport { PixiApp } from '../canvas/PixiApp';\nimport type { ViewportState } from '../types';\n\n/**\n * Connects ViewportManager to React state.\n */\nexport function useViewport(\n pixiRef: React.RefObject<PixiApp | null>,\n onViewportChange: (v: ViewportState) => void,\n) {\n const vmRef = useRef<ViewportManager | null>(null);\n\n useEffect(() => {\n const pixi = pixiRef.current;\n if (!pixi || pixi.destroyed) return;\n\n const vm = new ViewportManager(pixi.world, pixi.app.stage, onViewportChange);\n vmRef.current = vm;\n\n return () => {\n vm.destroy();\n vmRef.current = null;\n };\n }, [pixiRef.current, onViewportChange]);\n\n const setViewport = useCallback((state: Partial<ViewportState>) => {\n vmRef.current?.setState(state);\n }, []);\n\n const screenToWorld = useCallback((sx: number, sy: number) => {\n return vmRef.current?.screenToWorld(sx, sy) ?? { x: sx, y: sy };\n }, []);\n\n const worldToScreen = useCallback((wx: number, wy: number) => {\n return vmRef.current?.worldToScreen(wx, wy) ?? { x: wx, y: wy };\n }, []);\n\n return { vmRef, setViewport, screenToWorld, worldToScreen };\n}\n"],"mappings":";;;AAQA,
|
|
1
|
+
{"version":3,"file":"useViewport.js","names":["useViewport","vmRef","useRef","useEffect","pixi","pixiRef","vm","ViewportManager","onViewportChange","useCallback","state","sx","sy","wx","wy"],"sources":["../../../../../src/components/pro/Flow/hooks/useViewport.ts"],"sourcesContent":["import { useRef, useEffect, useCallback } from 'react';\nimport { ViewportManager } from '../canvas/ViewportContainer';\nimport { PixiApp } from '../canvas/PixiApp';\nimport type { ViewportState } from '../types';\n\n/**\n * Connects ViewportManager to React state.\n */\nexport function useViewport(\n pixiRef: React.RefObject<PixiApp | null>,\n onViewportChange: (v: ViewportState) => void,\n) {\n const vmRef = useRef<ViewportManager | null>(null);\n\n useEffect(() => {\n const pixi = pixiRef.current;\n if (!pixi || pixi.destroyed) return;\n\n const vm = new ViewportManager(pixi.world, pixi.app.stage, onViewportChange);\n vmRef.current = vm;\n\n return () => {\n vm.destroy();\n vmRef.current = null;\n };\n }, [pixiRef.current, onViewportChange]);\n\n const setViewport = useCallback((state: Partial<ViewportState>) => {\n vmRef.current?.setState(state);\n }, []);\n\n const screenToWorld = useCallback((sx: number, sy: number) => {\n return vmRef.current?.screenToWorld(sx, sy) ?? { x: sx, y: sy };\n }, []);\n\n const worldToScreen = useCallback((wx: number, wy: number) => {\n return vmRef.current?.worldToScreen(wx, wy) ?? { x: wx, y: wy };\n }, []);\n\n return { vmRef, setViewport, screenToWorld, worldToScreen };\n}\n"],"mappings":";;;AAQA,SAAgBA,EACd,GACA,GACA;CACA,IAAMC,IAAQC,EAA+B,IAAI;CA2BjD,OAzBAC,QAAgB;EACd,IAAMC,IAAOC,EAAQ;EACrB,IAAI,CAACD,KAAQA,EAAK,WAAW;EAE7B,IAAME,IAAK,IAAIC,EAAgBH,EAAK,OAAOA,EAAK,IAAI,OAAOI,CAAgB;EAG3E,OAFA,EAAM,UAAUF,SAEH;GAEX,AADAA,EAAG,QAAQ,GACX,EAAM,UAAU;EAClB;CACF,GAAG,CAACD,EAAQ,SAASG,CAAgB,CAAC,GAc/B;EAAE;EAAO,aAZIC,GAAa,MAAkC;GACjE,EAAM,SAAS,SAASC,CAAK;EAC/B,GAAG,CAAC,CAUY;EAAa,eARPD,GAAa,GAAY,MACtCR,EAAM,SAAS,cAAcU,GAAIC,CAAE,KAAK;GAAE,GAAGD;GAAI,GAAGC;EAAG,GAC7D,CAAC,CAMyB;EAAe,eAJtBH,GAAa,GAAY,MACtCR,EAAM,SAAS,cAAcY,GAAIC,CAAE,KAAK;GAAE,GAAGD;GAAI,GAAGC;EAAG,GAC7D,CAAC,CAEwC;CAAc;AAC5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FreehandTool.js","names":[],"sources":["../../../../../src/components/pro/Flow/lines/FreehandTool.ts"],"sourcesContent":["import { Graphics, Container, FederatedPointerEvent } from 'pixi.js';\nimport { simplifyPath, smoothCatmullRom, type Point } from './lineSmoothing';\nimport type { FlowLine } from '../types';\nimport { generateId } from '../nodes/nodeUtils';\nimport { resolveThemeColor } from '../utils/themeColor';\n\nexport interface FreehandToolCallbacks {\n /** Called when a freehand line is finished */\n onLineComplete: (line: FlowLine) => void;\n /** Convert screen coords to world coords */\n screenToWorld: (sx: number, sy: number) => { x: number; y: number };\n}\n\n/**\n * Captures pointer trail for freehand drawing, smooths and stores as FlowLine.\n */\nexport class FreehandTool {\n private stage: Container;\n private preview: Graphics;\n private callbacks: FreehandToolCallbacks;\n private rawPoints: Point[] = [];\n private drawing = false;\n private _active = false;\n\n constructor(stage: Container, world: Container, callbacks: FreehandToolCallbacks) {\n this.stage = stage;\n this.callbacks = callbacks;\n this.preview = new Graphics();\n this.preview.label = 'freehand-preview';\n this.preview.visible = false;\n world.addChild(this.preview);\n }\n\n get active(): boolean { return this._active; }\n\n activate(): void {\n this._active = true;\n this.stage.on('pointerdown', this._onDown, this);\n this.stage.on('globalpointermove', this._onMove, this);\n this.stage.on('pointerup', this._onUp, this);\n this.stage.on('pointerupoutside', this._onUp, this);\n }\n\n deactivate(): void {\n this._active = false;\n this.drawing = false;\n this.rawPoints = [];\n if (!this.preview.destroyed) {\n this.preview.clear();\n this.preview.visible = false;\n }\n this.stage.off('pointerdown', this._onDown, this);\n this.stage.off('globalpointermove', this._onMove, this);\n this.stage.off('pointerup', this._onUp, this);\n this.stage.off('pointerupoutside', this._onUp, this);\n }\n\n private _onDown = (e: FederatedPointerEvent): void => {\n if (e.button !== 0) return;\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n this.drawing = true;\n this.rawPoints = [world];\n this.preview.visible = true;\n this.preview.clear();\n };\n\n private _onMove = (e: FederatedPointerEvent): void => {\n if (!this.drawing) return;\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n this.rawPoints.push(world);\n\n // Draw preview\n this.preview.clear();\n if (this.rawPoints.length < 2) return;\n this.preview.moveTo(this.rawPoints[0].x, this.rawPoints[0].y);\n for (let i = 1; i < this.rawPoints.length; i++) {\n this.preview.lineTo(this.rawPoints[i].x, this.rawPoints[i].y);\n }\n this.preview.stroke({ color: resolveThemeColor('theme:base-content', '#374151'), width: 2, alpha: 0.6 });\n };\n\n private _onUp = (): void => {\n if (!this.drawing) return;\n this.drawing = false;\n this.preview.clear();\n this.preview.visible = false;\n\n if (this.rawPoints.length < 3) {\n this.rawPoints = [];\n return;\n }\n\n const simplified = simplifyPath(this.rawPoints, 2);\n const smoothed = smoothCatmullRom(simplified, 4);\n\n const line: FlowLine = {\n id: generateId('line'),\n type: 'freehand',\n points: smoothed,\n strokeColor: 'theme:base-content',\n strokeWidth: 2,\n lineStyle: 'solid',\n };\n\n this.callbacks.onLineComplete(line);\n this.rawPoints = [];\n };\n\n destroy(): void {\n this.deactivate();\n if (!this.preview.destroyed) this.preview.destroy();\n }\n}\n"],"mappings":";;;;;AAgBA,
|
|
1
|
+
{"version":3,"file":"FreehandTool.js","names":["FreehandTool","stage","callbacks","Graphics","world","e","i","resolveThemeColor","simplified","simplifyPath","smoothed","smoothCatmullRom","line","generateId"],"sources":["../../../../../src/components/pro/Flow/lines/FreehandTool.ts"],"sourcesContent":["import { Graphics, Container, FederatedPointerEvent } from 'pixi.js';\nimport { simplifyPath, smoothCatmullRom, type Point } from './lineSmoothing';\nimport type { FlowLine } from '../types';\nimport { generateId } from '../nodes/nodeUtils';\nimport { resolveThemeColor } from '../utils/themeColor';\n\nexport interface FreehandToolCallbacks {\n /** Called when a freehand line is finished */\n onLineComplete: (line: FlowLine) => void;\n /** Convert screen coords to world coords */\n screenToWorld: (sx: number, sy: number) => { x: number; y: number };\n}\n\n/**\n * Captures pointer trail for freehand drawing, smooths and stores as FlowLine.\n */\nexport class FreehandTool {\n private stage: Container;\n private preview: Graphics;\n private callbacks: FreehandToolCallbacks;\n private rawPoints: Point[] = [];\n private drawing = false;\n private _active = false;\n\n constructor(stage: Container, world: Container, callbacks: FreehandToolCallbacks) {\n this.stage = stage;\n this.callbacks = callbacks;\n this.preview = new Graphics();\n this.preview.label = 'freehand-preview';\n this.preview.visible = false;\n world.addChild(this.preview);\n }\n\n get active(): boolean { return this._active; }\n\n activate(): void {\n this._active = true;\n this.stage.on('pointerdown', this._onDown, this);\n this.stage.on('globalpointermove', this._onMove, this);\n this.stage.on('pointerup', this._onUp, this);\n this.stage.on('pointerupoutside', this._onUp, this);\n }\n\n deactivate(): void {\n this._active = false;\n this.drawing = false;\n this.rawPoints = [];\n if (!this.preview.destroyed) {\n this.preview.clear();\n this.preview.visible = false;\n }\n this.stage.off('pointerdown', this._onDown, this);\n this.stage.off('globalpointermove', this._onMove, this);\n this.stage.off('pointerup', this._onUp, this);\n this.stage.off('pointerupoutside', this._onUp, this);\n }\n\n private _onDown = (e: FederatedPointerEvent): void => {\n if (e.button !== 0) return;\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n this.drawing = true;\n this.rawPoints = [world];\n this.preview.visible = true;\n this.preview.clear();\n };\n\n private _onMove = (e: FederatedPointerEvent): void => {\n if (!this.drawing) return;\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n this.rawPoints.push(world);\n\n // Draw preview\n this.preview.clear();\n if (this.rawPoints.length < 2) return;\n this.preview.moveTo(this.rawPoints[0].x, this.rawPoints[0].y);\n for (let i = 1; i < this.rawPoints.length; i++) {\n this.preview.lineTo(this.rawPoints[i].x, this.rawPoints[i].y);\n }\n this.preview.stroke({ color: resolveThemeColor('theme:base-content', '#374151'), width: 2, alpha: 0.6 });\n };\n\n private _onUp = (): void => {\n if (!this.drawing) return;\n this.drawing = false;\n this.preview.clear();\n this.preview.visible = false;\n\n if (this.rawPoints.length < 3) {\n this.rawPoints = [];\n return;\n }\n\n const simplified = simplifyPath(this.rawPoints, 2);\n const smoothed = smoothCatmullRom(simplified, 4);\n\n const line: FlowLine = {\n id: generateId('line'),\n type: 'freehand',\n points: smoothed,\n strokeColor: 'theme:base-content',\n strokeWidth: 2,\n lineStyle: 'solid',\n };\n\n this.callbacks.onLineComplete(line);\n this.rawPoints = [];\n };\n\n destroy(): void {\n this.deactivate();\n if (!this.preview.destroyed) this.preview.destroy();\n }\n}\n"],"mappings":";;;;;AAgBA,IAAaA,IAAb,MAA0B;CACxB;CACA;CACA;CACA,YAA6B,CAAC;CAC9B,UAAkB;CAClB,UAAkB;CAElB,YAAY,GAAkB,GAAkB,GAAkC;EAMhF,AALA,KAAK,QAAQC,GACb,KAAK,YAAYC,GACjB,KAAK,UAAU,IAAIC,EAAS,GAC5B,KAAK,QAAQ,QAAQ,oBACrB,KAAK,QAAQ,UAAU,IACvBC,EAAM,SAAS,KAAK,OAAO;CAC7B;CAEA,IAAI,SAAkB;EAAE,OAAO,KAAK;CAAS;CAE7C,WAAiB;EAKf,AAJA,KAAK,UAAU,IACf,KAAK,MAAM,GAAG,eAAe,KAAK,SAAS,IAAI,GAC/C,KAAK,MAAM,GAAG,qBAAqB,KAAK,SAAS,IAAI,GACrD,KAAK,MAAM,GAAG,aAAa,KAAK,OAAO,IAAI,GAC3C,KAAK,MAAM,GAAG,oBAAoB,KAAK,OAAO,IAAI;CACpD;CAEA,aAAmB;EAWjB,AAVA,KAAK,UAAU,IACf,KAAK,UAAU,IACf,KAAK,YAAY,CAAC,GACb,KAAK,QAAQ,cAChB,KAAK,QAAQ,MAAM,GACnB,KAAK,QAAQ,UAAU,KAEzB,KAAK,MAAM,IAAI,eAAe,KAAK,SAAS,IAAI,GAChD,KAAK,MAAM,IAAI,qBAAqB,KAAK,SAAS,IAAI,GACtD,KAAK,MAAM,IAAI,aAAa,KAAK,OAAO,IAAI,GAC5C,KAAK,MAAM,IAAI,oBAAoB,KAAK,OAAO,IAAI;CACrD;CAEA,WAAmB,MAAmC;EACpD,IAAI,EAAE,WAAW,GAAG;EACpB,IAAMA,IAAQ,KAAK,UAAU,cAAc,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC;EAIjE,AAHA,KAAK,UAAU,IACf,KAAK,YAAY,CAACA,CAAK,GACvB,KAAK,QAAQ,UAAU,IACvB,KAAK,QAAQ,MAAM;CACrB;CAEA,WAAmB,MAAmC;EACpD,IAAI,CAAC,KAAK,SAAS;EACnB,IAAMA,IAAQ,KAAK,UAAU,cAAcC,EAAE,OAAO,GAAGA,EAAE,OAAO,CAAC;EAKjE,IAJA,KAAK,UAAU,KAAKD,CAAK,GAGzB,KAAK,QAAQ,MAAM,GACf,OAAK,UAAU,SAAS,IAC5B;QAAK,QAAQ,OAAO,KAAK,UAAU,EAAE,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC,CAAC;GAC5D,KAAK,IAAIE,IAAI,GAAGA,IAAI,KAAK,UAAU,QAAQ,KACzC,KAAK,QAAQ,OAAO,KAAK,UAAUA,EAAE,CAAC,GAAG,KAAK,UAAUA,EAAE,CAAC,CAAC;GAE9D,KAAK,QAAQ,OAAO;IAAE,OAAOC,EAAkB,sBAAsB,SAAS;IAAG,OAAO;IAAG,OAAO;GAAI,CAAC;EAJ3C;CAK9D;CAEA,cAA4B;EAC1B,IAAI,CAAC,KAAK,SAAS;EAKnB,IAJA,KAAK,UAAU,IACf,KAAK,QAAQ,MAAM,GACnB,KAAK,QAAQ,UAAU,IAEnB,KAAK,UAAU,SAAS,GAAG;GAC7B,KAAK,YAAY,CAAC;GAClB;EACF;EAEA,IAAMC,IAAaC,EAAa,KAAK,WAAW,CAAC,GAC3CC,IAAWC,EAAiBH,GAAY,CAAC,GAEzCI,IAAiB;GACrB,IAAIC,EAAW,MAAM;GACrB,MAAM;GACN,QAAQH;GACR,aAAa;GACb,aAAa;GACb,WAAW;EACb;EAGA,AADA,KAAK,UAAU,eAAeE,CAAI,GAClC,KAAK,YAAY,CAAC;CACpB;CAEA,UAAgB;EAEd,AADA,KAAK,WAAW,GACX,KAAK,QAAQ,aAAW,KAAK,QAAQ,QAAQ;CACpD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeometricLineTool.js","names":[],"sources":["../../../../../src/components/pro/Flow/lines/GeometricLineTool.ts"],"sourcesContent":["import { Graphics, Container, FederatedPointerEvent } from 'pixi.js';\nimport type { FlowLine, FlowLineType, FlowEdgeMarkerKind } from '../types';\nimport { generateId } from '../nodes/nodeUtils';\nimport { resolveThemeColor } from '../utils/themeColor';\n\nfunction previewStrokeColor(): string {\n return resolveThemeColor('theme:base-content', '#374151');\n}\n\nexport interface GeometricLineToolCallbacks {\n onLineComplete: (line: FlowLine) => void;\n screenToWorld: (sx: number, sy: number) => { x: number; y: number };\n}\n\ntype GeoMode = 'straight' | 'polyline' | 'arrow';\n\n/**\n * Click-to-place geometric lines: straight, polyline, and arrow.\n * - straight/arrow: click start, click end\n * - polyline: click each point, double-click to finish\n */\nexport class GeometricLineTool {\n private stage: Container;\n private preview: Graphics;\n private callbacks: GeometricLineToolCallbacks;\n private points: Array<{ x: number; y: number }> = [];\n private _active = false;\n private _mode: GeoMode = 'straight';\n private _currentPos = { x: 0, y: 0 };\n private _lastClickTime = 0;\n private _lastClickPos = { x: 0, y: 0 };\n\n constructor(stage: Container, world: Container, callbacks: GeometricLineToolCallbacks) {\n this.stage = stage;\n this.callbacks = callbacks;\n this.preview = new Graphics();\n this.preview.label = 'geo-line-preview';\n this.preview.visible = false;\n world.addChild(this.preview);\n this._handleKeyDown = this._handleKeyDown.bind(this);\n }\n\n get active(): boolean { return this._active; }\n get mode(): GeoMode { return this._mode; }\n set mode(m: GeoMode) { this._mode = m; }\n\n activate(mode: GeoMode = 'straight'): void {\n this._active = true;\n this._mode = mode;\n this.points = [];\n this._lastClickTime = 0;\n this.stage.on('pointerdown', this._onClick, this);\n this.stage.on('globalpointermove', this._onMove, this);\n window.addEventListener('keydown', this._handleKeyDown);\n }\n\n deactivate(): void {\n this._active = false;\n this.points = [];\n if (!this.preview.destroyed) {\n this.preview.clear();\n this.preview.visible = false;\n }\n this.stage.off('pointerdown', this._onClick, this);\n this.stage.off('globalpointermove', this._onMove, this);\n window.removeEventListener('keydown', this._handleKeyDown);\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === 'Return') {\n if (this._mode === 'polyline' && this.points.length >= 2) {\n this._finishLine();\n }\n }\n }\n\n private _onClick = (e: FederatedPointerEvent): void => {\n if (e.button !== 0) return;\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n\n const now = Date.now();\n const isDbl = now - this._lastClickTime < 350\n && Math.hypot(world.x - this._lastClickPos.x, world.y - this._lastClickPos.y) < 10;\n this._lastClickTime = now;\n this._lastClickPos = world;\n\n if (isDbl && this._mode === 'polyline' && this.points.length >= 2) {\n this._finishLine();\n return;\n }\n\n this.points.push(world);\n if ((this._mode === 'straight' || this._mode === 'arrow') && this.points.length >= 2) {\n this._finishLine();\n }\n };\n\n private _onMove = (e: FederatedPointerEvent): void => {\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n this._currentPos = world;\n\n if (this.points.length === 0) return;\n this.preview.visible = true;\n this.preview.clear();\n\n this.preview.moveTo(this.points[0].x, this.points[0].y);\n for (let i = 1; i < this.points.length; i++) {\n this.preview.lineTo(this.points[i].x, this.points[i].y);\n }\n this.preview.lineTo(world.x, world.y);\n this.preview.stroke({ color: previewStrokeColor(), width: 2, alpha: 0.5 });\n };\n\n private _finishLine(): void {\n const markerEnd: FlowEdgeMarkerKind = this._mode === 'arrow' ? 'arrowclosed' : 'none';\n const lineType: FlowLineType = this._mode === 'arrow' ? 'arrow' : this._mode;\n\n const line: FlowLine = {\n id: generateId('line'),\n type: lineType,\n points: [...this.points],\n strokeColor: 'theme:base-content',\n strokeWidth: 2,\n lineStyle: 'solid',\n markerEnd,\n };\n\n this.callbacks.onLineComplete(line);\n this.points = [];\n this.preview.clear();\n this.preview.visible = false;\n }\n\n destroy(): void {\n this.deactivate();\n if (!this.preview.destroyed) this.preview.destroy();\n }\n}\n"],"mappings":";;;;AAKA,
|
|
1
|
+
{"version":3,"file":"GeometricLineTool.js","names":["previewStrokeColor","resolveThemeColor","GeometricLineTool","stage","callbacks","Graphics","world","m","mode","now","isDbl","i","markerEnd","lineType","line","generateId"],"sources":["../../../../../src/components/pro/Flow/lines/GeometricLineTool.ts"],"sourcesContent":["import { Graphics, Container, FederatedPointerEvent } from 'pixi.js';\nimport type { FlowLine, FlowLineType, FlowEdgeMarkerKind } from '../types';\nimport { generateId } from '../nodes/nodeUtils';\nimport { resolveThemeColor } from '../utils/themeColor';\n\nfunction previewStrokeColor(): string {\n return resolveThemeColor('theme:base-content', '#374151');\n}\n\nexport interface GeometricLineToolCallbacks {\n onLineComplete: (line: FlowLine) => void;\n screenToWorld: (sx: number, sy: number) => { x: number; y: number };\n}\n\ntype GeoMode = 'straight' | 'polyline' | 'arrow';\n\n/**\n * Click-to-place geometric lines: straight, polyline, and arrow.\n * - straight/arrow: click start, click end\n * - polyline: click each point, double-click to finish\n */\nexport class GeometricLineTool {\n private stage: Container;\n private preview: Graphics;\n private callbacks: GeometricLineToolCallbacks;\n private points: Array<{ x: number; y: number }> = [];\n private _active = false;\n private _mode: GeoMode = 'straight';\n private _currentPos = { x: 0, y: 0 };\n private _lastClickTime = 0;\n private _lastClickPos = { x: 0, y: 0 };\n\n constructor(stage: Container, world: Container, callbacks: GeometricLineToolCallbacks) {\n this.stage = stage;\n this.callbacks = callbacks;\n this.preview = new Graphics();\n this.preview.label = 'geo-line-preview';\n this.preview.visible = false;\n world.addChild(this.preview);\n this._handleKeyDown = this._handleKeyDown.bind(this);\n }\n\n get active(): boolean { return this._active; }\n get mode(): GeoMode { return this._mode; }\n set mode(m: GeoMode) { this._mode = m; }\n\n activate(mode: GeoMode = 'straight'): void {\n this._active = true;\n this._mode = mode;\n this.points = [];\n this._lastClickTime = 0;\n this.stage.on('pointerdown', this._onClick, this);\n this.stage.on('globalpointermove', this._onMove, this);\n window.addEventListener('keydown', this._handleKeyDown);\n }\n\n deactivate(): void {\n this._active = false;\n this.points = [];\n if (!this.preview.destroyed) {\n this.preview.clear();\n this.preview.visible = false;\n }\n this.stage.off('pointerdown', this._onClick, this);\n this.stage.off('globalpointermove', this._onMove, this);\n window.removeEventListener('keydown', this._handleKeyDown);\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === 'Return') {\n if (this._mode === 'polyline' && this.points.length >= 2) {\n this._finishLine();\n }\n }\n }\n\n private _onClick = (e: FederatedPointerEvent): void => {\n if (e.button !== 0) return;\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n\n const now = Date.now();\n const isDbl = now - this._lastClickTime < 350\n && Math.hypot(world.x - this._lastClickPos.x, world.y - this._lastClickPos.y) < 10;\n this._lastClickTime = now;\n this._lastClickPos = world;\n\n if (isDbl && this._mode === 'polyline' && this.points.length >= 2) {\n this._finishLine();\n return;\n }\n\n this.points.push(world);\n if ((this._mode === 'straight' || this._mode === 'arrow') && this.points.length >= 2) {\n this._finishLine();\n }\n };\n\n private _onMove = (e: FederatedPointerEvent): void => {\n const world = this.callbacks.screenToWorld(e.global.x, e.global.y);\n this._currentPos = world;\n\n if (this.points.length === 0) return;\n this.preview.visible = true;\n this.preview.clear();\n\n this.preview.moveTo(this.points[0].x, this.points[0].y);\n for (let i = 1; i < this.points.length; i++) {\n this.preview.lineTo(this.points[i].x, this.points[i].y);\n }\n this.preview.lineTo(world.x, world.y);\n this.preview.stroke({ color: previewStrokeColor(), width: 2, alpha: 0.5 });\n };\n\n private _finishLine(): void {\n const markerEnd: FlowEdgeMarkerKind = this._mode === 'arrow' ? 'arrowclosed' : 'none';\n const lineType: FlowLineType = this._mode === 'arrow' ? 'arrow' : this._mode;\n\n const line: FlowLine = {\n id: generateId('line'),\n type: lineType,\n points: [...this.points],\n strokeColor: 'theme:base-content',\n strokeWidth: 2,\n lineStyle: 'solid',\n markerEnd,\n };\n\n this.callbacks.onLineComplete(line);\n this.points = [];\n this.preview.clear();\n this.preview.visible = false;\n }\n\n destroy(): void {\n this.deactivate();\n if (!this.preview.destroyed) this.preview.destroy();\n }\n}\n"],"mappings":"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"}
|