diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts
index aec2889070..04daff95d5 100644
--- a/packages/editor-ui/src/Interface.ts
+++ b/packages/editor-ui/src/Interface.ts
@@ -182,6 +182,7 @@ export interface IAiDataContent {
metadata: {
executionTime: number;
startTime: number;
+ executionId?: string;
};
}
diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue
index a3d354d7e7..79f2072b30 100644
--- a/packages/editor-ui/src/components/RunData.vue
+++ b/packages/editor-ui/src/components/RunData.vue
@@ -58,12 +58,13 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useNodeType } from '@/composables/useNodeType';
import { useToast } from '@/composables/useToast';
-import { isEqual, isObject } from 'lodash-es';
+import { get, isEqual, isObject } from 'lodash-es';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { useRootStore } from '@/stores/root.store';
import RunDataPinButton from '@/components/RunDataPinButton.vue';
import { getGenericHints } from '@/utils/nodeViewUtils';
+import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
const LazyRunDataTable = defineAsyncComponent(
async () => await import('@/components/RunDataTable.vue'),
@@ -180,6 +181,7 @@ export default defineComponent({
const { isSubNodeType } = useNodeType({
node,
});
+ const { openExecutionInNewTab } = useExecutionHelpers();
return {
...useToast(),
@@ -187,6 +189,7 @@ export default defineComponent({
nodeHelpers,
pinnedData,
isSubNodeType,
+ openExecutionInNewTab,
};
},
data() {
@@ -220,6 +223,43 @@ export default defineComponent({
useSourceControlStore,
useRootStore,
),
+ subWorkflowData(): { executionId: string; workflowId?: string } | null {
+ if (!this.node) {
+ return null;
+ }
+ const metadata = get(this.workflowRunData, [this.node.name, this.runIndex, 'metadata'], null);
+ if (metadata?.executionId) {
+ return {
+ executionId: metadata?.executionId,
+ workflowId: metadata?.workflowId,
+ };
+ }
+ return null;
+ },
+ itemSubWorkflowData(): Array<{
+ metadata?: { executionId?: string; workflowId?: string };
+ itemIndex: number;
+ }> {
+ const items = this.getData(this.runIndex, this.currentOutputIndex);
+ return items
+ .map((item, itemIndex) => {
+ return {
+ metadata: item.metadata,
+ itemIndex,
+ };
+ })
+ .filter((item) => {
+ return !!item.metadata;
+ });
+ },
+ hasInputOverwrite(): boolean {
+ if (!this.node) {
+ return false;
+ }
+ const taskData = this.nodeHelpers.getNodeTaskData(this.node, this.runIndex);
+ if (taskData === null) return false;
+ return !!taskData.inputOverride;
+ },
isReadOnlyRoute() {
return this.$route?.meta?.readOnlyCanvas === true;
},
@@ -654,6 +694,15 @@ export default defineComponent({
this.hidePinDataDiscoveryTooltip();
},
methods: {
+ getData(
+ runIndex: number,
+ outputIndex: number,
+ connectionType: NodeConnectionType = NodeConnectionType.Main,
+ ) {
+ const rawInputData = this.getRawInputData(runIndex, outputIndex, connectionType);
+ const pinOrLiveData = this.getPinDataOrLiveData(rawInputData);
+ return this.getFilteredData(pinOrLiveData);
+ },
getResolvedNodeOutputs() {
if (this.node && this.nodeType) {
const workflowNode = this.workflow.getNode(this.node.name);
@@ -1383,6 +1432,32 @@ export default defineComponent({
+
+
{
}}
+
+ EXECUTION ID {{ runMeta?.executionId }}
+
{{
$locale.baseText('runData.aiContentBlock.tokens', {
diff --git a/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue b/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue
index e76ab8241a..b2576b908d 100644
--- a/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue
+++ b/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue
@@ -12,7 +12,6 @@ import {
CHAT_TRIGGER_NODE_TYPE,
MANUAL_CHAT_TRIGGER_NODE_TYPE,
MODAL_CONFIRM,
- VIEWS,
WORKFLOW_LM_CHAT_MODAL_KEY,
} from '@/constants';
@@ -58,6 +57,7 @@ import { usePinnedData } from '@/composables/usePinnedData';
import { get, last } from 'lodash-es';
import { isEmpty } from '@/utils/typesUtils';
import { chatEventBus } from '@n8n/chat/event-buses';
+import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
const LazyRunDataAi = defineAsyncComponent(
async () => await import('@/components/RunDataAi/RunDataAi.vue'),
@@ -82,6 +82,8 @@ const workflowsStore = useWorkflowsStore();
const nodeTypesStore = useNodeTypesStore();
const uiStore = useUIStore();
+const executionHelpers = useExecutionHelpers();
+
const { showError } = useToast();
const messages: Ref = ref([]);
const currentSessionId = ref(String(Date.now()));
@@ -428,11 +430,7 @@ async function sendMessage(message: string, files?: File[]) {
}
function displayExecution(executionId: string) {
- const route = router.resolve({
- name: VIEWS.EXECUTION_PREVIEW,
- params: { name: workflow.value.id, executionId },
- });
- window.open(route.href, '_blank');
+ executionHelpers.openExecutionInNewTab(executionId, workflow.value.id);
}
function isTextMessage(message: ChatMessage): message is ChatMessageText {
return message.type === 'text' || !message.type;
diff --git a/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue b/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue
index f23dbbc4ac..1b258f3e53 100644
--- a/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue
+++ b/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue
@@ -2,7 +2,7 @@
import { ref, computed, useCssModule } from 'vue';
import type { ExecutionSummary } from 'n8n-workflow';
import { useI18n } from '@/composables/useI18n';
-import { VIEWS, WAIT_TIME_UNLIMITED } from '@/constants';
+import { WAIT_TIME_UNLIMITED } from '@/constants';
import { useRouter } from 'vue-router';
import { convertToDisplayDate } from '@/utils/formatters/dateFormatter';
import { i18n as locale } from '@/plugins/i18n';
@@ -138,11 +138,7 @@ function formatDate(fullDate: Date | string | number) {
}
function displayExecution() {
- const route = router.resolve({
- name: VIEWS.EXECUTION_PREVIEW,
- params: { name: props.execution.workflowId, executionId: props.execution.id },
- });
- window.open(route.href, '_blank');
+ executionHelpers.openExecutionInNewTab(props.execution.id, props.execution.workflowId);
}
function onStopExecution() {
diff --git a/packages/editor-ui/src/composables/useExecutionHelpers.ts b/packages/editor-ui/src/composables/useExecutionHelpers.ts
index c87006c92a..a68cfd85bf 100644
--- a/packages/editor-ui/src/composables/useExecutionHelpers.ts
+++ b/packages/editor-ui/src/composables/useExecutionHelpers.ts
@@ -1,6 +1,8 @@
import type { ExecutionSummary } from 'n8n-workflow';
import { convertToDisplayDate } from '@/utils/formatters/dateFormatter';
import { useI18n } from '@/composables/useI18n';
+import { useRouter } from 'vue-router';
+import { VIEWS } from '@/constants';
export interface IExecutionUIData {
name: string;
@@ -14,6 +16,7 @@ export interface IExecutionUIData {
export function useExecutionHelpers() {
const i18n = useI18n();
+ const router = useRouter();
function getUIDetails(execution: ExecutionSummary): IExecutionUIData {
const status = {
@@ -69,9 +72,18 @@ export function useExecutionHelpers() {
return ['crashed', 'error'].includes(execution.status) && !execution.retrySuccessId;
}
+ function openExecutionInNewTab(executionId: string, workflowId?: string) {
+ const route = router.resolve({
+ name: VIEWS.EXECUTION_PREVIEW,
+ params: { name: workflowId, executionId },
+ });
+ window.open(route.href, '_blank');
+ }
+
return {
getUIDetails,
formatDate,
isExecutionRetriable,
+ openExecutionInNewTab,
};
}
diff --git a/packages/editor-ui/src/composables/useNodeHelpers.ts b/packages/editor-ui/src/composables/useNodeHelpers.ts
index f2f33a5ef7..42a573bdf2 100644
--- a/packages/editor-ui/src/composables/useNodeHelpers.ts
+++ b/packages/editor-ui/src/composables/useNodeHelpers.ts
@@ -565,6 +565,29 @@ export function useNodeHelpers() {
}
}
+ function getNodeTaskData(node: INodeUi | null, runIndex = 0) {
+ if (node === null) {
+ return null;
+ }
+ if (workflowsStore.getWorkflowExecution === null) {
+ return null;
+ }
+
+ const executionData = workflowsStore.getWorkflowExecution.data;
+ if (!executionData?.resultData) {
+ // unknown status
+ return null;
+ }
+ const runData = executionData.resultData.runData;
+
+ const taskData = get(runData, [node.name, runIndex]);
+ if (!taskData) {
+ return null;
+ }
+
+ return taskData;
+ }
+
function getNodeInputData(
node: INodeUi | null,
runIndex = 0,
@@ -582,22 +605,8 @@ export function useNodeHelpers() {
runIndex = runIndex - 1;
}
- if (node === null) {
- return [];
- }
- if (workflowsStore.getWorkflowExecution === null) {
- return [];
- }
-
- const executionData = workflowsStore.getWorkflowExecution.data;
- if (!executionData?.resultData) {
- // unknown status
- return [];
- }
- const runData = executionData.resultData.runData;
-
- const taskData = get(runData, [node.name, runIndex]);
- if (!taskData) {
+ const taskData = getNodeTaskData(node, runIndex);
+ if (taskData === null) {
return [];
}
@@ -1281,5 +1290,6 @@ export function useNodeHelpers() {
removeConnectionByConnectionInfo,
addPinDataConnections,
removePinDataConnections,
+ getNodeTaskData,
};
}