Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
}
},
parser: '@typescript-eslint/parser',
ignorePatterns: ['**/node_modules', '**/dist', '**/build', '**/package-lock.json'],
ignorePatterns: ['**/node_modules', '**/dist', '**/build', '**/coverage', '**/package-lock.json'],
plugins: ['unused-imports'],
rules: {
'@typescript-eslint/explicit-module-boundary-types': 'off',
Expand All @@ -23,6 +23,7 @@ module.exports = {
'unused-imports/no-unused-vars': ['warn', { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' }],
'no-undef': 'off',
'no-console': [process.env.CI ? 'error' : 'warn', { allow: ['warn', 'error', 'info'] }],
'prettier/prettier': 'error'
'prettier/prettier': 'error',
'no-control-regex': 0 // Used to match control regex's in user input
}
}
2 changes: 1 addition & 1 deletion packages/agentflow/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ module.exports = {
},
overrides: [
{
files: ['examples/**/*.{js,jsx,ts,tsx}'],
files: ['examples/**/*.{js,jsx,ts,tsx}', '**/*.md/**'],
rules: {
'no-console': 'off',
'@typescript-eslint/no-non-null-assertion': 'off'
Expand Down
13 changes: 8 additions & 5 deletions packages/agentflow/.npmignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# Source files
# Source & config
/src
/tests
/*.ts
/*.tsx
/examples
*.ts
!*.d.ts
tsconfig.json
vite.config.ts
jest.config.js
.eslintrc.js

# Dev files
# Dev artifacts
.turbo
node_modules
.DS_Store

# Keep dist
!dist
127 changes: 124 additions & 3 deletions packages/agentflow/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# @flowise/agentflow

[![Version](https://img.shields.io/npm/v/@flowise/agentflow)](https://www.npmjs.com/package/@flowise/agentflow)
[![License](https://img.shields.io/badge/license-SEE%20LICENSE-blue)](./LICENSE.md)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/FlowiseAI/Flowise/blob/main/LICENSE.md)

> Embeddable React component for building and visualizing AI agent workflows

Expand Down Expand Up @@ -43,12 +43,94 @@ import '@flowise/agentflow/flowise.css'
export default function App() {
return (
<div style={{ width: '100vw', height: '100vh' }}>
<Agentflow instanceUrl='http://localhost:3000' token='your-api-key' />
<Agentflow apiBaseUrl='http://localhost:3000' token='your-api-key' />
</div>
)
}
```

### With Initial Flow Data and Callbacks

```tsx
import { useRef } from 'react'

import { Agentflow, type AgentFlowInstance, type FlowData } from '@flowise/agentflow'

import '@flowise/agentflow/flowise.css'

export default function App() {
const ref = useRef<AgentFlowInstance>(null)

const initialFlow: FlowData = {
nodes: [
{
id: 'startAgentflow_0',
type: 'agentflowNode',
position: { x: 100, y: 100 },
data: {
id: 'startAgentflow_0',
name: 'startAgentflow',
label: 'Start',
color: '#7EE787',
hideInput: true,
outputAnchors: [{ id: 'startAgentflow_0-output-0', name: 'start', label: 'Start', type: 'start' }]
}
}
],
edges: [],
viewport: { x: 0, y: 0, zoom: 1 }
}

return (
<div style={{ width: '100vw', height: '100vh' }}>
<Agentflow
ref={ref}
apiBaseUrl='http://localhost:3000'
token='your-api-key'
initialFlow={initialFlow}
onFlowChange={(flow) => console.log('Flow changed:', flow)}
onSave={(flow) => console.log('Flow saved:', flow)}
/>
</div>
)
}
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `apiBaseUrl` | `string` | **(required)** | Flowise API server endpoint |
| `token` | `string` | — | Authentication token for API calls |
| `initialFlow` | `FlowData` | — | Initial flow data to render (uncontrolled — only used on mount) |
| `flowId` | `string` | — | Flow ID for loading an existing flow from the API |
| `components` | `string[]` | — | Restrict which node types appear in the palette |
| `onFlowChange` | `(flow: FlowData) => void` | — | Called when the flow changes (node/edge add, remove, move) |
| `onSave` | `(flow: FlowData) => void` | — | Called when the user triggers a save |
| `onFlowGenerated` | `(flow: FlowData) => void` | — | Called when a flow is generated via AI |
| `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Theme override |
| `readOnly` | `boolean` | `false` | Disable editing (nodes not draggable/connectable) |
| `showDefaultHeader` | `boolean` | `true` | Show built-in header (ignored if `renderHeader` provided) |
| `showDefaultPalette` | `boolean` | `true` | Show built-in node palette |
| `enableGenerator` | `boolean` | `true` | Show the AI flow generator button |
| `renderHeader` | `(props: HeaderRenderProps) => ReactNode` | — | Custom header renderer |
| `renderNodePalette` | `(props: PaletteRenderProps) => ReactNode` | — | Custom node palette renderer |

### Imperative Methods (via `ref`)

| Method | Return Type | Description |
|--------|-------------|-------------|
| `getFlow()` | `FlowData` | Get current flow data |
| `toJSON()` | `string` | Export flow as JSON string |
| `validate()` | `ValidationResult` | Validate the current flow |
| `fitView()` | `void` | Fit all nodes into view |
| `clear()` | `void` | Remove all nodes and edges |
| `addNode(name)` | `void` | Add a node by component name |

### Design Note

`<Agentflow>` is an **uncontrolled component**. The `initialFlow` prop seeds the canvas state on mount, but the component owns its own state afterward. Use the `ref` for imperative access and `onFlowChange` to observe changes.

## Development

```bash
Expand All @@ -67,6 +149,45 @@ cd examples && pnpm install && pnpm dev

Visit the [examples](./examples) directory for more usage patterns. See [TESTS.md](./TESTS.md) for the full test plan and coverage status.

## Publishing

### Version Update

Bump the version in `package.json` before publishing. Use `npm version` to update the version and create a git tag:

```bash
# Prerelease (for testing)
npm version prerelease --preid=dev # 0.0.0-dev.1 → 0.0.0-dev.2

# Patch / Minor / Major (for stable releases)
npm version patch # 0.0.1
npm version minor # 0.1.0
npm version major # 1.0.0
```

### Verify Before Publishing

```bash
# Build and check the tarball contents
pnpm build
npm pack --dry-run

# Full publish dry-run (runs prepublishOnly + simulates upload)
npm publish --dry-run
```

### Publish

```bash
# Prerelease — tagged so `npm install @flowise/agentflow` won't pick it up
npm publish --tag dev

# Stable release — gets the `latest` tag
npm publish
```

> The `prepublishOnly` script automatically runs `clean` and `build` before every publish, so stale dist files are never uploaded.

## Documentation

- [ARCHITECTURE.md](./ARCHITECTURE.md) - Internal architecture and design patterns
Expand All @@ -79,7 +200,7 @@ This package follows a feature-based architecture with clear separation of conce

## License

See [LICENSE.md](./LICENSE.md) for details.
Apache-2.0 — see the repository root [LICENSE.md](https://github.com/FlowiseAI/Flowise/blob/main/LICENSE.md) for details.

---

Expand Down
11 changes: 6 additions & 5 deletions packages/agentflow/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ The examples app uses environment variables for configuration. To set up:
cp .env.example .env
```

2. Edit `.env` to configure your Flowise instance:
2. Edit `.env` to configure your Flowise API server:

```bash
# Flowise Instance Configuration
# Flowise API Base URL
VITE_INSTANCE_URL=http://localhost:3000
# API Key (required for authenticated endpoints)
Expand All @@ -62,7 +62,7 @@ The examples app uses environment variables for configuration. To set up:

**Environment Variables:**

- `VITE_INSTANCE_URL`: Base URL of your Flowise instance (default: `http://localhost:3000`)
- `VITE_INSTANCE_URL`: Flowise API server endpoint (maps to `apiBaseUrl` prop, default: `http://localhost:3000`)
- `VITE_API_TOKEN`: Flowise API Key for programmatic access (required for authenticated endpoints)

**Note**: The `.env` file is gitignored and will not be committed to version control. Add your actual API key to `.env`, not `.env.example`.
Expand Down Expand Up @@ -94,11 +94,12 @@ Common causes:
## Examples
### Basic Usage (`App.tsx`)
### Basic Usage (`BasicExample.tsx`)
The default export shows:
Demonstrates core usage:
- Basic canvas rendering with `<Agentflow>` component
- Passing `apiBaseUrl` and `initialFlow` props
- Using the `ref` to access imperative methods (`validate`, `fitView`, `getFlow`, `clear`)
- Handling `onFlowChange` and `onSave` callbacks
Expand Down
10 changes: 5 additions & 5 deletions packages/agentflow/examples/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { type ComponentType, lazy, Suspense, useState } from 'react'

import { instanceUrl, token } from './config'
import { apiBaseUrl, token } from './config'
import {
AllNodeTypesExampleProps,
BasicExampleProps,
Expand Down Expand Up @@ -108,8 +108,8 @@ export default function App() {
const actualProps = currentExample?.props
? Object.fromEntries(
Object.entries(currentExample.props).map(([key, value]) => {
if (key === 'instanceUrl' && typeof value === 'string' && value.includes('environment variables')) {
return [key, instanceUrl]
if (key === 'apiBaseUrl' && typeof value === 'string' && value.includes('environment variables')) {
return [key, apiBaseUrl]
}
if (key === 'token' && typeof value === 'string' && value.includes('environment variables')) {
return [key, token ? `${token.substring(0, 20)}...` : 'undefined']
Expand Down Expand Up @@ -163,9 +163,9 @@ export default function App() {
</select>
{currentExample && <span style={{ color: '#666', fontSize: '13px' }}>{currentExample.description}</span>}

{/* Instance URL Display */}
{/* API Base URL Display */}
<div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: '12px' }}>
<span style={{ fontSize: '12px', color: '#999' }}>{instanceUrl}</span>
<span style={{ fontSize: '12px', color: '#999' }}>{apiBaseUrl}</span>
</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion packages/agentflow/examples/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Application configuration from environment variables
*/
export const instanceUrl = import.meta.env.VITE_INSTANCE_URL || 'http://localhost:3000'
export const apiBaseUrl = import.meta.env.VITE_INSTANCE_URL || 'http://localhost:3000'
export const token = import.meta.env.VITE_API_TOKEN || undefined
10 changes: 5 additions & 5 deletions packages/agentflow/examples/src/demos/AllNodeTypesExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useRef } from 'react'
import type { AgentFlowInstance, FlowData } from '@flowise/agentflow'
import { Agentflow } from '@flowise/agentflow'

import { instanceUrl, token } from '../config'
import { apiBaseUrl, token } from '../config'

// Showcase all node types in a grid layout
const allNodesFlow: FlowData = {
Expand Down Expand Up @@ -238,9 +238,9 @@ export function AllNodeTypesExample() {
<div style={{ flex: 1 }}>
<Agentflow
ref={agentflowRef}
instanceUrl={instanceUrl}
apiBaseUrl={apiBaseUrl}
token={token ?? undefined}
flow={allNodesFlow}
initialFlow={allNodesFlow}
showDefaultHeader={false}
readOnly={true}
/>
Expand All @@ -250,9 +250,9 @@ export function AllNodeTypesExample() {
}

export const AllNodeTypesExampleProps = {
instanceUrl: instanceUrl,
apiBaseUrl: apiBaseUrl,
token: token,
flow: 'FlowData (15 node types)',
initialFlow: 'FlowData (15 node types)',
readOnly: true,
showDefaultHeader: false,
enableGenerator: false
Expand Down
10 changes: 5 additions & 5 deletions packages/agentflow/examples/src/demos/BasicExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useRef, useState } from 'react'
import type { AgentFlowInstance, FlowData, ValidationResult } from '@flowise/agentflow'
import { Agentflow } from '@flowise/agentflow'

import { instanceUrl, token } from '../config'
import { apiBaseUrl, token } from '../config'

// Example flow data
const initialFlow: FlowData = {
Expand Down Expand Up @@ -102,9 +102,9 @@ export function BasicExample() {
<div style={{ flex: 1 }}>
<Agentflow
ref={agentflowRef}
instanceUrl={instanceUrl}
apiBaseUrl={apiBaseUrl}
token={token ?? undefined}
flow={initialFlow}
initialFlow={initialFlow}
onFlowChange={handleFlowChange}
onSave={handleSave}
showDefaultHeader={true}
Expand All @@ -115,9 +115,9 @@ export function BasicExample() {
}

export const BasicExampleProps = {
instanceUrl: '{from environment variables}',
apiBaseUrl: '{from environment variables}',
token: '{from environment variables}',
flow: 'FlowData',
initialFlow: 'FlowData',
onFlowChange: '(flow: FlowData) => void',
onSave: '(flow: FlowData) => void',
showDefaultHeader: true
Expand Down
10 changes: 5 additions & 5 deletions packages/agentflow/examples/src/demos/CustomUIExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useRef, useState } from 'react'
import type { AgentFlowInstance, FlowData, HeaderRenderProps, PaletteRenderProps } from '@flowise/agentflow'
import { Agentflow } from '@flowise/agentflow'

import { instanceUrl, token } from '../config'
import { apiBaseUrl, token } from '../config'

const initialFlow: FlowData = {
nodes: [
Expand Down Expand Up @@ -256,9 +256,9 @@ export function CustomUIExample() {
<div style={{ width: '100%', height: '100vh' }}>
<Agentflow
ref={agentflowRef}
instanceUrl={instanceUrl}
apiBaseUrl={apiBaseUrl}
token={token ?? undefined}
flow={initialFlow}
initialFlow={initialFlow}
renderHeader={(props) => <CustomHeader {...props} />}
renderNodePalette={(props) => <CustomPalette {...props} />}
showDefaultHeader={false}
Expand All @@ -273,9 +273,9 @@ export function CustomUIExample() {
}

export const CustomUIExampleProps = {
instanceUrl: '{from environment variables}',
apiBaseUrl: '{from environment variables}',
token: '{from environment variables}',
flow: 'FlowData',
initialFlow: 'FlowData',
renderHeader: '(props: HeaderRenderProps) => ReactNode',
renderNodePalette: '(props: PaletteRenderProps) => ReactNode',
showDefaultHeader: false,
Expand Down
Loading
Loading