{{ message.text }}
@@ -80,21 +81,29 @@
v-model="currentMessage"
class="message-input"
type="textarea"
+ :minlength="1"
ref="inputField"
+ m
:placeholder="$locale.baseText('chat.window.chat.placeholder')"
data-test-id="workflow-chat-input"
@keydown.stop="updated"
/>
-
+
+
+
+ {{ $locale.baseText('chat.window.chat.provideMessage') }}
+
+
{{ $locale.baseText('chatEmbed.infoTip.description') }}
@@ -218,25 +227,22 @@ export default defineComponent({
}
},
async sendChatMessage(message: string) {
+ if (this.currentMessage.trim() === '') {
+ this.showError(
+ new Error(this.$locale.baseText('chat.window.chat.provideMessage')),
+ this.$locale.baseText('chat.window.chat.emptyChatMessage'),
+ );
+ return;
+ }
this.messages.push({
text: message,
sender: 'user',
} as ChatMessage);
this.currentMessage = '';
-
+ await this.$nextTick();
+ this.scrollToLatestMessage();
await this.startWorkflowWithMessage(message);
-
- // Scroll to bottom
- const containerRef = this.$refs.messagesContainer as HTMLElement | undefined;
- if (containerRef) {
- // Wait till message got added else it will not scroll correctly
- await this.$nextTick();
- containerRef.scrollTo({
- top: containerRef.scrollHeight,
- behavior: 'smooth',
- });
- }
},
setConnectedNode() {
@@ -477,10 +483,20 @@ export default defineComponent({
void this.$nextTick(() => {
that.setNode();
+ this.scrollToLatestMessage();
});
}
}, 500);
},
+ scrollToLatestMessage() {
+ const containerRef = this.$refs.messageContainer as HTMLElement[] | undefined;
+ if (containerRef) {
+ containerRef[containerRef.length - 1]?.scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ }
+ },
closeDialog() {
this.modalBus.emit('close');
void this.externalHooks.run('workflowSettings.dialogVisibleChanged', {
diff --git a/packages/editor-ui/src/composables/useGlobalLinkActions.ts b/packages/editor-ui/src/composables/useGlobalLinkActions.ts
index 6302ff1455..801fe1f71b 100644
--- a/packages/editor-ui/src/composables/useGlobalLinkActions.ts
+++ b/packages/editor-ui/src/composables/useGlobalLinkActions.ts
@@ -2,11 +2,12 @@
* Creates event listeners for `data-action` attribute to allow for actions to be called from locale without using
* unsafe onclick attribute
*/
-import { reactive, computed, onMounted, onUnmounted, getCurrentInstance } from 'vue';
+import { reactive, computed, onMounted, onUnmounted } from 'vue';
import { globalLinkActionsEventBus } from '@/event-bus';
const state = reactive({
customActions: {} as Record,
+ delegatedClickHandler: null as null | ((e: MouseEvent) => void),
});
export default () => {
@@ -56,15 +57,17 @@ export default () => {
}));
onMounted(() => {
- const instance = getCurrentInstance();
+ if (state.delegatedClickHandler) return;
+
+ state.delegatedClickHandler = delegateClick;
window.addEventListener('click', delegateClick);
globalLinkActionsEventBus.on('registerGlobalLinkAction', registerCustomAction);
});
onUnmounted(() => {
- const instance = getCurrentInstance();
window.removeEventListener('click', delegateClick);
+ state.delegatedClickHandler = null;
globalLinkActionsEventBus.off('registerGlobalLinkAction', registerCustomAction);
});
diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts
index 1e92c89c0e..2c43be87f7 100644
--- a/packages/editor-ui/src/constants.ts
+++ b/packages/editor-ui/src/constants.ts
@@ -127,6 +127,9 @@ export const MICROSOFT_EXCEL_NODE_TYPE = 'n8n-nodes-base.microsoftExcel';
export const MANUAL_TRIGGER_NODE_TYPE = 'n8n-nodes-base.manualTrigger';
export const MANUAL_CHAT_TRIGGER_NODE_TYPE = '@n8n/n8n-nodes-langchain.manualChatTrigger';
export const AGENT_NODE_TYPE = '@n8n/n8n-nodes-langchain.agent';
+export const OPEN_AI_ASSISTANT_NODE_TYPE = '@n8n/n8n-nodes-langchain.openAiAssistant';
+export const BASIC_CHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.chainLlm';
+export const QA_CHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.chainRetrievalQa';
export const MICROSOFT_TEAMS_NODE_TYPE = 'n8n-nodes-base.microsoftTeams';
export const N8N_NODE_TYPE = 'n8n-nodes-base.n8n';
export const NO_OP_NODE_TYPE = 'n8n-nodes-base.noOp';
diff --git a/packages/editor-ui/src/mixins/workflowHelpers.ts b/packages/editor-ui/src/mixins/workflowHelpers.ts
index f1bfbaf2a5..e2e24c1818 100644
--- a/packages/editor-ui/src/mixins/workflowHelpers.ts
+++ b/packages/editor-ui/src/mixins/workflowHelpers.ts
@@ -572,7 +572,8 @@ export const workflowHelpers = defineComponent({
workflow.nodes[nodeName].disabled !== true &&
workflow.nodes[nodeName].type === WEBHOOK_NODE_TYPE
) {
- checkWebhook = [nodeName, ...checkWebhook, ...workflow.getChildNodes(nodeName)];
+ const childNodes = workflow.getChildNodes(nodeName);
+ checkWebhook = [nodeName, ...checkWebhook, ...childNodes];
}
}
@@ -582,8 +583,14 @@ export const workflowHelpers = defineComponent({
// If no webhook nodes got found try to find another trigger node
const startNode = workflow.getStartNode();
if (startNode !== undefined) {
- checkNodes = workflow.getChildNodes(startNode.name);
- checkNodes.push(startNode.name);
+ checkNodes = [...workflow.getChildNodes(startNode.name), startNode.name];
+
+ // For the short-listed checkNodes, we also need to check them for any
+ // connected sub-nodes
+ for (const nodeName of checkNodes) {
+ const childNodes = workflow.getParentNodes(nodeName, 'ALL_NON_MAIN');
+ checkNodes.push(...childNodes);
+ }
}
}
}
diff --git a/packages/editor-ui/src/mixins/workflowRun.ts b/packages/editor-ui/src/mixins/workflowRun.ts
index 574cd71c8d..08f4befb05 100644
--- a/packages/editor-ui/src/mixins/workflowRun.ts
+++ b/packages/editor-ui/src/mixins/workflowRun.ts
@@ -83,6 +83,7 @@ export const workflowRun = defineComponent({
try {
// Check first if the workflow has any issues before execute it
+ this.refreshNodeIssues();
const issuesExist = this.workflowsStore.nodesIssuesExist;
if (issuesExist) {
// If issues exist get all of the issues of all nodes
@@ -112,7 +113,9 @@ export const workflowRun = defineComponent({
};
for (const nodeIssue of nodeIssues) {
- errorMessages.push(`${nodeName}: ${nodeIssue}`);
+ errorMessages.push(
+ `${nodeName}: ${nodeIssue}`,
+ );
trackNodeIssue.error = trackNodeIssue.error.concat(', ', nodeIssue);
}
trackNodeIssues.push(trackNodeIssue);
diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json
index 0e3b10ab77..d7cd62ab1b 100644
--- a/packages/editor-ui/src/plugins/i18n/locales/en.json
+++ b/packages/editor-ui/src/plugins/i18n/locales/en.json
@@ -140,6 +140,8 @@
"chat.window.noExecution": "Nothing got executed yet",
"chat.window.chat.placeholder": "Type in message",
"chat.window.chat.sendButtonText": "Send",
+ "chat.window.chat.provideMessage": "Please provide a message",
+ "chat.window.chat.emptyChatMessage": "Empty chat message",
"chat.window.chat.chatMessageOptions.reuseMessage": "Reuse Message",
"chat.window.chat.chatMessageOptions.repostMessage": "Repost Message",
"chat.window.chat.chatMessageOptions.executionId": "Execution ID",
@@ -925,6 +927,7 @@
"nodeCreator.aiPanel.aiOtherNodesDescription": "Embeddings, Vector Stores, LLMs and other AI nodes",
"nodeCreator.aiPanel.selectAiNode": "Select an Al Node to add to your workflow",
"nodeCreator.aiPanel.nodesForAi": "Build autonomous agents, summarize or interrogate documents, etc.",
+ "nodeCreator.aiPanel.newTag": "New",
"nodeCreator.aiPanel.langchainAiNodes": "Advanced AI",
"nodeCreator.aiPanel.title": "When should this workflow run?",
"nodeCreator.aiPanel.infoBox": "Check out our templates for workflow examples and inspiration.",
diff --git a/packages/editor-ui/src/utils/nodeViewUtils.ts b/packages/editor-ui/src/utils/nodeViewUtils.ts
index b22ea6275b..841bf2c9f5 100644
--- a/packages/editor-ui/src/utils/nodeViewUtils.ts
+++ b/packages/editor-ui/src/utils/nodeViewUtils.ts
@@ -282,9 +282,9 @@ export const getInputNameOverlay = (
label.innerHTML += ' *';
}
label.classList.add('node-input-endpoint-label');
+ label.classList.add(`node-connection-type-${inputName ?? 'main'}`);
if (inputName !== NodeConnectionType.Main) {
label.classList.add('node-input-endpoint-label--data');
- label.classList.add(`node-connection-type-${inputName}`);
}
return label;
},
@@ -317,9 +317,9 @@ export const getOutputNameOverlay = (
if (ep?.__meta?.endpointLabelLength) {
label.setAttribute('data-endpoint-label-length', ep?.__meta?.endpointLabelLength);
}
+ label.classList.add(`node-connection-type-${getScope(outputName) ?? 'main'}`);
if (outputName !== NodeConnectionType.Main) {
label.classList.add('node-output-endpoint-label--data');
- label.classList.add(`node-connection-type-${getScope(outputName)}`);
}
if (category) {
label.classList.add(`node-connection-category-${category}`);
@@ -998,3 +998,61 @@ export const getFixedNodesList = (workflowNo
return nodes;
};
+
+/**
+ * Calculates the intersecting distances of the mouse event coordinates with the given element's boundaries,
+ * adjusted by the specified offset.
+ *
+ * @param {Element} element - The DOM element to check against.
+ * @param {MouseEvent | TouchEvent} mouseEvent - The mouse or touch event with the coordinates.
+ * @param {number} offset - Offset to adjust the element's boundaries.
+ * @returns { {x: number | null, y: number | null} | null } Object containing intersecting distances along x and y axes or null if no intersection.
+ */
+export function calculateElementIntersection(
+ element: Element,
+ mouseEvent: MouseEvent | TouchEvent,
+ offset: number,
+): { x: number | null; y: number | null } | null {
+ const { top, left, right, bottom } = element.getBoundingClientRect();
+ const [x, y] = getMousePosition(mouseEvent);
+
+ let intersectX: number | null = null;
+ let intersectY: number | null = null;
+
+ if (x >= left - offset && x <= right + offset) {
+ intersectX = Math.min(x - (left - offset), right + offset - x);
+ }
+ if (y >= top - offset && y <= bottom + offset) {
+ intersectY = Math.min(y - (top - offset), bottom + offset - y);
+ }
+
+ if (intersectX === null && intersectY === null) return null;
+
+ return { x: intersectX, y: intersectY };
+}
+
+/**
+ * Checks if the mouse event coordinates intersect with the given element's boundaries,
+ * adjusted by the specified offset.
+ *
+ * @param {Element} element - The DOM element to check against.
+ * @param {MouseEvent | TouchEvent} mouseEvent - The mouse or touch event with the coordinates.
+ * @param {number} offset - Offset to adjust the element's boundaries.
+ * @returns {boolean} True if the mouse coordinates intersect with the element.
+ */
+export function isElementIntersection(
+ element: Element,
+ mouseEvent: MouseEvent | TouchEvent,
+ offset: number,
+): boolean {
+ const intersection = calculateElementIntersection(element, mouseEvent, offset);
+
+ if (intersection === null) {
+ return false;
+ }
+
+ const isWithinVerticalBounds = intersection.y !== null;
+ const isWithinHorizontalBounds = intersection.x !== null;
+
+ return isWithinVerticalBounds && isWithinHorizontalBounds;
+}
diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue
index 93ee597da4..e06a2fbc0f 100644
--- a/packages/editor-ui/src/views/NodeView.vue
+++ b/packages/editor-ui/src/views/NodeView.vue
@@ -113,6 +113,7 @@
@mouseenter="showTriggerMissingToltip(true)"
@mouseleave="showTriggerMissingToltip(false)"
@click="onRunContainerClick"
+ v-if="!isManualChatOnly"
>
0;
},
+ isManualChatOnly(): boolean {
+ return this.containsChatNodes && this.triggerNodes.length === 1;
+ },
containsChatNodes(): boolean {
return !!this.nodes.find(
(node) => node.type === MANUAL_CHAT_TRIGGER_NODE_TYPE && node.disabled !== true,
@@ -2526,28 +2530,35 @@ export default defineComponent({
.catch((e) => {});
}
},
- onEventConnectionAbort(connection: Connection) {
+ async onEventConnectionAbort(connection: Connection) {
try {
if (this.dropPrevented) {
this.dropPrevented = false;
return;
}
-
if (this.pullConnActiveNodeName) {
const sourceNode = this.workflowsStore.getNodeById(connection.parameters.nodeId);
+ const connectionType = connection.parameters.type ?? NodeConnectionType.Main;
+ const overrideTargetEndpoint = connection?.connector
+ ?.overrideTargetEndpoint as Endpoint | null;
+
if (sourceNode) {
- const sourceNodeName = sourceNode.name;
+ const isTarget = connection.parameters.connection === 'target';
+ const sourceNodeName = isTarget ? this.pullConnActiveNodeName : sourceNode.name;
+ const targetNodeName = isTarget ? sourceNode.name : this.pullConnActiveNodeName;
const outputIndex = connection.parameters.index;
+ NodeViewUtils.resetConnectionAfterPull(connection);
+ await this.$nextTick();
this.connectTwoNodes(
sourceNodeName,
outputIndex,
- this.pullConnActiveNodeName,
- 0,
- NodeConnectionType.Main,
+ targetNodeName,
+ overrideTargetEndpoint?.parameters?.index ?? 0,
+ connectionType,
);
this.pullConnActiveNodeName = null;
- this.dropPrevented = true;
+ this.dropPrevented = false;
}
return;
}
@@ -2894,6 +2905,8 @@ export default defineComponent({
const sourceNode = this.workflowsStore.getNodeById(info.connection.parameters.nodeId);
const sourceNodeName = sourceNode.name;
const outputIndex = info.connection.parameters.index;
+ const overrideTargetEndpoint = info.connection.connector
+ .overrideTargetEndpoint as Endpoint | null;
if (connectionInfo) {
this.historyStore.pushCommandToUndo(new RemoveConnectionCommand(connectionInfo));
@@ -2902,7 +2915,7 @@ export default defineComponent({
sourceNodeName,
outputIndex,
this.pullConnActiveNodeName,
- 0,
+ overrideTargetEndpoint?.parameters?.index ?? 0,
NodeConnectionType.Main,
);
this.pullConnActiveNodeName = null;
@@ -2932,58 +2945,86 @@ export default defineComponent({
this.pullConnActiveNodeName = null;
this.pullConnActive = true;
this.canvasStore.newNodeInsertPosition = null;
+ NodeViewUtils.hideConnectionActions(connection);
NodeViewUtils.resetConnection(connection);
- const nodes = [...document.querySelectorAll('.node-wrapper')];
+ const scope = connection.scope as ConnectionTypes;
+ const scopedEndpoints = Array.from(
+ document.querySelectorAll(`[data-jtk-scope-${scope}=true]`),
+ );
+ const connectionType = connection.parameters.connection;
+ const requiredType = connectionType === 'source' ? 'target' : 'source';
+
+ const filteredEndpoints = scopedEndpoints.filter((el) => {
+ const endpoint = el.jtk.endpoint as Endpoint;
+ if (!endpoint) return false;
+
+ // Prevent snapping(but not connecting) to the same node
+ const isSameNode = endpoint.parameters.nodeId === connection.parameters.nodeId;
+ const endpointType = endpoint.parameters.connection;
+
+ return !isSameNode && endpointType === requiredType;
+ });
const onMouseMove = (e: MouseEvent | TouchEvent) => {
if (!connection) {
return;
}
- const element = document.querySelector('.jtk-endpoint.jtk-drag-hover');
- if (element) {
- const endpoint = element.jtk.endpoint;
- NodeViewUtils.showDropConnectionState(connection, endpoint);
- return;
- }
+ const intersectingEndpoints = filteredEndpoints
+ .filter((element: Element) => {
+ const endpoint = element.jtk.endpoint as Endpoint;
- const inputMargin = 24;
- const intersecting = nodes.find((element: Element) => {
- const { top, left, right, bottom } = element.getBoundingClientRect();
- const [x, y] = NodeViewUtils.getMousePosition(e);
- if (top <= y && bottom >= y && left - inputMargin <= x && right >= x) {
- const nodeName = (element as HTMLElement).dataset.name as string;
- const node = this.workflowsStore.getNodeByName(nodeName);
- if (node) {
- const nodeType = this.nodeTypesStore.getNodeType(node.type, node.typeVersion);
+ if (element.classList.contains('jtk-floating-endpoint')) {
+ return false;
+ }
+ const isEndpointIntersect = NodeViewUtils.isElementIntersection(element, e, 50);
+ const isNodeElementIntersect = NodeViewUtils.isElementIntersection(
+ endpoint.element,
+ e,
+ 30,
+ );
- const workflow = this.getCurrentWorkflow();
- const workflowNode = workflow.getNode(nodeName);
- const inputs = NodeHelpers.getNodeInputs(workflow, workflowNode!, nodeType);
+ if (isEndpointIntersect || isNodeElementIntersect) {
+ const node = this.workflowsStore.getNodeById(endpoint.parameters.nodeId);
- if (nodeType && inputs.length === 1) {
- this.pullConnActiveNodeName = node.name;
- const endpointUUID = this.getInputEndpointUUID(
- nodeName,
- connection.parameters.type,
- 0,
- );
- if (endpointUUID) {
- const endpoint = this.instance?.getEndpoint(endpointUUID);
+ if (node) {
+ const nodeType = this.nodeTypesStore.getNodeType(node.type, node.typeVersion);
- NodeViewUtils.showDropConnectionState(connection, endpoint);
+ if (!nodeType) return false;
- return true;
- }
+ return true;
}
}
- }
- return false;
- });
+ return false;
+ })
+ .sort((a, b) => {
+ const aEndpointIntersect = NodeViewUtils.calculateElementIntersection(a, e, 50);
+ const bEndpointIntersect = NodeViewUtils.calculateElementIntersection(b, e, 50);
- if (!intersecting) {
+ // If both intersections are null, treat them as equal
+ if (!aEndpointIntersect?.y && !bEndpointIntersect?.y) {
+ return 0;
+ }
+
+ // If one intersection is null, sort the non-null one first
+ if (!aEndpointIntersect?.y) return 1;
+ if (!bEndpointIntersect?.y) return -1;
+
+ // Otherwise, sort by ascending Y distance
+ return bEndpointIntersect.y - aEndpointIntersect.y;
+ });
+
+ if (intersectingEndpoints.length > 0) {
+ const intersectingEndpoint = intersectingEndpoints[0];
+ const endpoint = intersectingEndpoint.jtk.endpoint as Endpoint;
+ const node = this.workflowsStore.getNodeById(endpoint.parameters.nodeId);
+
+ this.pullConnActiveNodeName = node?.name ?? null;
+
+ NodeViewUtils.showDropConnectionState(connection, endpoint);
+ } else {
NodeViewUtils.showPullConnectionState(connection);
this.pullConnActiveNodeName = null;
}
@@ -4444,6 +4485,23 @@ export default defineComponent({
NodeConnectionType.Main,
);
}
+
+ const lastAddedNode = this.nodes[this.nodes.length - 1];
+ const workflow = this.getCurrentWorkflow();
+ const lastNodeInputs = workflow.getParentNodesByDepth(lastAddedNode.name, 1);
+
+ // If the last added node has multiple inputs, move them down
+ if (lastNodeInputs.length > 1) {
+ lastNodeInputs.slice(1).forEach((node, index) => {
+ const nodeUi = this.workflowsStore.getNodeByName(node.name);
+ if (!nodeUi) return;
+
+ this.onMoveNode({
+ nodeName: nodeUi.name,
+ position: [nodeUi.position[0], nodeUi.position[1] + 100 * (index + 1)],
+ });
+ });
+ }
},
async saveCurrentWorkflowExternal(callback: () => void) {
@@ -4691,25 +4749,37 @@ export default defineComponent({
this.registerCustomAction({
key: 'openSelectiveNodeCreator',
- action: ({ connectiontype, node }: { connectiontype: NodeConnectionType; node: string }) => {
- this.onToggleNodeCreator({
- source: NODE_CREATOR_OPEN_SOURCES.NOTICE_ERROR_MESSAGE,
- createNodeActive: true,
- nodeCreatorView: AI_NODE_CREATOR_VIEW,
- });
+ action: async ({
+ connectiontype,
+ node,
+ creatorview,
+ }: {
+ connectiontype: NodeConnectionType;
+ node: string;
+ creatorview?: string;
+ }) => {
+ const nodeName = node ?? this.ndvStore.activeNodeName;
+ const nodeData = nodeName ? this.workflowsStore.getNodeByName(nodeName) : null;
this.ndvStore.activeNodeName = null;
- // Select the node so that the node creator knows which node to connect to
- const nodeData = this.workflowsStore.getNodeByName(node);
- if (connectiontype && nodeData) {
- this.insertNodeAfterSelected({
- index: 0,
- endpointUuid: `${nodeData.id}-input${connectiontype}0`,
- eventSource: NODE_CREATOR_OPEN_SOURCES.NOTICE_ERROR_MESSAGE,
- outputType: connectiontype,
- sourceId: nodeData.id,
- });
- }
+ await this.redrawNode(node);
+ // Wait for UI to update
+ setTimeout(() => {
+ if (creatorview) {
+ this.onToggleNodeCreator({
+ createNodeActive: true,
+ nodeCreatorView: creatorview,
+ });
+ } else if (connectiontype && nodeData) {
+ this.insertNodeAfterSelected({
+ index: 0,
+ endpointUuid: `${nodeData.id}-input${connectiontype}0`,
+ eventSource: NODE_CREATOR_OPEN_SOURCES.NOTICE_ERROR_MESSAGE,
+ outputType: connectiontype,
+ sourceId: nodeData.id,
+ });
+ }
+ }, 0);
},
});
@@ -4910,14 +4980,14 @@ export default defineComponent({
&.connection-drag-scope-active-connection-target {
// Apply style to compatible output endpoints
.diamond-output-endpoint[data-jtk-scope-#{$node-type}='true'] {
- transform: scale(1.375) rotate(45deg);
+ transform: scale(1.5) rotate(45deg);
}
.add-input-endpoint[data-jtk-scope-#{$node-type}='true'] {
// Apply style to dragged compatible input endpoint
&.jtk-dragging {
.add-input-endpoint-default {
- transform: translate(-4px, -4px) scale(1.375);
+ transform: translate(-5px, -5px) scale(1.5);
}
}
@@ -4939,7 +5009,7 @@ export default defineComponent({
// Apply style to dragged compatible output endpoint
.diamond-output-endpoint[data-jtk-scope-#{$node-type}='true'] {
&.jtk-dragging {
- transform: scale(1.375) rotate(45deg);
+ transform: scale(1.5) rotate(45deg);
}
// Apply style to non-dragged compatible input endpoints
@@ -4951,7 +5021,7 @@ export default defineComponent({
// Apply style to compatible output endpoints
.add-input-endpoint[data-jtk-scope-#{$node-type}='true'] {
.add-input-endpoint-default {
- transform: translate(-4px, -4px) scale(1.375);
+ transform: translate(-5px, -5px) scale(1.5);
}
}
diff --git a/packages/editor-ui/src/views/TemplatesWorkflowView.vue b/packages/editor-ui/src/views/TemplatesWorkflowView.vue
index 9d2f2facb7..6156baa74c 100644
--- a/packages/editor-ui/src/views/TemplatesWorkflowView.vue
+++ b/packages/editor-ui/src/views/TemplatesWorkflowView.vue
@@ -151,7 +151,7 @@ export default defineComponent({
async mounted() {
this.scrollToTop();
- if (this.template && (this.template as ITemplatesWorkflowFull).full) {
+ if (this.template && this.template.full) {
this.loading = false;
return;
}
diff --git a/packages/nodes-base/nodes/OpenAi/OpenAi.node.ts b/packages/nodes-base/nodes/OpenAi/OpenAi.node.ts
index 4f7fcc4672..e34af59259 100644
--- a/packages/nodes-base/nodes/OpenAi/OpenAi.node.ts
+++ b/packages/nodes-base/nodes/OpenAi/OpenAi.node.ts
@@ -28,6 +28,13 @@ export class OpenAi implements INodeType {
baseURL: 'https://api.openai.com',
},
properties: [
+ {
+ displayName:
+ 'For more advanced uses, consider using an advanced AI node',
+ name: 'noticeAdvanceAi',
+ type: 'notice',
+ default: '',
+ },
{
displayName: 'Resource',
name: 'resource',