diff --git a/ui/package-lock.json b/ui/package-lock.json index 46fccd086..c81180432 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -14,6 +14,7 @@ "@codemirror/legacy-modes": "^6.5.1", "@flowgram.ai/document": "^0.3.2", "@flowgram.ai/fixed-layout-editor": "^0.3.2", + "@flowgram.ai/minimap-plugin": "^0.3.2", "@tabler/icons-react": "^3.34.1", "@uiw/codemirror-extensions-basic-setup": "^4.24.2", "@uiw/codemirror-theme-vscode": "^4.24.2", @@ -3183,6 +3184,25 @@ "styled-components": ">=4" } }, + "node_modules/@flowgram.ai/minimap-plugin": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/minimap-plugin/-/minimap-plugin-0.3.2.tgz", + "integrity": "sha512-p3MH1sbMt6QJuBrcbDIOdZrEH7T//0oA2Yv7htdCmkqkkTyvfa+X34ZwzrkJcfWcL/ExJWtAsGDY9Ocz0XX9hg==", + "dependencies": { + "@flowgram.ai/core": "0.3.2", + "@flowgram.ai/document": "0.3.2", + "@flowgram.ai/renderer": "0.3.2", + "@flowgram.ai/utils": "0.3.2", + "inversify": "^6.0.1", + "lodash": "^4.17.21", + "reflect-metadata": "~0.2.2" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8", + "styled-components": ">=4" + } + }, "node_modules/@flowgram.ai/node": { "version": "0.3.2", "resolved": "https://registry.npmmirror.com/@flowgram.ai/node/-/node-0.3.2.tgz", diff --git a/ui/package.json b/ui/package.json index 4bfdbc5e7..2725ceafb 100644 --- a/ui/package.json +++ b/ui/package.json @@ -14,8 +14,9 @@ "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.11.2", "@codemirror/legacy-modes": "^6.5.1", - "@flowgram.ai/fixed-layout-editor": "^0.3.2", "@flowgram.ai/document": "^0.3.2", + "@flowgram.ai/fixed-layout-editor": "^0.3.2", + "@flowgram.ai/minimap-plugin": "^0.3.2", "@tabler/icons-react": "^3.34.1", "@uiw/codemirror-extensions-basic-setup": "^4.24.2", "@uiw/codemirror-theme-vscode": "^4.24.2", diff --git a/ui/src/components/workflow/designer/Editor.tsx b/ui/src/components/workflow/designer/Editor.tsx index eed080e4f..b36e8e661 100644 --- a/ui/src/components/workflow/designer/Editor.tsx +++ b/ui/src/components/workflow/designer/Editor.tsx @@ -8,6 +8,7 @@ import { type FlowDocumentJSON, FlowTextKey, } from "@flowgram.ai/fixed-layout-editor"; +import { createMinimapPlugin } from "@flowgram.ai/minimap-plugin"; import "@flowgram.ai/fixed-layout-editor/index.css"; import { useDeepCompareEffect } from "ahooks"; import { theme } from "antd"; @@ -92,6 +93,13 @@ const Editor = ({ className, style, children, initialData, readonly }: EditorPro }; }, + plugins: () => [ + createMinimapPlugin({ + disableLayer: true, + enableDisplayAllNodes: true, + }), + ], + onAllLayersRendered: (ctx) => { // 画布初始化后向下滚动一点,露出可能被 Alert 遮挡的部分 setTimeout(() => { diff --git a/ui/src/components/workflow/designer/Minimap.tsx b/ui/src/components/workflow/designer/Minimap.tsx new file mode 100644 index 000000000..0da737463 --- /dev/null +++ b/ui/src/components/workflow/designer/Minimap.tsx @@ -0,0 +1,36 @@ +import { useService } from "@flowgram.ai/fixed-layout-editor"; +import { FlowMinimapService, MinimapRender } from "@flowgram.ai/minimap-plugin"; + +export interface MinimapProps { + className?: string; + style?: React.CSSProperties; +} + +const Minimap = ({ className, style }: MinimapProps) => { + const minimapService = useService(FlowMinimapService); + + return ( +
+ +
+ ); +}; + +export default Minimap; diff --git a/ui/src/components/workflow/designer/Toolbar.tsx b/ui/src/components/workflow/designer/Toolbar.tsx index 72245af6f..f04f3352c 100644 --- a/ui/src/components/workflow/designer/Toolbar.tsx +++ b/ui/src/components/workflow/designer/Toolbar.tsx @@ -1,11 +1,13 @@ import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { EditorState, FlowLayoutDefault, useClientContext, usePlaygroundTools, useRefresh } from "@flowgram.ai/fixed-layout-editor"; -import { IconHandStop, IconLayoutCards, IconMaximize, IconMinus, IconPlus, IconPointer } from "@tabler/icons-react"; +import { IconHandStop, IconLayoutCards, IconMatrix, IconMaximize, IconMinus, IconPlus } from "@tabler/icons-react"; import { Button, Tooltip } from "antd"; import { mergeCls } from "@/utils/css"; +import Minimap from "./Minimap"; + export interface ToolbarProps { className?: string; style?: React.CSSProperties; @@ -25,6 +27,8 @@ const Toolbar = ({ className, style }: ToolbarProps) => { return () => disposable.dispose(); }, [playground]); + const [isMinimapVisible, setIsMinimapVisible] = useState(window.screen.availWidth >= 1024); + const [isMouseFriendly, setIsMouseFriendly] = useState(playground.editorState.is(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id)); const handleToggleLayout = useCallback(() => { @@ -35,6 +39,10 @@ const Toolbar = ({ className, style }: ToolbarProps) => { } }, [tools.isVertical]); + const handleToggleMinimap = useCallback(() => { + setIsMinimapVisible((prev) => !prev); + }, [isMinimapVisible]); + const handleToggleMouseFriendly = useCallback(() => { if (isMouseFriendly) { playground.editorState.changeState(EditorState.STATE_SELECT.id); @@ -47,7 +55,16 @@ const Toolbar = ({ className, style }: ToolbarProps) => { return (
-
+
+ +
); diff --git a/ui/src/components/workflow/designer/flowgram.css b/ui/src/components/workflow/designer/flowgram.css index 0e4ac1936..ee2cba5c5 100644 --- a/ui/src/components/workflow/designer/flowgram.css +++ b/ui/src/components/workflow/designer/flowgram.css @@ -13,6 +13,10 @@ position: absolute !important; top: -99999px !important; left: -99999px !important; + width: 0 !important; + height: 0 !important; + visibility: hidden !important; + transform: scale(0) !important; } /* 控制 flowgram 线条样式 */ diff --git a/ui/src/components/workflow/designer/nodes/BusinessNode.tsx b/ui/src/components/workflow/designer/nodes/BusinessNode.tsx index af9288615..60e3e58ae 100644 --- a/ui/src/components/workflow/designer/nodes/BusinessNode.tsx +++ b/ui/src/components/workflow/designer/nodes/BusinessNode.tsx @@ -1,6 +1,6 @@ import { getI18n } from "react-i18next"; import { IconCloudUpload, IconContract, IconDeviceDesktopSearch, IconPackage, IconSend } from "@tabler/icons-react"; -import { Avatar, Typography } from "antd"; +import { Avatar } from "antd"; import { nanoid } from "nanoid"; import { deploymentProvidersMap, notificationProvidersMap } from "@/domain/provider"; diff --git a/ui/src/components/workflow/designer/nodes/StartNode.tsx b/ui/src/components/workflow/designer/nodes/StartNode.tsx index 5b14291b7..60104cc26 100644 --- a/ui/src/components/workflow/designer/nodes/StartNode.tsx +++ b/ui/src/components/workflow/designer/nodes/StartNode.tsx @@ -1,6 +1,5 @@ import { getI18n } from "react-i18next"; import { IconRocket } from "@tabler/icons-react"; -import { Typography } from "antd"; import { WORKFLOW_TRIGGERS } from "@/domain/workflow"; diff --git a/ui/src/components/workflow/designer/nodes/_shared.tsx b/ui/src/components/workflow/designer/nodes/_shared.tsx index a9bd61bdb..0e579a0b7 100644 --- a/ui/src/components/workflow/designer/nodes/_shared.tsx +++ b/ui/src/components/workflow/designer/nodes/_shared.tsx @@ -99,7 +99,7 @@ export const NodeMenuButton = ({ className, style, ...props }: ButtonProps) => { return playground.config.readonlyOrDisabled ? null : ( -