diff --git a/packages/editor-ui/src/components/Node/NodeCreator/MainPanel.vue b/packages/editor-ui/src/components/Node/NodeCreator/MainPanel.vue index 79166b9f3d..1947318e37 100644 --- a/packages/editor-ui/src/components/Node/NodeCreator/MainPanel.vue +++ b/packages/editor-ui/src/components/Node/NodeCreator/MainPanel.vue @@ -262,9 +262,9 @@ function transformCreateElements( }); return sorted.map((nodeType) => { - // N8n node is a special case since it's the only core node that is both trigger and regular - // if we have more cases like this we should add more robust logic - const isN8nNode = nodeType.name.includes(N8N_NODE_TYPE); + const hasTriggerActions = nodeType.actions?.find((action) => action.name.includes('trigger')); + const hasRgeularActions = nodeType.actions?.find((action) => !action.name.includes('trigger')); + return { type, category: nodeType.codex?.categories, @@ -273,8 +273,8 @@ function transformCreateElements( nodeType, subcategory: state.activeNodeActions?.displayName ?? '', }, - includedByTrigger: isN8nNode || nodeType.group.includes('trigger'), - includedByRegular: isN8nNode || !nodeType.group.includes('trigger'), + includedByTrigger: hasTriggerActions || nodeType.group.includes('trigger'), + includedByRegular: hasRgeularActions || !nodeType.group.includes('trigger'), } as INodeCreateElement; }); } diff --git a/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue b/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue index 88676dc4c2..62dd517abc 100644 --- a/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue +++ b/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue @@ -105,7 +105,10 @@ const displayName = computed(() => { return instance?.proxy.$locale.headerText({ key: `headers.${shortNodeType}.displayName`, - fallback: props.allowActions ? displayName.replace('Trigger', '') : displayName, + fallback: + props.allowActions && props.nodeType.actions?.length + ? displayName.replace('Trigger', '') + : displayName, }); }); diff --git a/packages/editor-ui/src/stores/nodeCreator.ts b/packages/editor-ui/src/stores/nodeCreator.ts index 8fb92574b8..aea5f2af93 100644 --- a/packages/editor-ui/src/stores/nodeCreator.ts +++ b/packages/editor-ui/src/stores/nodeCreator.ts @@ -308,31 +308,32 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, { return nodesWithActions; }, mergedAppNodes(): INodeTypeDescription[] { - const mergedNodes = [...this.visibleNodesWithActions] - // Sort triggers so they are always on top and when later get merged - // they won't be discarded if they have the same name as a core node which doesn't contain actions - .sort((a, b) => { - if (a.group.includes('trigger')) return -1; - if (b.group.includes('trigger')) return 1; + const triggers = this.visibleNodesWithActions.filter((node) => + node.group.includes('trigger'), + ); + const apps = this.visibleNodesWithActions + .filter((node) => !node.group.includes('trigger')) + .map((node) => { + const newNode = deepCopy(node); + newNode.actions = newNode.actions || []; + return newNode; + }); - return 0; - }) - .reduce((acc: Record, node: INodeTypeDescription) => { - const clonedNode = deepCopy(node); - const actions = node.actions || []; + triggers.forEach((node) => { + const normalizedName = node.name.toLowerCase().replace('trigger', ''); + const app = apps.find((node) => node.name.toLowerCase() === normalizedName); + const newNode = deepCopy(node); + if (app && app.actions?.length) { + // merge triggers into regular nodes that match + app?.actions?.push(...(newNode.actions || [])); + app.description = newNode.description; // default to trigger description + } else { + newNode.actions = newNode.actions || []; + apps.push(newNode); + } + }); - const normalizedName = node.name.toLowerCase().replace('trigger', ''); - const existingNode = acc[normalizedName]; - - if (existingNode) existingNode.actions?.push(...actions); - else acc[normalizedName] = clonedNode; - - acc[normalizedName].displayName = node.displayName.replace('Trigger', ''); - - return acc; - }, {}); - - const filteredNodes = Object.values(mergedNodes).map((node) => ({ + const filteredNodes = apps.map((node) => ({ ...node, actions: filterActions(node.actions || []), }));