Skip to content

Commit 883e258

Browse files
committed
Merge remote-tracking branch 'origin' into feat/344-ambient-agents-webhooks
2 parents bbb7161 + e10a4ca commit 883e258

80 files changed

Lines changed: 4290 additions & 461 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Flowise support different environment variables to configure your instance. You
159159
| PORT | The HTTP port Flowise runs on | Number | 3000 |
160160
| CORS_ALLOW_CREDENTIALS | Enables CORS `Access-Control-Allow-Credentials` when `true` | Boolean | false |
161161
| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | |
162+
| MCP_CORS_ORIGINS | The allowed origins for MCP endpoint cross-origin calls. If unset, only non-browser (no Origin header) requests are allowed. Set to `*` to allow all origins. | String | |
162163
| IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | |
163164
| FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb |
164165
| DEBUG | Print logs from components | Boolean | |

docker/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ PORT=3000
7676
# NUMBER_OF_PROXIES= 1
7777
# CORS_ALLOW_CREDENTIALS=false
7878
# CORS_ORIGINS=*
79+
# MCP_CORS_ORIGINS=*
7980
# IFRAME_ORIGINS=*
8081
# FLOWISE_FILE_SIZE_LIMIT=50mb
8182
# SHOW_COMMUNITY_NODES=true

docker/docker-compose-queue-prebuilt.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ services:
7676
# SETTINGS
7777
- NUMBER_OF_PROXIES=${NUMBER_OF_PROXIES}
7878
- CORS_ORIGINS=${CORS_ORIGINS}
79+
- MCP_CORS_ORIGINS=${MCP_CORS_ORIGINS}
7980
- IFRAME_ORIGINS=${IFRAME_ORIGINS}
8081
- FLOWISE_FILE_SIZE_LIMIT=${FLOWISE_FILE_SIZE_LIMIT}
8182
- SHOW_COMMUNITY_NODES=${SHOW_COMMUNITY_NODES}

docker/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ services:
6161
# SETTINGS
6262
- NUMBER_OF_PROXIES=${NUMBER_OF_PROXIES}
6363
- CORS_ORIGINS=${CORS_ORIGINS}
64+
- MCP_CORS_ORIGINS=${MCP_CORS_ORIGINS}
6465
- IFRAME_ORIGINS=${IFRAME_ORIGINS}
6566
- FLOWISE_FILE_SIZE_LIMIT=${FLOWISE_FILE_SIZE_LIMIT}
6667
- SHOW_COMMUNITY_NODES=${SHOW_COMMUNITY_NODES}

packages/agentflow/examples/src/App.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
DarkModeExampleProps,
1616
FilteredComponentsExampleProps,
1717
MultiNodeFlowProps,
18-
StatusIndicatorsExampleProps
18+
StatusIndicatorsExampleProps,
19+
ValidationActionsExampleProps
1920
} from './demos'
2021
import { PropsDisplay } from './PropsDisplay'
2122

@@ -81,6 +82,13 @@ const examples: Array<{
8182
description: 'Restrict available nodes with presets',
8283
props: FilteredComponentsExampleProps,
8384
component: lazy(() => import('./demos/FilteredComponentsExample').then((m) => ({ default: m.FilteredComponentsExample })))
85+
},
86+
{
87+
id: 'canvas-actions',
88+
name: 'Canvas Actions',
89+
description: 'Custom FABs alongside the validation button via canvasActions',
90+
props: ValidationActionsExampleProps,
91+
component: lazy(() => import('./demos/ValidationActionsExample').then((m) => ({ default: m.ValidationActionsExample })))
8492
}
8593
]
8694

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Canvas Actions Example
3+
*
4+
* Demonstrates how to add custom FAB buttons alongside the built-in validation
5+
* button in the top-right canvas overlay using the `canvasActions` prop.
6+
*
7+
* Mirrors the legacy v2 pattern where a chat FAB and validation FAB sit side-by-side.
8+
*/
9+
10+
import { useState } from 'react'
11+
12+
import { Agentflow } from '@flowiseai/agentflow'
13+
import { Box, Dialog, DialogContent, DialogTitle, Fab, IconButton, Typography } from '@mui/material'
14+
import { IconMessage, IconX } from '@tabler/icons-react'
15+
16+
import { apiBaseUrl, token } from '../config'
17+
18+
function ChatFab() {
19+
const [open, setOpen] = useState(false)
20+
21+
return (
22+
<>
23+
<Fab
24+
size='small'
25+
aria-label='chat'
26+
title='Chat'
27+
onClick={() => setOpen(true)}
28+
sx={{
29+
color: 'white',
30+
backgroundColor: 'secondary.main',
31+
'&:hover': {
32+
backgroundColor: 'secondary.main',
33+
backgroundImage: 'linear-gradient(rgb(0 0 0/10%) 0 0)'
34+
}
35+
}}
36+
>
37+
<IconMessage />
38+
</Fab>
39+
40+
<Dialog open={open} onClose={() => setOpen(false)} maxWidth='sm' fullWidth>
41+
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
42+
Chat
43+
<IconButton size='small' onClick={() => setOpen(false)}>
44+
<IconX size={18} />
45+
</IconButton>
46+
</DialogTitle>
47+
<DialogContent>
48+
<Box sx={{ py: 4, textAlign: 'center' }}>
49+
<Typography variant='body2' color='text.secondary'>
50+
Your chat UI goes here. Full control — bring any component.
51+
</Typography>
52+
</Box>
53+
</DialogContent>
54+
</Dialog>
55+
</>
56+
)
57+
}
58+
59+
export function ValidationActionsExample() {
60+
return (
61+
<div style={{ width: '100%', height: '100%' }}>
62+
<Agentflow apiBaseUrl={apiBaseUrl} token={token ?? undefined} canvasActions={<ChatFab />} />
63+
</div>
64+
)
65+
}
66+
67+
export const ValidationActionsExampleProps = {
68+
apiBaseUrl: '{from environment variables}',
69+
token: '{from environment variables}',
70+
canvasActions: '<ChatFab />'
71+
}

