From 02b000e67f11614354648328f9a75bf834c2f555 Mon Sep 17 00:00:00 2001 From: Neelasha Bhattacharjee <46113280+Neelashab@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:26:12 +0000 Subject: [PATCH 1/7] feat: added run from here block to block menu --- src/ui/src/builder/settings/useBuilderSettingsActions.ts | 9 +++++++++ src/ui/src/builder/useComponentActions.ts | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/ui/src/builder/settings/useBuilderSettingsActions.ts b/src/ui/src/builder/settings/useBuilderSettingsActions.ts index 2431c0bf6..49be300f2 100644 --- a/src/ui/src/builder/settings/useBuilderSettingsActions.ts +++ b/src/ui/src/builder/settings/useBuilderSettingsActions.ts @@ -8,6 +8,7 @@ import { Option } from "@/components/shared/SharedMoreDropdown.vue"; export enum BuilderSettingsDropdownActions { Add = "add", + Run = "run", MoveUp = "moveUp", MoveDown = "moveDown", Cut = "cut", @@ -45,6 +46,7 @@ export function useBuilderSettingsActions( pasteComponent, copyComponent, isAddAllowed, + isRunAllowed, isCopyAllowed, isCutAllowed, isGoToParentAllowed, @@ -77,6 +79,7 @@ export function useBuilderSettingsActions( isAddEnabled: isAddAllowed(componentId.value), componentTypeName: wf.getComponentDefinition(component.type)?.name, toolkit: wf.getComponentDefinition(component.type)?.toolkit, + isRunEnabled: isRunAllowed(componentId.value), isMoveUpEnabled, isMoveDownEnabled, isCopyEnabled: isCopyAllowed(componentId.value), @@ -118,6 +121,12 @@ export function useBuilderSettingsActions( icon: "plus", disabled: !shortcutsInfo.value.isAddEnabled, }, + { + value: BuilderSettingsDropdownActions.Run, + label: "Run from here", + icon: "play", + disabled: !shortcutsInfo.value.isRunEnabled, + }, { value: BuilderSettingsDropdownActions.MoveUp, label: `Move up`, diff --git a/src/ui/src/builder/useComponentActions.ts b/src/ui/src/builder/useComponentActions.ts index 8945af5dc..fb0fd785f 100644 --- a/src/ui/src/builder/useComponentActions.ts +++ b/src/ui/src/builder/useComponentActions.ts @@ -446,6 +446,13 @@ export function useComponentActions( ); } + // NB-TODO: reevaluate logic + /** + * Whether the blueprint can be run from the target component. + */ + function isRunAllowed(targetId: Component["id"]): boolean { + return !isRoot(targetId); + } /** * Whether a component can be copied into the clipboard. */ @@ -1243,6 +1250,7 @@ export function useComponentActions( getUndoRedoSnapshot, setHandlerValue, isAddAllowed, + isRunAllowed, isCopyAllowed, isCutAllowed, isDeleteAllowed, From 47d5a1c0b9699d622c75ba20d6c085027d4b6b85 Mon Sep 17 00:00:00 2001 From: Neelasha Bhattacharjee <46113280+Neelashab@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:46:22 +0000 Subject: [PATCH 2/7] feat: updates to blueprint runners --- src/writer/blueprints.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/writer/blueprints.py b/src/writer/blueprints.py index 0089e5bb3..3617dd2d1 100644 --- a/src/writer/blueprints.py +++ b/src/writer/blueprints.py @@ -321,6 +321,21 @@ def run_branch( execution_environment, self, title=title ).run() + ## NB-TODO: Implement this and run_node_batch + ## Get correct name for start_node_id + def run_node( + self, + start_node_id: str, + execution_environment: Dict, + title: str = "Node execution"): + builder = GraphBuilder( + components=[self.session.session_component_tree.get_component(node_id)], + tools=writer.blocks.base_block.block_map + ) + return GraphRunner( + builder.build(), execution_environment, self, title=title + ).run() + def run_branch_batch( self, base_component_id: str, base_outcome: str, execution_environments: List[Dict] ): @@ -333,6 +348,9 @@ def run_branch_batch( results.append(result) return results + + ## TODO: Add run_blueprint_from_node_id + def run_blueprint( self, component_id: str, execution_environment: Dict, title="Blueprint execution" From 69b06559716560758ab85530160eef93aca3f539 Mon Sep 17 00:00:00 2001 From: Neelasha Bhattacharjee <46113280+Neelashab@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:22:09 +0000 Subject: [PATCH 3/7] feat: run from block - deleting unnecessary functions --- src/writer/blueprints.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/writer/blueprints.py b/src/writer/blueprints.py index 3617dd2d1..140c3653a 100644 --- a/src/writer/blueprints.py +++ b/src/writer/blueprints.py @@ -300,6 +300,7 @@ def _get_blueprint_nodes(self, component_id): current_node_id = node.parentId return [] + def run_branch( self, start_node_id: str, @@ -321,20 +322,6 @@ def run_branch( execution_environment, self, title=title ).run() - ## NB-TODO: Implement this and run_node_batch - ## Get correct name for start_node_id - def run_node( - self, - start_node_id: str, - execution_environment: Dict, - title: str = "Node execution"): - builder = GraphBuilder( - components=[self.session.session_component_tree.get_component(node_id)], - tools=writer.blocks.base_block.block_map - ) - return GraphRunner( - builder.build(), execution_environment, self, title=title - ).run() def run_branch_batch( self, base_component_id: str, base_outcome: str, execution_environments: List[Dict] From 718c7b5a39032434c30b9baf5ed701d633571f09 Mon Sep 17 00:00:00 2001 From: Neelasha Bhattacharjee <46113280+Neelashab@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:51:33 +0000 Subject: [PATCH 4/7] feat: add run from block - temporary comments for myself --- src/ui/src/components/blueprints/base/BlueprintToolbar.vue | 4 ++++ src/ui/src/composables/useBlueprintRun.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/src/ui/src/components/blueprints/base/BlueprintToolbar.vue b/src/ui/src/components/blueprints/base/BlueprintToolbar.vue index 510012996..a3cd576c5 100644 --- a/src/ui/src/components/blueprints/base/BlueprintToolbar.vue +++ b/src/ui/src/components/blueprints/base/BlueprintToolbar.vue @@ -19,6 +19,7 @@ const blueprintComponentId = inject(injectionKeys.componentId); const runBlueprintBtn = useTemplateRef("runBlueprintBtn"); +// NB STEP 3 - Get event handlers for blueprint run and stop const { run: handleRun, stop: handleStop, @@ -66,6 +67,7 @@ function jumpToComponent(componentId: string) { runBlueprintBtn.value?.toggleDropdown(false); } +// NB STEP 2 - Function called when blueprint button is clicked async function runBlueprint(componentId?: string) { runBlueprintBtn.value?.toggleDropdown(false); await handleRun(componentId); @@ -95,6 +97,8 @@ async function runBlueprint(componentId?: string) { Publish blueprint + + Date: Fri, 30 Jan 2026 17:36:00 +0000 Subject: [PATCH 5/7] feat: add run from block button --- .../settings/useBuilderSettingsActions.ts | 36 +++++++++++++++++-- src/ui/src/builder/useComponentActions.ts | 33 +++++++++++++++++ .../blueprints/base/BlueprintToolbar.vue | 2 +- src/ui/src/composables/useBlueprintRun.ts | 15 ++------ 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/ui/src/builder/settings/useBuilderSettingsActions.ts b/src/ui/src/builder/settings/useBuilderSettingsActions.ts index 49be300f2..c1d566997 100644 --- a/src/ui/src/builder/settings/useBuilderSettingsActions.ts +++ b/src/ui/src/builder/settings/useBuilderSettingsActions.ts @@ -43,6 +43,9 @@ export function useBuilderSettingsActions( moveComponentUp, moveComponentDown, cutComponent, + runBlueprintFromComponent, + stopBlueprintFromComponent, + isBlueprintRunning, pasteComponent, copyComponent, isAddAllowed, @@ -102,6 +105,22 @@ export function useBuilderSettingsActions( } } + async function handleRunBlueprint() { + try { + await runBlueprintFromComponent(componentId.value); + } catch (error) { + toasts.pushToast({ type: "error", message: String(error) }); + } + } + + async function handleStopBlueprint() { + try { + await stopBlueprintFromComponent(componentId.value); + } catch (error) { + toasts.pushToast({ type: "error", message: String(error) }); + } + } + function deleteComponent() { if (!shortcutsInfo.value.isDeleteEnabled) return; if (targetComponent) { @@ -123,9 +142,13 @@ export function useBuilderSettingsActions( }, { value: BuilderSettingsDropdownActions.Run, - label: "Run from here", - icon: "play", - disabled: !shortcutsInfo.value.isRunEnabled, + label: isBlueprintRunning(componentId.value) + ? "Stop run" + : "Run from here", + icon: isBlueprintRunning(componentId.value) ? "square" : "play", + disabled: isBlueprintRunning(componentId.value) + ? false + : !shortcutsInfo.value.isRunEnabled, }, { value: BuilderSettingsDropdownActions.MoveUp, @@ -187,6 +210,13 @@ export function useBuilderSettingsActions( case BuilderSettingsDropdownActions.Add: // Handled by callback break; + case BuilderSettingsDropdownActions.Run: + if (isBlueprintRunning(componentId.value)) { + handleStopBlueprint(); + } else { + handleRunBlueprint(); + } + break; case BuilderSettingsDropdownActions.MoveUp: moveComponentUp(componentId.value); break; diff --git a/src/ui/src/builder/useComponentActions.ts b/src/ui/src/builder/useComponentActions.ts index fb0fd785f..aa9d52f3a 100644 --- a/src/ui/src/builder/useComponentActions.ts +++ b/src/ui/src/builder/useComponentActions.ts @@ -5,6 +5,7 @@ import { useComponentClipboard } from "./useComponentClipboard"; import { COMPONENT_TYPES_ROOT } from "@/constants/component"; import { getComponentPage } from "@/composables/useComponentPage"; import { SHARED_BLUEPRINT_FLAG_VALUE } from "@/utils/sharedBlueprint"; +import { useBlueprintRun } from "@/composables/useBlueprintRun"; export function useComponentActions( wf: Core, @@ -420,6 +421,35 @@ export function useComponentActions( return removeComponentsSubtree(componentId); } + /** + * Runs a blueprint from a target component. + */ + async function runBlueprintFromComponent( + componentId: Component["id"], + ): Promise { + const { run } = useBlueprintRun(wf, ssbm, componentId); + await run(componentId); + } + + /** + * Stops a running blueprint. + */ + async function stopBlueprintFromComponent( + componentId: Component["id"], + ): Promise { + const { stop } = useBlueprintRun(wf, ssbm, componentId); + await stop(); + } + + /** + * Checks if a blueprint is currently running. + * Note: This checks if any blueprint is running, not specifically for the given component. + */ + function isBlueprintRunning(_componentId: Component["id"]): boolean { + const { isRunning } = useBlueprintRun(wf, ssbm, _componentId); + return isRunning.value !== null; + } + /** * Whether a target component is the root */ @@ -1234,6 +1264,9 @@ export function useComponentActions( copyComponent, pasteComponent, createAndInsertComponent, + runBlueprintFromComponent, + stopBlueprintFromComponent, + isBlueprintRunning, createAndInsertComponentsTree, removeComponentSubtree, removeComponentsSubtree, diff --git a/src/ui/src/components/blueprints/base/BlueprintToolbar.vue b/src/ui/src/components/blueprints/base/BlueprintToolbar.vue index a3cd576c5..aa20828c9 100644 --- a/src/ui/src/components/blueprints/base/BlueprintToolbar.vue +++ b/src/ui/src/components/blueprints/base/BlueprintToolbar.vue @@ -98,7 +98,7 @@ async function runBlueprint(componentId?: string) { Publish blueprint - + , ) { - const isRunning = ref(false); - async function run(branchId?: string) { - if (isRunning.value) return; - isRunning.value = true; - try { - await runBlueprint(wf, unref(blueprintComponentId), branchId); - } finally { - isRunning.value = false; - } + if (wfbm.activeBlueprintRunId.value) return; + await runBlueprint(wf, unref(blueprintComponentId), branchId); } - async function stop() { const activeRunId = wfbm.activeBlueprintRunId.value; if (!activeRunId) return; await stopBlueprintRun(wf, activeRunId); } - return { isRunning: readonly(isRunning), run, stop }; + return { isRunning: readonly(wfbm.activeBlueprintRunId), run, stop }; } export type BlueprintsRunListItem = { blueprintId: string; branchId: string }; From 2d1d6851d261761a94ec5e6851ec769e38db2ac5 Mon Sep 17 00:00:00 2001 From: Neelasha Bhattacharjee <46113280+Neelashab@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:40:43 +0000 Subject: [PATCH 6/7] feat: add run from block button - got rid of comments --- src/ui/src/builder/useComponentActions.ts | 1 - src/ui/src/components/blueprints/base/BlueprintToolbar.vue | 3 --- src/writer/blueprints.py | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ui/src/builder/useComponentActions.ts b/src/ui/src/builder/useComponentActions.ts index aa9d52f3a..e7e843d23 100644 --- a/src/ui/src/builder/useComponentActions.ts +++ b/src/ui/src/builder/useComponentActions.ts @@ -476,7 +476,6 @@ export function useComponentActions( ); } - // NB-TODO: reevaluate logic /** * Whether the blueprint can be run from the target component. */ diff --git a/src/ui/src/components/blueprints/base/BlueprintToolbar.vue b/src/ui/src/components/blueprints/base/BlueprintToolbar.vue index aa20828c9..a588aecae 100644 --- a/src/ui/src/components/blueprints/base/BlueprintToolbar.vue +++ b/src/ui/src/components/blueprints/base/BlueprintToolbar.vue @@ -19,7 +19,6 @@ const blueprintComponentId = inject(injectionKeys.componentId); const runBlueprintBtn = useTemplateRef("runBlueprintBtn"); -// NB STEP 3 - Get event handlers for blueprint run and stop const { run: handleRun, stop: handleStop, @@ -67,7 +66,6 @@ function jumpToComponent(componentId: string) { runBlueprintBtn.value?.toggleDropdown(false); } -// NB STEP 2 - Function called when blueprint button is clicked async function runBlueprint(componentId?: string) { runBlueprintBtn.value?.toggleDropdown(false); await handleRun(componentId); @@ -98,7 +96,6 @@ async function runBlueprint(componentId?: string) { Publish blueprint - Date: Fri, 30 Jan 2026 17:53:01 +0000 Subject: [PATCH 7/7] feat: add run from block button - updated spec to expect 9 dropdown options --- .../src/builder/settings/useBuilderSettingsActions.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/src/builder/settings/useBuilderSettingsActions.spec.ts b/src/ui/src/builder/settings/useBuilderSettingsActions.spec.ts index 0142f6c40..ec952bcf2 100644 --- a/src/ui/src/builder/settings/useBuilderSettingsActions.spec.ts +++ b/src/ui/src/builder/settings/useBuilderSettingsActions.spec.ts @@ -30,7 +30,7 @@ describe("useBuilderSettingsActions", () => { wfbm, ); - expect(dropdownOptions.value).toHaveLength(8); + expect(dropdownOptions.value).toHaveLength(9); for (const option of dropdownOptions.value) { expect(option.disabled).toBe(true); @@ -52,7 +52,7 @@ describe("useBuilderSettingsActions", () => { wfbm, ); - expect(dropdownOptions.value).toHaveLength(8); + expect(dropdownOptions.value).toHaveLength(9); expect( dropdownOptions.value.find( @@ -89,7 +89,7 @@ describe("useBuilderSettingsActions", () => { wfbm, ); - expect(dropdownOptions.value).toHaveLength(8); + expect(dropdownOptions.value).toHaveLength(9); expect( dropdownOptions.value.find(