diff --git a/web/app/components/base/icons/assets/vender/line/general/plus-02.svg b/web/app/components/base/icons/assets/vender/line/general/plus-02.svg
new file mode 100644
index 0000000000..435832507d
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/line/general/plus-02.svg
@@ -0,0 +1,5 @@
+
diff --git a/web/app/components/base/icons/assets/vender/line/mediaAndDevices/play.svg b/web/app/components/base/icons/assets/vender/line/mediaAndDevices/play.svg
new file mode 100644
index 0000000000..8f80a994e2
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/line/mediaAndDevices/play.svg
@@ -0,0 +1,5 @@
+
diff --git a/web/app/components/base/icons/assets/vender/line/mediaAndDevices/stop.svg b/web/app/components/base/icons/assets/vender/line/mediaAndDevices/stop.svg
new file mode 100644
index 0000000000..c6ec6d4f63
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/line/mediaAndDevices/stop.svg
@@ -0,0 +1,10 @@
+
diff --git a/web/app/components/base/icons/src/vender/line/general/Plus02.json b/web/app/components/base/icons/src/vender/line/general/Plus02.json
new file mode 100644
index 0000000000..8a9516f1ae
--- /dev/null
+++ b/web/app/components/base/icons/src/vender/line/general/Plus02.json
@@ -0,0 +1,39 @@
+{
+ "icon": {
+ "type": "element",
+ "isRootNode": true,
+ "name": "svg",
+ "attributes": {
+ "width": "10",
+ "height": "10",
+ "viewBox": "0 0 10 10",
+ "fill": "none",
+ "xmlns": "http://www.w3.org/2000/svg"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {
+ "id": "plus"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "id": "Icon",
+ "d": "M5.00004 2.08325V7.91659M2.08337 4.99992H7.91671",
+ "stroke": "currentColor",
+ "stroke-width": "1.5",
+ "stroke-linecap": "round",
+ "stroke-linejoin": "round"
+ },
+ "children": []
+ }
+ ]
+ }
+ ]
+ },
+ "name": "Plus02"
+}
\ No newline at end of file
diff --git a/web/app/components/base/icons/src/vender/line/general/Plus02.tsx b/web/app/components/base/icons/src/vender/line/general/Plus02.tsx
new file mode 100644
index 0000000000..5bc02de6e3
--- /dev/null
+++ b/web/app/components/base/icons/src/vender/line/general/Plus02.tsx
@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Plus02.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef, Omit>((
+ props,
+ ref,
+) => )
+
+Icon.displayName = 'Plus02'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/vender/line/general/index.ts b/web/app/components/base/icons/src/vender/line/general/index.ts
index b487685fdf..879e619902 100644
--- a/web/app/components/base/icons/src/vender/line/general/index.ts
+++ b/web/app/components/base/icons/src/vender/line/general/index.ts
@@ -19,6 +19,7 @@ export { default as LogOut04 } from './LogOut04'
export { default as Menu01 } from './Menu01'
export { default as Pin01 } from './Pin01'
export { default as Pin02 } from './Pin02'
+export { default as Plus02 } from './Plus02'
export { default as Plus } from './Plus'
export { default as SearchLg } from './SearchLg'
export { default as Settings01 } from './Settings01'
diff --git a/web/app/components/base/icons/src/vender/line/mediaAndDevices/Play.json b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Play.json
new file mode 100644
index 0000000000..d6af631a84
--- /dev/null
+++ b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Play.json
@@ -0,0 +1,39 @@
+{
+ "icon": {
+ "type": "element",
+ "isRootNode": true,
+ "name": "svg",
+ "attributes": {
+ "width": "12",
+ "height": "12",
+ "viewBox": "0 0 12 12",
+ "fill": "none",
+ "xmlns": "http://www.w3.org/2000/svg"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {
+ "id": "Icon"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "id": "Icon_2",
+ "d": "M2.5 2.49482C2.5 2.00924 2.5 1.76644 2.60125 1.63261C2.68945 1.51601 2.82426 1.44386 2.9702 1.43515C3.13772 1.42515 3.33973 1.55982 3.74376 1.82918L9.00154 5.33436C9.33538 5.55693 9.5023 5.66821 9.56047 5.80847C9.61133 5.9311 9.61133 6.06891 9.56047 6.19154C9.5023 6.3318 9.33538 6.44308 9.00154 6.66564L3.74376 10.1708C3.33973 10.4402 3.13772 10.5749 2.9702 10.5649C2.82426 10.5561 2.68945 10.484 2.60125 10.3674C2.5 10.2336 2.5 9.99077 2.5 9.50519V2.49482Z",
+ "stroke": "currentColor",
+ "stroke-width": "1.25",
+ "stroke-linecap": "round",
+ "stroke-linejoin": "round"
+ },
+ "children": []
+ }
+ ]
+ }
+ ]
+ },
+ "name": "Play"
+}
\ No newline at end of file
diff --git a/web/app/components/base/icons/src/vender/line/mediaAndDevices/Play.tsx b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Play.tsx
new file mode 100644
index 0000000000..f182f7a565
--- /dev/null
+++ b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Play.tsx
@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Play.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef, Omit>((
+ props,
+ ref,
+) => )
+
+Icon.displayName = 'Play'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/vender/line/mediaAndDevices/Stop.json b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Stop.json
new file mode 100644
index 0000000000..7d25397087
--- /dev/null
+++ b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Stop.json
@@ -0,0 +1,66 @@
+{
+ "icon": {
+ "type": "element",
+ "isRootNode": true,
+ "name": "svg",
+ "attributes": {
+ "width": "12",
+ "height": "12",
+ "viewBox": "0 0 12 12",
+ "fill": "none",
+ "xmlns": "http://www.w3.org/2000/svg"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {
+ "id": "Icon",
+ "clip-path": "url(#clip0_467_1645)"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "id": "Icon_2",
+ "d": "M1.5 3.9C1.5 3.05992 1.5 2.63988 1.66349 2.31901C1.8073 2.03677 2.03677 1.8073 2.31901 1.66349C2.63988 1.5 3.05992 1.5 3.9 1.5H8.1C8.94008 1.5 9.36012 1.5 9.68099 1.66349C9.96323 1.8073 10.1927 2.03677 10.3365 2.31901C10.5 2.63988 10.5 3.05992 10.5 3.9V8.1C10.5 8.94008 10.5 9.36012 10.3365 9.68099C10.1927 9.96323 9.96323 10.1927 9.68099 10.3365C9.36012 10.5 8.94008 10.5 8.1 10.5H3.9C3.05992 10.5 2.63988 10.5 2.31901 10.3365C2.03677 10.1927 1.8073 9.96323 1.66349 9.68099C1.5 9.36012 1.5 8.94008 1.5 8.1V3.9Z",
+ "stroke": "currentColor",
+ "stroke-width": "1.25",
+ "stroke-linecap": "round",
+ "stroke-linejoin": "round"
+ },
+ "children": []
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "defs",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "clipPath",
+ "attributes": {
+ "id": "clip0_467_1645"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "rect",
+ "attributes": {
+ "width": "12",
+ "height": "12",
+ "fill": "white"
+ },
+ "children": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "name": "Stop"
+}
\ No newline at end of file
diff --git a/web/app/components/base/icons/src/vender/line/mediaAndDevices/Stop.tsx b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Stop.tsx
new file mode 100644
index 0000000000..e501aaccd2
--- /dev/null
+++ b/web/app/components/base/icons/src/vender/line/mediaAndDevices/Stop.tsx
@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Stop.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef, Omit>((
+ props,
+ ref,
+) => )
+
+Icon.displayName = 'Stop'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/vender/line/mediaAndDevices/index.ts b/web/app/components/base/icons/src/vender/line/mediaAndDevices/index.ts
index ba693b054e..d78d0c7980 100644
--- a/web/app/components/base/icons/src/vender/line/mediaAndDevices/index.ts
+++ b/web/app/components/base/icons/src/vender/line/mediaAndDevices/index.ts
@@ -1,3 +1,5 @@
export { default as Microphone01 } from './Microphone01'
+export { default as Play } from './Play'
export { default as SlidersH } from './SlidersH'
export { default as Speaker } from './Speaker'
+export { default as Stop } from './Stop'
diff --git a/web/app/components/workflow/context.tsx b/web/app/components/workflow/context.tsx
index 1a82ce8f0b..9dff560b43 100644
--- a/web/app/components/workflow/context.tsx
+++ b/web/app/components/workflow/context.tsx
@@ -18,6 +18,7 @@ export type WorkflowContextValue = {
handleSelectedNodeIdChange: (nodeId: string) => void
selectedNode?: Node
handleAddNextNode: (prevNode: Node, nextNodeType: BlockEnum) => void
+ handleUpdateNodeData: (nodeId: string, data: Node['data']) => void
}
export const WorkflowContext = createContext({
@@ -26,5 +27,6 @@ export const WorkflowContext = createContext({
edges: [],
handleSelectedNodeIdChange: () => {},
handleAddNextNode: () => {},
+ handleUpdateNodeData: () => {},
})
export const useWorkflowContext = () => useContext(WorkflowContext)
diff --git a/web/app/components/workflow/hooks.ts b/web/app/components/workflow/hooks.ts
index c7fb940492..b111266d23 100644
--- a/web/app/components/workflow/hooks.ts
+++ b/web/app/components/workflow/hooks.ts
@@ -60,10 +60,21 @@ export const useWorkflow = (
})
}, [setNodes, setEdges])
+ const handleUpdateNodeData = useCallback((nodeId: string, data: Node['data']) => {
+ setNodes((oldNodes) => {
+ return produce(oldNodes, (draft) => {
+ const node = draft.find(node => node.id === nodeId)
+ if (node)
+ node.data = data
+ })
+ })
+ }, [setNodes])
+
return {
selectedNodeId,
selectedNode,
handleSelectedNodeIdChange,
handleAddNextNode,
+ handleUpdateNodeData,
}
}
diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx
index 4bcbafdcc6..fe01903479 100644
--- a/web/app/components/workflow/index.tsx
+++ b/web/app/components/workflow/index.tsx
@@ -73,6 +73,7 @@ const WorkflowWrap: FC = ({
handleSelectedNodeIdChange,
selectedNode,
handleAddNextNode,
+ handleUpdateNodeData,
} = useWorkflow(
nodes,
edges,
@@ -90,6 +91,7 @@ const WorkflowWrap: FC = ({
nodes,
edges,
handleAddNextNode,
+ handleUpdateNodeData,
}}>
diff --git a/web/app/components/workflow/node-control.tsx b/web/app/components/workflow/node-control.tsx
new file mode 100644
index 0000000000..b66220be87
--- /dev/null
+++ b/web/app/components/workflow/node-control.tsx
@@ -0,0 +1,42 @@
+import type { FC } from 'react'
+import { memo } from 'react'
+import {
+ DotsHorizontal,
+ Loading02,
+} from '@/app/components/base/icons/src/vender/line/general'
+import {
+ Play,
+ Stop,
+} from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
+
+type NodeControlProps = {
+ isRunning?: boolean
+}
+const NodeControl: FC = ({
+ isRunning,
+}) => {
+ return (
+
+ {
+ isRunning && (
+
+
+ RUNNING
+
+ )
+ }
+
+ {
+ isRunning
+ ?
+ :
+ }
+
+
+
+
+
+ )
+}
+
+export default memo(NodeControl)
diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx
index ffbdad4ee2..599e1d7034 100644
--- a/web/app/components/workflow/nodes/_base/node.tsx
+++ b/web/app/components/workflow/nodes/_base/node.tsx
@@ -12,8 +12,9 @@ import type { NodeProps } from 'reactflow'
import { getOutgoers } from 'reactflow'
import { useWorkflowContext } from '../../context'
import BlockSelector from '../../block-selector'
+import NodeControl from '../../node-control'
import BlockIcon from '../../block-icon'
-import { Plus } from '@/app/components/base/icons/src/vender/line/general'
+import { Plus02 } from '@/app/components/base/icons/src/vender/line/general'
type BaseNodeProps = {
children: ReactElement
@@ -48,7 +49,7 @@ const BaseNode: FC = ({
${open && '!flex'}
`}
>
-
+
)
@@ -63,6 +64,7 @@ const BaseNode: FC = ({
`}
onClick={() => handleSelectedNodeIdChange(nodeId || '')}
>
+