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(