From 90e3f56a9dde7a71a197d38c1272a1fe93ed6e69 Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Wed, 10 Jul 2024 10:36:00 +0200 Subject: [PATCH] feat(editor): Add execution indication to canvas v2 (no-changelog) (#9984) --- packages/design-system/src/css/_tokens.scss | 1 + .../editor-ui/src/__tests__/data/canvas.ts | 6 +- .../src/components/canvas/Canvas.spec.ts | 3 +- .../src/components/canvas/Canvas.vue | 2 + .../src/components/canvas/WorkflowCanvas.vue | 26 ------ .../canvas/elements/edges/CanvasEdge.spec.ts | 52 ++++++++--- .../canvas/elements/edges/CanvasEdge.vue | 13 ++- .../canvas/elements/nodes/CanvasNode.spec.ts | 11 ++- .../CanvasNodeConfigurable.spec.ts | 8 +- .../render-types/CanvasNodeDefault.spec.ts | 21 ++++- .../nodes/render-types/CanvasNodeDefault.vue | 11 ++- .../parts/CanvasNodeStatusIcons.spec.ts | 40 ++++++++ .../parts/CanvasNodeStatusIcons.vue | 91 ++++++++++++------- .../src/composables/useCanvasMapping.spec.ts | 26 ++++++ .../src/composables/useCanvasMapping.ts | 16 +++- .../src/composables/useCanvasNode.spec.ts | 44 ++++----- .../src/composables/useCanvasNode.ts | 6 +- packages/editor-ui/src/types/canvas.ts | 3 +- 18 files changed, 260 insertions(+), 120 deletions(-) create mode 100644 packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.spec.ts diff --git a/packages/design-system/src/css/_tokens.scss b/packages/design-system/src/css/_tokens.scss index 993b63d646..8e4050821e 100644 --- a/packages/design-system/src/css/_tokens.scss +++ b/packages/design-system/src/css/_tokens.scss @@ -85,6 +85,7 @@ --color-node-executing-background: var(--color-primary-tint-3); --color-node-executing-other-background: var(--color-primary-tint-3); --color-node-pinned-border: var(--color-secondary); + --color-node-running-border: var(--color-primary); --node-type-main-color: var(--prim-gray-490); // Sticky diff --git a/packages/editor-ui/src/__tests__/data/canvas.ts b/packages/editor-ui/src/__tests__/data/canvas.ts index 58e497ef01..11253bfec2 100644 --- a/packages/editor-ui/src/__tests__/data/canvas.ts +++ b/packages/editor-ui/src/__tests__/data/canvas.ts @@ -10,7 +10,7 @@ export function createCanvasNodeData({ inputs = [], outputs = [], connections = { input: {}, output: {} }, - execution = {}, + execution = { running: false }, issues = { items: [], visible: false }, pinnedData = { count: 0, visible: false }, runData = { count: 0, visible: false }, @@ -55,7 +55,7 @@ export function createCanvasNodeProps({ label = 'Test Node', selected = false, data = {}, -} = {}) { +}: { id?: string; label?: string; selected?: boolean; data?: Partial } = {}) { return { id, label, @@ -69,7 +69,7 @@ export function createCanvasNodeProvide({ label = 'Test Node', selected = false, data = {}, -} = {}) { +}: { id?: string; label?: string; selected?: boolean; data?: Partial } = {}) { const props = createCanvasNodeProps({ id, label, selected, data }); return { [`${CanvasNodeKey}`]: { diff --git a/packages/editor-ui/src/components/canvas/Canvas.spec.ts b/packages/editor-ui/src/components/canvas/Canvas.spec.ts index 8c98f03f88..5b99630fdf 100644 --- a/packages/editor-ui/src/components/canvas/Canvas.spec.ts +++ b/packages/editor-ui/src/components/canvas/Canvas.spec.ts @@ -107,6 +107,7 @@ describe('Canvas', () => { }); await fireEvent.mouseUp(node, { view: window }); - expect(emitted()['update:node:position']).toEqual([['1', { x: 100, y: 100 }]]); + // Snap to 16px grid: 100 -> 96 + expect(emitted()['update:node:position']).toEqual([['1', { x: 96, y: 96 }]]); }); }); diff --git a/packages/editor-ui/src/components/canvas/Canvas.vue b/packages/editor-ui/src/components/canvas/Canvas.vue index 0fc66def2c..450b23557b 100644 --- a/packages/editor-ui/src/components/canvas/Canvas.vue +++ b/packages/editor-ui/src/components/canvas/Canvas.vue @@ -126,6 +126,8 @@ function onClickPane(event: MouseEvent) { :apply-changes="false" fit-view-on-init pan-on-scroll + snap-to-grid + :snap-grid="[16, 16]" :min-zoom="0.2" :max-zoom="2" data-test-id="canvas" diff --git a/packages/editor-ui/src/components/canvas/WorkflowCanvas.vue b/packages/editor-ui/src/components/canvas/WorkflowCanvas.vue index c2b99f1471..f92e21f1e2 100644 --- a/packages/editor-ui/src/components/canvas/WorkflowCanvas.vue +++ b/packages/editor-ui/src/components/canvas/WorkflowCanvas.vue @@ -47,30 +47,4 @@ const { elements, connections } = useCanvasMapping({ workflow, workflowObject }) position: relative; display: block; } - -.executionButtons { - position: absolute; - display: flex; - justify-content: center; - align-items: center; - left: 50%; - transform: translateX(-50%); - bottom: var(--spacing-l); - width: auto; - - @media (max-width: $breakpoint-2xs) { - bottom: 150px; - } - - button { - display: flex; - justify-content: center; - align-items: center; - margin-left: 0.625rem; - - &:first-child { - margin: 0; - } - } -} diff --git a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts index 46c8e69447..397033a5f0 100644 --- a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts +++ b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts @@ -1,21 +1,25 @@ import { fireEvent } from '@testing-library/vue'; -import CanvasEdge from './CanvasEdge.vue'; +import CanvasEdge, { type CanvasEdgeProps } from './CanvasEdge.vue'; import { createComponentRenderer } from '@/__tests__/render'; import { createTestingPinia } from '@pinia/testing'; import { setActivePinia } from 'pinia'; +import { Position } from '@vue-flow/core'; -const renderComponent = createComponentRenderer(CanvasEdge, { - props: { - sourceX: 0, - sourceY: 0, - sourcePosition: 'top', - targetX: 100, - targetY: 100, - targetPosition: 'bottom', - data: { - status: undefined, - }, +const DEFAULT_PROPS = { + sourceX: 0, + sourceY: 0, + sourcePosition: Position.Top, + targetX: 100, + targetY: 100, + targetPosition: Position.Bottom, + data: { + status: undefined, + source: { index: 0, type: 'main' }, + target: { index: 0, type: 'main' }, }, +} satisfies Partial; +const renderComponent = createComponentRenderer(CanvasEdge, { + props: DEFAULT_PROPS, }); beforeEach(() => { @@ -42,4 +46,28 @@ describe('CanvasEdge', () => { stroke: 'var(--color-foreground-xdark)', }); }); + + it('should correctly style a running connection', () => { + const { container } = renderComponent({ + props: { ...DEFAULT_PROPS, data: { ...DEFAULT_PROPS.data, status: 'running' } }, + }); + + const edge = container.querySelector('.vue-flow__edge-path'); + + expect(edge).toHaveStyle({ + stroke: 'var(--color-primary)', + }); + }); + + it('should correctly style a pinned connection', () => { + const { container } = renderComponent({ + props: { ...DEFAULT_PROPS, data: { ...DEFAULT_PROPS.data, status: 'pinned' } }, + }); + + const edge = container.querySelector('.vue-flow__edge-path'); + + expect(edge).toHaveStyle({ + stroke: 'var(--color-secondary)', + }); + }); }); diff --git a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue index 06beae7cae..c0d1f1e55f 100644 --- a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue +++ b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue @@ -4,16 +4,17 @@ import type { Connection, EdgeProps } from '@vue-flow/core'; import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@vue-flow/core'; import CanvasEdgeToolbar from './CanvasEdgeToolbar.vue'; import { computed, useCssModule } from 'vue'; +import type { CanvasConnectionData } from '@/types'; const emit = defineEmits<{ delete: [connection: Connection]; }>(); -const props = defineProps< - EdgeProps & { - hovered?: boolean; - } ->(); +export type CanvasEdgeProps = EdgeProps & { + hovered?: boolean; +}; + +const props = defineProps(); const $style = useCssModule(); @@ -27,6 +28,8 @@ const statusColor = computed(() => { return 'var(--color-success)'; } else if (status.value === 'pinned') { return 'var(--color-secondary)'; + } else if (status.value === 'running') { + return 'var(--color-primary)'; } else { return 'var(--color-foreground-xdark)'; } diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.spec.ts b/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.spec.ts index 6d426f23b6..b19f16c883 100644 --- a/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.spec.ts +++ b/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.spec.ts @@ -53,11 +53,14 @@ describe('CanvasNode', () => { ...createCanvasNodeProps({ data: { inputs: [ - { type: NodeConnectionType.Main }, - { type: NodeConnectionType.Main }, - { type: NodeConnectionType.Main }, + { type: NodeConnectionType.Main, index: 0 }, + { type: NodeConnectionType.Main, index: 0 }, + { type: NodeConnectionType.Main, index: 0 }, + ], + outputs: [ + { type: NodeConnectionType.Main, index: 0 }, + { type: NodeConnectionType.Main, index: 0 }, ], - outputs: [{ type: NodeConnectionType.Main }, { type: NodeConnectionType.Main }], }, }), }, diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeConfigurable.spec.ts b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeConfigurable.spec.ts index 5acf7ff855..217190d557 100644 --- a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeConfigurable.spec.ts +++ b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeConfigurable.spec.ts @@ -89,10 +89,10 @@ describe('CanvasNodeConfigurable', () => { ...createCanvasNodeProvide({ data: { inputs: [ - { type: NodeConnectionType.Main }, - { type: NodeConnectionType.AiTool }, - { type: NodeConnectionType.AiDocument, required: true }, - { type: NodeConnectionType.AiMemory, required: true }, + { type: NodeConnectionType.Main, index: 0 }, + { type: NodeConnectionType.AiTool, index: 0 }, + { type: NodeConnectionType.AiDocument, index: 0, required: true }, + { type: NodeConnectionType.AiMemory, index: 0, required: true }, ], }, }), diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.spec.ts b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.spec.ts index 24bfcf595e..ad05f860a6 100644 --- a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.spec.ts +++ b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.spec.ts @@ -32,7 +32,7 @@ describe('CanvasNodeDefault', () => { provide: { ...createCanvasNodeProvide({ data: { - outputs: [{ type: NodeConnectionType.Main }], + outputs: [{ type: NodeConnectionType.Main, index: 0 }], }, }), }, @@ -50,9 +50,9 @@ describe('CanvasNodeDefault', () => { ...createCanvasNodeProvide({ data: { outputs: [ - { type: NodeConnectionType.Main }, - { type: NodeConnectionType.Main }, - { type: NodeConnectionType.Main }, + { type: NodeConnectionType.Main, index: 0 }, + { type: NodeConnectionType.Main, index: 0 }, + { type: NodeConnectionType.Main, index: 0 }, ], }, }), @@ -118,4 +118,17 @@ describe('CanvasNodeDefault', () => { expect(getByText('Test Node').closest('.node')).not.toHaveClass('disabled'); }); }); + + describe('running', () => { + it('should apply running class when node is running', () => { + const { getByText } = renderComponent({ + global: { + provide: { + ...createCanvasNodeProvide({ data: { execution: { running: true } } }), + }, + }, + }); + expect(getByText('Test Node').closest('.node')).toHaveClass('running'); + }); + }); }); diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue index e12952c295..895b8a6579 100644 --- a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue +++ b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue @@ -17,6 +17,7 @@ const { isDisabled, isSelected, hasPinnedData, + executionRunning, hasRunData, hasIssues, } = useCanvasNode(); @@ -34,6 +35,7 @@ const classes = computed(() => { [$style.success]: hasRunData.value, [$style.error]: hasIssues.value, [$style.pinned]: hasPinnedData.value, + [$style.running]: executionRunning.value, }; }); @@ -94,6 +96,11 @@ const styles = computed(() => { &.disabled { border-color: var(--color-canvas-node-disabled-border-color, var(--color-foreground-base)); } + + &.running { + background-color: var(--color-node-executing-background); + border-color: var(--color-canvas-node-running-border-color, var(--color-node-running-border)); + } } .label { @@ -108,7 +115,7 @@ const styles = computed(() => { .statusIcons { position: absolute; - top: calc(var(--canvas-node--height) - 24px); - right: var(--spacing-xs); + bottom: var(--spacing-2xs); + right: var(--spacing-2xs); } diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.spec.ts b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.spec.ts new file mode 100644 index 0000000000..5327d71524 --- /dev/null +++ b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.spec.ts @@ -0,0 +1,40 @@ +import CanvasNodeStatusIcons from './CanvasNodeStatusIcons.vue'; +import { createComponentRenderer } from '@/__tests__/render'; +import { createCanvasNodeProvide } from '@/__tests__/data'; +import { createTestingPinia } from '@pinia/testing'; + +const renderComponent = createComponentRenderer(CanvasNodeStatusIcons, { + pinia: createTestingPinia(), +}); + +describe('CanvasNodeStatusIcons', () => { + it('should render correctly for a pinned node', () => { + const { getByTestId } = renderComponent({ + global: { + provide: createCanvasNodeProvide({ data: { pinnedData: { count: 5, visible: true } } }), + }, + }); + + expect(getByTestId('canvas-node-status-pinned')).toHaveTextContent('5'); + }); + + it('should render correctly for a running node', () => { + const { getByTestId } = renderComponent({ + global: { + provide: createCanvasNodeProvide({ data: { execution: { running: true } } }), + }, + }); + + expect(getByTestId('canvas-node-status-running')).toBeInTheDocument(); + }); + + it('should render correctly for a node that ran successfully', () => { + const { getByTestId } = renderComponent({ + global: { + provide: createCanvasNodeProvide({ data: { runData: { count: 15, visible: true } } }), + }, + }); + + expect(getByTestId('canvas-node-status-success')).toHaveTextContent('15'); + }); +}); diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.vue b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.vue index 02b8ec4edb..6c5136bcdf 100644 --- a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.vue +++ b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/parts/CanvasNodeStatusIcons.vue @@ -13,6 +13,7 @@ const { hasIssues, executionStatus, executionWaiting, + executionRunning, hasRunData, runDataCount, } = useCanvasNode(); @@ -21,46 +22,62 @@ const hideNodeIssues = computed(() => false); // @TODO Implement this diff --git a/packages/editor-ui/src/composables/useCanvasMapping.spec.ts b/packages/editor-ui/src/composables/useCanvasMapping.spec.ts index cb3b0aadb1..55ccebe048 100644 --- a/packages/editor-ui/src/composables/useCanvasMapping.spec.ts +++ b/packages/editor-ui/src/composables/useCanvasMapping.spec.ts @@ -15,6 +15,7 @@ import { } from '@/__tests__/mocks'; import { MANUAL_TRIGGER_NODE_TYPE, SET_NODE_TYPE } from '@/constants'; import { useNodeTypesStore } from '@/stores/nodeTypes.store'; +import { useWorkflowsStore } from '../stores/workflows.store'; beforeEach(() => { const pinia = createPinia(); @@ -80,6 +81,7 @@ describe('useCanvasMapping', () => { disabled: false, execution: { status: 'new', + running: false, waiting: undefined, }, issues: { @@ -137,6 +139,27 @@ describe('useCanvasMapping', () => { expect(elements.value[0]?.data?.disabled).toEqual(true); }); + it('should handle execution state', () => { + const manualTriggerNode = mockNode({ + name: 'Manual Trigger', + type: MANUAL_TRIGGER_NODE_TYPE, + disabled: true, + }); + const workflow = mock({ + nodes: [manualTriggerNode], + }); + const workflowObject = createTestWorkflowObject(workflow); + + useWorkflowsStore().addExecutingNode(manualTriggerNode.name); + + const { elements } = useCanvasMapping({ + workflow: ref(workflow), + workflowObject: ref(workflowObject) as Ref, + }); + + expect(elements.value[0]?.data?.execution.running).toEqual(true); + }); + it('should handle input and output connections', () => { const [manualTriggerNode, setNode] = mockNodes.slice(0, 2); const workflow = mock({ @@ -217,6 +240,7 @@ describe('useCanvasMapping', () => { target: setNode.id, targetHandle: `inputs/${NodeConnectionType.Main}/0`, type: 'canvas-edge', + animated: false, }, ]); }); @@ -264,6 +288,7 @@ describe('useCanvasMapping', () => { target: setNode.id, targetHandle: `inputs/${NodeConnectionType.AiTool}/0`, type: 'canvas-edge', + animated: false, }, { data: { @@ -285,6 +310,7 @@ describe('useCanvasMapping', () => { target: setNode.id, targetHandle: `inputs/${NodeConnectionType.AiDocument}/1`, type: 'canvas-edge', + animated: false, }, ]); }); diff --git a/packages/editor-ui/src/composables/useCanvasMapping.ts b/packages/editor-ui/src/composables/useCanvasMapping.ts index 1f73e2f02d..c8b3afb565 100644 --- a/packages/editor-ui/src/composables/useCanvasMapping.ts +++ b/packages/editor-ui/src/composables/useCanvasMapping.ts @@ -110,6 +110,13 @@ export function useCanvasMapping({ }, {}), ); + const nodeExecutionRunningById = computed(() => + workflow.value.nodes.reduce>((acc, node) => { + acc[node.id] = workflowsStore.isNodeExecuting(node.name); + return acc; + }, {}), + ); + const nodeExecutionStatusById = computed(() => workflow.value.nodes.reduce>((acc, node) => { acc[node.id] = @@ -221,6 +228,7 @@ export function useCanvasMapping({ execution: { status: nodeExecutionStatusById.value[node.id], waiting: nodeExecutionWaitingById.value[node.id], + running: nodeExecutionRunningById.value[node.id], }, runData: { count: nodeExecutionRunDataById.value[node.id]?.length ?? 0, @@ -255,6 +263,7 @@ export function useCanvasMapping({ data, type, label, + animated: data.status === 'running', }; }); }); @@ -266,7 +275,12 @@ export function useCanvasMapping({ let status: CanvasConnectionData['status']; if (fromNode) { - if (nodePinnedDataById.value[fromNode.id] && nodeExecutionRunDataById.value[fromNode.id]) { + if (nodeExecutionRunningById.value[fromNode.id]) { + status = 'running'; + } else if ( + nodePinnedDataById.value[fromNode.id] && + nodeExecutionRunDataById.value[fromNode.id] + ) { status = 'pinned'; } else if (nodeHasIssuesById.value[fromNode.id]) { status = 'error'; diff --git a/packages/editor-ui/src/composables/useCanvasNode.spec.ts b/packages/editor-ui/src/composables/useCanvasNode.spec.ts index 1983ffd5bb..f7393c4aa8 100644 --- a/packages/editor-ui/src/composables/useCanvasNode.spec.ts +++ b/packages/editor-ui/src/composables/useCanvasNode.spec.ts @@ -1,5 +1,6 @@ import { useCanvasNode } from '@/composables/useCanvasNode'; import { inject, ref } from 'vue'; +import type { CanvasNodeInjectionData } from '../types'; vi.mock('vue', async () => { const actual = await vi.importActual('vue'); @@ -27,38 +28,38 @@ describe('useCanvasNode', () => { expect(result.hasIssues.value).toBe(false); expect(result.executionStatus.value).toBeUndefined(); expect(result.executionWaiting.value).toBeUndefined(); + expect(result.executionRunning.value).toBe(false); }); it('should return node data when node is provided', () => { const node = { - data: { - value: { - id: 'node1', - type: 'nodeType1', - typeVersion: 1, - disabled: true, - inputs: ['input1'], - outputs: ['output1'], - connections: { input: { '0': ['node2'] }, output: {} }, - issues: { items: ['issue1'], visible: true }, - execution: { status: 'running', waiting: false }, - runData: { count: 1, visible: true }, - pinnedData: { count: 1, visible: true }, - renderType: 'default', - }, - }, + data: ref({ + id: 'node1', + type: 'nodeType1', + typeVersion: 1, + disabled: true, + inputs: [{ type: 'main', index: 0 }], + outputs: [{ type: 'main', index: 0 }], + connections: { input: { '0': [] }, output: {} }, + issues: { items: ['issue1'], visible: true }, + execution: { status: 'running', waiting: 'waiting', running: true }, + runData: { count: 1, visible: true }, + pinnedData: { count: 1, visible: true }, + renderType: 'default', + }), + id: ref('1'), label: ref('Node 1'), selected: ref(true), - }; + } satisfies Partial; vi.mocked(inject).mockReturnValue(node); const result = useCanvasNode(); expect(result.label.value).toBe('Node 1'); - expect(result.inputs.value).toEqual(['input1']); - expect(result.outputs.value).toEqual(['output1']); - expect(result.connections.value).toEqual({ input: { '0': ['node2'] }, output: {} }); + expect(result.inputs.value).toEqual([{ type: 'main', index: 0 }]); + expect(result.outputs.value).toEqual([{ type: 'main', index: 0 }]); + expect(result.connections.value).toEqual({ input: { '0': [] }, output: {} }); expect(result.isDisabled.value).toBe(true); expect(result.isSelected.value).toBe(true); expect(result.pinnedDataCount.value).toBe(1); @@ -68,6 +69,7 @@ describe('useCanvasNode', () => { expect(result.issues.value).toEqual(['issue1']); expect(result.hasIssues.value).toBe(true); expect(result.executionStatus.value).toBe('running'); - expect(result.executionWaiting.value).toBe(false); + expect(result.executionWaiting.value).toBe('waiting'); + expect(result.executionRunning.value).toBe(true); }); }); diff --git a/packages/editor-ui/src/composables/useCanvasNode.ts b/packages/editor-ui/src/composables/useCanvasNode.ts index 2a7a29ef91..2966cd0d9c 100644 --- a/packages/editor-ui/src/composables/useCanvasNode.ts +++ b/packages/editor-ui/src/composables/useCanvasNode.ts @@ -21,7 +21,9 @@ export function useCanvasNode() { connections: { input: {}, output: {} }, issues: { items: [], visible: false }, pinnedData: { count: 0, visible: false }, - execution: {}, + execution: { + running: false, + }, runData: { count: 0, visible: false }, renderType: 'default', }, @@ -45,6 +47,7 @@ export function useCanvasNode() { const executionStatus = computed(() => data.value.execution.status); const executionWaiting = computed(() => data.value.execution.waiting); + const executionRunning = computed(() => data.value.execution.running); const runDataCount = computed(() => data.value.runData.count); const hasRunData = computed(() => data.value.runData.visible); @@ -65,5 +68,6 @@ export function useCanvasNode() { hasIssues, executionStatus, executionWaiting, + executionRunning, }; } diff --git a/packages/editor-ui/src/types/canvas.ts b/packages/editor-ui/src/types/canvas.ts index ab025ba3ff..fe68cabc44 100644 --- a/packages/editor-ui/src/types/canvas.ts +++ b/packages/editor-ui/src/types/canvas.ts @@ -58,6 +58,7 @@ export interface CanvasElementData { execution: { status?: ExecutionStatus; waiting?: string; + running: boolean; }; runData: { count: number; @@ -72,7 +73,7 @@ export interface CanvasConnectionData { source: CanvasConnectionPort; target: CanvasConnectionPort; fromNodeName?: string; - status?: 'success' | 'error' | 'pinned'; + status?: 'success' | 'error' | 'pinned' | 'running'; } export type CanvasConnection = DefaultEdge;