diff --git a/packages/ui/src/views/agentflowsv2/Canvas.jsx b/packages/ui/src/views/agentflowsv2/Canvas.jsx
index 3a724666f94..bb76275ae36 100644
--- a/packages/ui/src/views/agentflowsv2/Canvas.jsx
+++ b/packages/ui/src/views/agentflowsv2/Canvas.jsx
@@ -31,6 +31,7 @@ import ConfirmDialog from '@/ui-component/dialog/ConfirmDialog'
import EditNodeDialog from '@/views/agentflowsv2/EditNodeDialog'
import ChatPopUp from '@/views/chatmessage/ChatPopUp'
import ValidationPopUp from '@/views/chatmessage/ValidationPopUp'
+import SaveChatflowDialog from '@/ui-component/dialog/SaveChatflowDialog'
import { flowContext } from '@/store/context/ReactFlowContext'
// API
@@ -100,6 +101,7 @@ const AgentflowCanvas = () => {
const [isSyncNodesButtonEnabled, setIsSyncNodesButtonEnabled] = useState(false)
const [editNodeDialogOpen, setEditNodeDialogOpen] = useState(false)
const [editNodeDialogProps, setEditNodeDialogProps] = useState({})
+ const [flowDialogOpen, setFlowDialogOpen] = useState(false)
const [isSnappingEnabled, setIsSnappingEnabled] = useState(false)
const reactFlowWrapper = useRef(null)
@@ -240,6 +242,15 @@ const AgentflowCanvas = () => {
}
}
+ const onConfirmSaveName = (flowName) => {
+ setFlowDialogOpen(false)
+ handleSaveFlow(flowName)
+ }
+
+ const openSaveDialog = () => {
+ setFlowDialogOpen(true)
+ }
+
// eslint-disable-next-line
const onNodeClick = useCallback((event, clickedNode) => {
setSelectedNode(clickedNode)
@@ -785,13 +796,28 @@ const AgentflowCanvas = () => {
)}
-
+
{!chatPopupOpen && }
+ setFlowDialogOpen(false)}
+ onConfirm={onConfirmSaveName}
+ />
>
)
diff --git a/packages/ui/src/views/canvas/index.jsx b/packages/ui/src/views/canvas/index.jsx
index ebfbd0506fa..4027af6149f 100644
--- a/packages/ui/src/views/canvas/index.jsx
+++ b/packages/ui/src/views/canvas/index.jsx
@@ -24,6 +24,7 @@ import StickyNote from './StickyNote'
import CanvasHeader from './CanvasHeader'
import AddNodes from './AddNodes'
import ConfirmDialog from '@/ui-component/dialog/ConfirmDialog'
+import SaveChatflowDialog from '@/ui-component/dialog/SaveChatflowDialog'
import ChatPopUp from '@/views/chatmessage/ChatPopUp'
import VectorStorePopUp from '@/views/vectorstore/VectorStorePopUp'
import { flowContext } from '@/store/context/ReactFlowContext'
@@ -104,6 +105,7 @@ const Canvas = () => {
const [lastUpdatedDateTime, setLasUpdatedDateTime] = useState('')
const [chatflowName, setChatflowName] = useState('')
const [flowData, setFlowData] = useState('')
+ const [flowDialogOpen, setFlowDialogOpen] = useState(false)
// ==============================|| Chatflow API ||============================== //
@@ -243,6 +245,16 @@ const Canvas = () => {
}
}
+ const openSaveDialog = () => {
+ if (chatflow?.id) {
+ // If chatflow has an ID, save directly using the flow name
+ handleSaveFlow(chatflow.name)
+ } else {
+ // If no ID, open the save dialog
+ setFlowDialogOpen(true)
+ }
+ }
+
// eslint-disable-next-line
const onNodeClick = useCallback((event, clickedNode) => {
setSelectedNode(clickedNode)
@@ -431,7 +443,8 @@ const Canvas = () => {
const chatflow = createNewChatflowApi.data
dispatch({ type: SET_CHATFLOW, chatflow })
saveChatflowSuccess()
- window.history.replaceState(state, null, `/${isAgentCanvas ? 'agentcanvas' : 'canvas'}/${chatflow.id}`)
+ setFlowDialogOpen(false) // Close the save dialog
+ navigate(`/${isAgentCanvas ? 'agentcanvas' : 'canvas'}/${chatflow.id}`)
} else if (createNewChatflowApi.error) {
errorFailed(`Failed to retrieve ${canvasTitle}: ${createNewChatflowApi.error.response.data.message}`)
}
@@ -645,12 +658,22 @@ const Canvas = () => {
)}
{isUpsertButtonEnabled && }
-
+
+ setFlowDialogOpen(false)}
+ onConfirm={(name) => handleSaveFlow(name)}
+ />
>
)
diff --git a/packages/ui/src/views/chatmessage/ChatMessage.jsx b/packages/ui/src/views/chatmessage/ChatMessage.jsx
index b3eb5ed2718..cd8e9ec4509 100644
--- a/packages/ui/src/views/chatmessage/ChatMessage.jsx
+++ b/packages/ui/src/views/chatmessage/ChatMessage.jsx
@@ -38,7 +38,8 @@ import {
IconSquareFilled,
IconCheck,
IconPaperclip,
- IconSparkles
+ IconSparkles,
+ IconDeviceFloppy
} from '@tabler/icons-react'
import robotPNG from '@/assets/images/robot.png'
import userPNG from '@/assets/images/account.png'
@@ -162,7 +163,7 @@ CardWithDeleteOverlay.propTypes = {
onDelete: PropTypes.func
}
-const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, previews, setPreviews }) => {
+const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, previews, setPreviews, onOpenSaveDialog }) => {
const theme = useTheme()
const customization = useSelector((state) => state.customization)
@@ -1672,6 +1673,43 @@ const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, previews, setP
}
}
+ // If no chatflowid and this is an agent canvas, show save prompt
+ if (!chatflowid) {
+ const handleSaveFlow = () => {
+ // Use the proper Flowise save dialog
+ if (onOpenSaveDialog) {
+ onOpenSaveDialog()
+ }
+ }
+
+ return (
+
+
+
+ Save Flow to Continue
+
+
+ You need to save this {isAgentCanvas ? 'agent flow' : 'chatflow'} before you can test it with messages.
+
+ }>
+ Save Flow
+
+
+ )
+ }
+
if (isConfigLoading) {
return (
{
+const ChatPopUp = ({ chatflowid, isAgentCanvas, onOpenChange, onOpenSaveDialog }) => {
const theme = useTheme()
const { confirm } = useConfirm()
const dispatch = useDispatch()
@@ -215,6 +215,7 @@ const ChatPopUp = ({ chatflowid, isAgentCanvas, onOpenChange }) => {
open={open}
previews={previews}
setPreviews={setPreviews}
+ onOpenSaveDialog={onOpenSaveDialog}
/>
@@ -238,7 +239,8 @@ const ChatPopUp = ({ chatflowid, isAgentCanvas, onOpenChange }) => {
ChatPopUp.propTypes = {
chatflowid: PropTypes.string,
isAgentCanvas: PropTypes.bool,
- onOpenChange: PropTypes.func
+ onOpenChange: PropTypes.func,
+ onOpenSaveDialog: PropTypes.func
}
export default memo(ChatPopUp)