packages/agentflow/examples/src/demos/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './DarkModeExample'
66
export * from './FilteredComponentsExample'
77
export * from './MultiNodeFlow'
88
export * from './StatusIndicatorsExample'
9+
export * from './ValidationActionsExample'

packages/agentflow/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@flowiseai/agentflow",
3-
"version": "0.0.0-dev.10",
3+
"version": "0.0.0-dev.13",
44
"description": "Embeddable React component for building and visualizing AI agent workflows",
55
"license": "Apache-2.0",
66
"repository": {

packages/agentflow/src/Agentflow.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,30 @@ describe('Agentflow Component', () => {
428428
})
429429
})
430430

431+
describe('Canvas Actions', () => {
432+
it('should render canvasActions content in the canvas', async () => {
433+
const { getByTestId } = render(
434+
<Agentflow {...defaultProps} canvasActions={<button data-testid='custom-action'>My Button</button>} />
435+
)
436+
437+
await waitFor(() => {
438+
expect(getByTestId('custom-action')).toBeInTheDocument()
439+
})
440+
})
441+
442+
it('should not render canvasActions in read-only mode', async () => {
443+
const { container, queryByTestId } = render(
444+
<Agentflow {...defaultProps} readOnly={true} canvasActions={<button data-testid='custom-action'>My Button</button>} />
445+
)
446+
447+
await waitFor(() => {
448+
expect(container.querySelector('.agentflow-container')).toBeInTheDocument()
449+
})
450+
451+
expect(queryByTestId('custom-action')).not.toBeInTheDocument()
452+
})
453+
})
454+
431455
describe('Imperative Ref', () => {
432456
it('should expose agentflow instance via ref', async () => {
433457
const ref = createRef<AgentFlowInstance>()

packages/agentflow/src/Agentflow.tsx

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ function AgentflowCanvas({
4141
showDefaultHeader = true,
4242
enableGenerator = true,
4343
showDefaultPalette = true,
44+
canvasActions,
4445
renderHeader,
4546
renderNodePalette
4647
}: {
@@ -52,6 +53,7 @@ function AgentflowCanvas({
5253
showDefaultHeader?: boolean
5354
showDefaultPalette?: boolean
5455
enableGenerator?: boolean
56+
canvasActions?: AgentflowProps['canvasActions']
5557
renderHeader?: AgentflowProps['renderHeader']
5658
renderNodePalette?: AgentflowProps['renderNodePalette']
5759
}) {
@@ -265,21 +267,33 @@ function AgentflowCanvas({
265267
position: 'absolute',
266268
left: showDefaultPalette ? 70 : 20, // 70px offset = ~10px gap between buttons
267269
top: 20,
268-
zIndex: 1001
270+
zIndex: tokens.zIndex.canvasButton
269271
}}
270272
>
271273
<IconSparkles />
272274
</StyledFab>
273275
)}
274276

275-
{/* Validation Feedback - positioned at top right */}
277+
{/* Canvas action buttons - positioned at top right */}
276278
{!readOnly && (
277-
<ValidationFeedback
278-
nodes={nodes as FlowNode[]}
279-
edges={edges as FlowEdge[]}
280-
availableNodes={availableNodes}
281-
setNodes={setLocalNodes as React.Dispatch<React.SetStateAction<FlowNode[]>>}
282-
/>
279+
<div
280+
style={{
281+
position: 'absolute',
282+
right: 20,
283+
top: 20,
284+
zIndex: tokens.zIndex.canvasButton,
285+
display: 'flex',
286+
gap: 8
287+
}}
288+
>
289+
<ValidationFeedback
290+
nodes={nodes as FlowNode[]}
291+
edges={edges as FlowEdge[]}
292+
availableNodes={availableNodes}
293+
setNodes={setLocalNodes as React.Dispatch<React.SetStateAction<FlowNode[]>>}
294+
/>
295+
{canvasActions}
296+
</div>
283297
)}
284298

285299
<ReactFlow
@@ -368,7 +382,8 @@ export const Agentflow = forwardRef<AgentFlowInstance, AgentflowProps>(function
368382
renderHeader,
369383
renderNodePalette,
370384
showDefaultHeader = true,
371-
showDefaultPalette = true
385+
showDefaultPalette = true,
386+
canvasActions
372387
} = props
373388

374389
return (
@@ -394,6 +409,7 @@ export const Agentflow = forwardRef<AgentFlowInstance, AgentflowProps>(function
394409
showDefaultPalette={showDefaultPalette}
395410
renderHeader={renderHeader}
396411
renderNodePalette={renderNodePalette}
412+
canvasActions={canvasActions}
397413
/>
398414
</ReactFlowProvider>
399415
</AgentflowProvider>
@@ -416,6 +432,7 @@ const AgentflowCanvasWithRef = forwardRef<
416432
enableGenerator?: boolean
417433
renderHeader?: AgentflowProps['renderHeader']
418434
renderNodePalette?: AgentflowProps['renderNodePalette']
435+
canvasActions?: AgentflowProps['canvasActions']
419436
}
420437
>(function AgentflowCanvasWithRef(props, ref) {
421438
const agentflow = useAgentflow()

0 commit comments

Comments
 (0)