mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 10:31:15 +00:00
feat(editor): Add pre-built agents experiment (#18124)
This commit is contained in:
@@ -9,15 +9,15 @@ defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n8n-node-creator-node
|
||||
:class="$style.creatorOpenTemplate"
|
||||
<N8nNodeCreatorNode
|
||||
:class="{ [$style.creatorOpenTemplate]: true, [$style.compact]: openTemplate.compact }"
|
||||
:title="openTemplate.title"
|
||||
:is-trigger="false"
|
||||
:description="openTemplate.description"
|
||||
:tag="openTemplate.tag"
|
||||
:show-action-arrow="true"
|
||||
:is-trigger="false"
|
||||
>
|
||||
<template #icon>
|
||||
<template v-if="openTemplate.icon" #icon>
|
||||
<n8n-node-icon
|
||||
type="icon"
|
||||
:name="openTemplate.icon"
|
||||
@@ -26,7 +26,17 @@ defineProps<Props>();
|
||||
:use-updated-icons="true"
|
||||
/>
|
||||
</template>
|
||||
</n8n-node-creator-node>
|
||||
|
||||
<template v-if="openTemplate.nodes" #extraDetails>
|
||||
<NodeIcon
|
||||
v-for="node in openTemplate.nodes"
|
||||
:key="node.name"
|
||||
:node-type="node"
|
||||
:size="16"
|
||||
:show-tooltip="true"
|
||||
/>
|
||||
</template>
|
||||
</N8nNodeCreatorNode>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
@@ -34,5 +44,11 @@ defineProps<Props>();
|
||||
--action-arrow-color: var(--color-text-light);
|
||||
margin-left: var(--spacing-s);
|
||||
margin-right: var(--spacing-xs);
|
||||
padding-bottom: var(--spacing-xs);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
.compact {
|
||||
margin-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -29,10 +29,11 @@ import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { useNodeCreatorStore } from '@/stores/nodeCreator.store';
|
||||
import OrderSwitcher from './../OrderSwitcher.vue';
|
||||
import { isNodePreviewKey } from '../utils';
|
||||
import { getActiveViewCallouts, isNodePreviewKey } from '../utils';
|
||||
|
||||
import CommunityNodeInfo from '../Panel/CommunityNodeInfo.vue';
|
||||
import CommunityNodeFooter from '../Panel/CommunityNodeFooter.vue';
|
||||
import { useCalloutHelpers } from '@/composables/useCalloutHelpers';
|
||||
|
||||
const emit = defineEmits<{
|
||||
nodeTypeSelected: [value: NodeTypeSelectedPayload[]];
|
||||
@@ -53,6 +54,7 @@ const {
|
||||
} = useActions();
|
||||
|
||||
const nodeCreatorStore = useNodeCreatorStore();
|
||||
const calloutHelpers = useCalloutHelpers();
|
||||
|
||||
// We only inject labels if search is empty
|
||||
const parsedTriggerActions = computed(() =>
|
||||
@@ -159,6 +161,15 @@ function onKeySelect(activeItemId: string) {
|
||||
}
|
||||
|
||||
function onSelected(actionCreateElement: INodeCreateElement) {
|
||||
if (actionCreateElement.type === 'openTemplate') {
|
||||
calloutHelpers.openSampleWorkflowTemplate(actionCreateElement.properties.templateId, {
|
||||
telemetry: {
|
||||
source: 'nodeCreator',
|
||||
section: useViewStacks().activeViewStack.title,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (actionCreateElement.type !== 'action') return;
|
||||
|
||||
const actionData = getActionData(actionCreateElement.properties);
|
||||
@@ -234,6 +245,14 @@ function addHttpNode() {
|
||||
onMounted(() => {
|
||||
trackActionsView();
|
||||
});
|
||||
|
||||
const callouts = computed<INodeCreateElement[]>(() =>
|
||||
getActiveViewCallouts(
|
||||
useViewStacks().activeViewStack.title,
|
||||
calloutHelpers.isPreBuiltAgentsCalloutVisible.value,
|
||||
calloutHelpers.getPreBuiltAgentNodeCreatorItems(),
|
||||
),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -243,6 +262,8 @@ onMounted(() => {
|
||||
[$style.containerPaddingBottom]: !communityNodeDetails,
|
||||
}"
|
||||
>
|
||||
<ItemsRenderer :elements="callouts" :class="$style.items" @selected="onSelected" />
|
||||
|
||||
<CommunityNodeInfo v-if="communityNodeDetails" />
|
||||
<OrderSwitcher v-if="rootView" :root-view="rootView">
|
||||
<template v-if="shouldShowTriggers" #triggers>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
AI_NODE_CREATOR_VIEW,
|
||||
AI_OTHERS_NODE_CREATOR_VIEW,
|
||||
HITL_SUBCATEGORY,
|
||||
PRE_BUILT_AGENTS_COLLECTION,
|
||||
} from '@/constants';
|
||||
|
||||
import type { BaseTextKey } from '@n8n/i18n';
|
||||
@@ -28,6 +29,7 @@ import {
|
||||
prepareCommunityNodeDetailsViewStack,
|
||||
transformNodeType,
|
||||
getRootSearchCallouts,
|
||||
getActiveViewCallouts,
|
||||
shouldShowCommunityNodeDetails,
|
||||
getHumanInTheLoopActions,
|
||||
} from '../utils';
|
||||
@@ -104,6 +106,17 @@ function getFilteredActions(
|
||||
}
|
||||
|
||||
function onSelected(item: INodeCreateElement) {
|
||||
if (item.key === PRE_BUILT_AGENTS_COLLECTION) {
|
||||
void calloutHelpers.openPreBuiltAgentsCollection({
|
||||
telemetry: {
|
||||
source: 'nodeCreator',
|
||||
section: activeViewStack.value.title,
|
||||
},
|
||||
resetStacks: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.type === 'subcategory') {
|
||||
const subcategoryKey = camelCase(item.properties.title);
|
||||
const title = i18n.baseText(`nodeCreator.subcategoryNames.${subcategoryKey}` as BaseTextKey);
|
||||
@@ -223,9 +236,12 @@ function onSelected(item: INodeCreateElement) {
|
||||
}
|
||||
|
||||
if (item.type === 'openTemplate') {
|
||||
if (item.properties.key === 'rag-starter-template') {
|
||||
void calloutHelpers.openRagStarterTemplate();
|
||||
}
|
||||
calloutHelpers.openSampleWorkflowTemplate(item.properties.templateId, {
|
||||
telemetry: {
|
||||
source: 'nodeCreator',
|
||||
section: activeViewStack.value.title,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,11 +281,16 @@ function baseSubcategoriesFilter(item: INodeCreateElement): boolean {
|
||||
return hasActions || !hasTriggerGroup;
|
||||
}
|
||||
|
||||
const globalCallouts = computed<INodeCreateElement[]>(() =>
|
||||
getRootSearchCallouts(activeViewStack.value.search ?? '', {
|
||||
const globalCallouts = computed<INodeCreateElement[]>(() => [
|
||||
...getRootSearchCallouts(activeViewStack.value.search ?? '', {
|
||||
isRagStarterCalloutVisible: calloutHelpers.isRagStarterCalloutVisible.value,
|
||||
}),
|
||||
);
|
||||
...getActiveViewCallouts(
|
||||
activeViewStack.value.title,
|
||||
calloutHelpers.isPreBuiltAgentsCalloutVisible.value,
|
||||
calloutHelpers.getPreBuiltAgentNodeCreatorItems(),
|
||||
),
|
||||
]);
|
||||
|
||||
function arrowLeft() {
|
||||
popViewStack();
|
||||
|
||||
@@ -305,6 +305,8 @@ watch(
|
||||
&:last-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
&:after {
|
||||
content: none;
|
||||
|
||||
@@ -8,7 +8,7 @@ exports[`viewsData > AIView > should return ai view with ai transform node 1`] =
|
||||
"properties": {
|
||||
"description": "See what's possible and get started 5x faster",
|
||||
"icon": "box-open",
|
||||
"name": "ai_templates_root",
|
||||
"key": "ai_templates_root",
|
||||
"tag": {
|
||||
"text": "Recommended",
|
||||
"type": "info",
|
||||
@@ -81,7 +81,7 @@ exports[`viewsData > AIView > should return ai view without ai transform node if
|
||||
"properties": {
|
||||
"description": "See what's possible and get started 5x faster",
|
||||
"icon": "box-open",
|
||||
"name": "ai_templates_root",
|
||||
"key": "ai_templates_root",
|
||||
"tag": {
|
||||
"text": "Recommended",
|
||||
"type": "info",
|
||||
@@ -141,7 +141,7 @@ exports[`viewsData > AIView > should return ai view without ai transform node if
|
||||
"properties": {
|
||||
"description": "See what's possible and get started 5x faster",
|
||||
"icon": "box-open",
|
||||
"name": "ai_templates_root",
|
||||
"key": "ai_templates_root",
|
||||
"tag": {
|
||||
"text": "Recommended",
|
||||
"type": "info",
|
||||
|
||||
@@ -7,17 +7,24 @@ import type {
|
||||
SectionCreateElement,
|
||||
ActionTypeDescription,
|
||||
NodeFilterType,
|
||||
OpenTemplateElement,
|
||||
LinkCreateElement,
|
||||
ViewCreateElement,
|
||||
} from '@/Interface';
|
||||
import {
|
||||
AI_CATEGORY_AGENTS,
|
||||
AI_CATEGORY_OTHER_TOOLS,
|
||||
AI_CATEGORY_TOOLS,
|
||||
AI_SUBCATEGORY,
|
||||
AI_TRANSFORM_NODE_TYPE,
|
||||
AI_CATEGORY_LANGUAGE_MODELS,
|
||||
AI_CATEGORY_MEMORY,
|
||||
CORE_NODES_CATEGORY,
|
||||
DEFAULT_SUBCATEGORY,
|
||||
DISCORD_NODE_TYPE,
|
||||
HUMAN_IN_THE_LOOP_CATEGORY,
|
||||
MICROSOFT_TEAMS_NODE_TYPE,
|
||||
PRE_BUILT_AGENTS_COLLECTION,
|
||||
} from '@/constants';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@@ -28,10 +35,11 @@ import sortBy from 'lodash/sortBy';
|
||||
import * as changeCase from 'change-case';
|
||||
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { SEND_AND_WAIT_OPERATION } from 'n8n-workflow';
|
||||
import type { NodeIconSource } from '../../../utils/nodeIcon';
|
||||
import type { CommunityNodeDetails, ViewStack } from './composables/useViewStacks';
|
||||
import { useNodeTypesStore } from '../../../stores/nodeTypes.store';
|
||||
import { PrebuiltAgentTemplates, SampleTemplates } from '@/utils/templates/workflowSamples';
|
||||
|
||||
const COMMUNITY_NODE_TYPE_PREVIEW_TOKEN = '-preview';
|
||||
|
||||
@@ -308,30 +316,140 @@ export function prepareCommunityNodeDetailsViewStack(
|
||||
};
|
||||
}
|
||||
|
||||
export function getRootSearchCallouts(search: string, { isRagStarterCalloutVisible = false }) {
|
||||
export function getRagStarterCallout(): OpenTemplateElement {
|
||||
return {
|
||||
uuid: SampleTemplates.RagStarterTemplate,
|
||||
key: SampleTemplates.RagStarterTemplate,
|
||||
type: 'openTemplate',
|
||||
properties: {
|
||||
templateId: SampleTemplates.RagStarterTemplate,
|
||||
title: i18n.baseText('nodeCreator.ragStarterTemplate.openTemplateItem.title'),
|
||||
icon: 'database',
|
||||
description: i18n.baseText('nodeCreator.ragStarterTemplate.openTemplateItem.description'),
|
||||
tag: {
|
||||
type: 'info',
|
||||
text: i18n.baseText('nodeCreator.triggerHelperPanel.manualTriggerTag'),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Callout without a divider
|
||||
export function getPreBuiltAgentsCallout(): ViewCreateElement {
|
||||
return {
|
||||
key: PRE_BUILT_AGENTS_COLLECTION,
|
||||
type: 'view',
|
||||
properties: {
|
||||
title: i18n.baseText('nodeCreator.preBuiltAgents.title'),
|
||||
icon: 'box',
|
||||
description: i18n.baseText('nodeCreator.preBuiltAgents.description'),
|
||||
borderless: true,
|
||||
tag: {
|
||||
type: 'info',
|
||||
text: i18n.baseText('nodeCreator.triggerHelperPanel.manualTriggerTag'),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Callout with divider after it
|
||||
export function getPreBuiltAgentsCalloutWithDivider(): LinkCreateElement {
|
||||
return {
|
||||
key: PRE_BUILT_AGENTS_COLLECTION,
|
||||
type: 'link',
|
||||
properties: {
|
||||
key: PRE_BUILT_AGENTS_COLLECTION,
|
||||
url: '',
|
||||
title: i18n.baseText('nodeCreator.preBuiltAgents.title'),
|
||||
icon: 'box',
|
||||
description: i18n.baseText('nodeCreator.preBuiltAgents.description'),
|
||||
tag: {
|
||||
type: 'info',
|
||||
text: i18n.baseText('nodeCreator.triggerHelperPanel.manualTriggerTag'),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getAiTemplatesCallout(aiTemplatesURL: string): LinkCreateElement {
|
||||
return {
|
||||
key: 'ai_templates_root',
|
||||
type: 'link',
|
||||
properties: {
|
||||
title: i18n.baseText('nodeCreator.aiPanel.linkItem.title'),
|
||||
icon: 'box-open',
|
||||
description: i18n.baseText('nodeCreator.aiPanel.linkItem.description'),
|
||||
key: 'ai_templates_root',
|
||||
url: aiTemplatesURL,
|
||||
tag: {
|
||||
type: 'info',
|
||||
text: i18n.baseText('nodeCreator.triggerHelperPanel.manualTriggerTag'),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getRootSearchCallouts(search: string, { isRagStarterCalloutVisible = false } = {}) {
|
||||
const results: INodeCreateElement[] = [];
|
||||
|
||||
const ragKeywords = ['rag', 'vec', 'know'];
|
||||
if (isRagStarterCalloutVisible && ragKeywords.some((x) => search.toLowerCase().startsWith(x))) {
|
||||
results.push({
|
||||
uuid: 'rag-starter-template',
|
||||
key: 'rag-starter-template',
|
||||
type: 'openTemplate',
|
||||
properties: {
|
||||
key: 'rag-starter-template',
|
||||
title: i18n.baseText('nodeCreator.ragStarterTemplate.openTemplateItem.title'),
|
||||
icon: 'database',
|
||||
description: i18n.baseText('nodeCreator.ragStarterTemplate.openTemplateItem.description'),
|
||||
tag: {
|
||||
type: 'info',
|
||||
text: i18n.baseText('nodeCreator.triggerHelperPanel.manualTriggerTag'),
|
||||
},
|
||||
},
|
||||
});
|
||||
results.push(getRagStarterCallout());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const getTemplateLink = (
|
||||
templateId: string,
|
||||
availableTemplates: OpenTemplateElement[],
|
||||
): OpenTemplateElement | undefined => {
|
||||
const templateLink = availableTemplates.find((template) => template.key === templateId);
|
||||
|
||||
if (templateLink?.properties) {
|
||||
templateLink.properties.compact = true;
|
||||
}
|
||||
|
||||
return templateLink;
|
||||
};
|
||||
|
||||
export function getActiveViewCallouts(
|
||||
title: string | undefined,
|
||||
isPreBuiltAgentsCalloutVisible: boolean,
|
||||
templates: OpenTemplateElement[],
|
||||
) {
|
||||
const results: INodeCreateElement[] = [];
|
||||
|
||||
if (isPreBuiltAgentsCalloutVisible && title) {
|
||||
if (title === AI_CATEGORY_LANGUAGE_MODELS) {
|
||||
results.push(getPreBuiltAgentsCalloutWithDivider());
|
||||
} else if ([AI_CATEGORY_MEMORY, AI_CATEGORY_TOOLS].includes(title)) {
|
||||
results.push(getPreBuiltAgentsCallout());
|
||||
} else if (title === 'Google Calendar' || title === 'Telegram') {
|
||||
const templateLink = getTemplateLink(PrebuiltAgentTemplates.VoiceAssistantAgent, templates);
|
||||
if (templateLink) {
|
||||
results.push(templateLink);
|
||||
}
|
||||
} else if (title === 'Google Drive') {
|
||||
const templateLink = getTemplateLink(PrebuiltAgentTemplates.KnowledgeStoreAgent, templates);
|
||||
if (templateLink) {
|
||||
results.push(templateLink);
|
||||
}
|
||||
} else if (title === 'Google Sheets') {
|
||||
const templateLink = getTemplateLink(PrebuiltAgentTemplates.TaskManagementAgent, templates);
|
||||
if (templateLink) {
|
||||
results.push(templateLink);
|
||||
}
|
||||
} else if (title === 'Gmail') {
|
||||
const templateLink = getTemplateLink(PrebuiltAgentTemplates.EmailTriageAgent, templates);
|
||||
if (templateLink) {
|
||||
results.push(templateLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
export const shouldShowCommunityNodeDetails = (communityNode: boolean, viewStack: ViewStack) => {
|
||||
if (viewStack.rootView === 'AI Other' && viewStack.title === 'Tools') {
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { setActivePinia } from 'pinia';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { AI_CATEGORY_AGENTS, AI_CATEGORY_CHAINS, AI_TRANSFORM_NODE_TYPE } from '@/constants';
|
||||
import {
|
||||
AI_CATEGORY_AGENTS,
|
||||
AI_CATEGORY_CHAINS,
|
||||
AI_TRANSFORM_NODE_TYPE,
|
||||
PRE_BUILT_AGENTS_EXPERIMENT,
|
||||
} from '@/constants';
|
||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
import { START_NODE_TYPE } from 'n8n-workflow';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
@@ -55,7 +60,14 @@ describe('viewsData', () => {
|
||||
setActivePinia(createTestingPinia());
|
||||
|
||||
posthogStore = usePostHog();
|
||||
vi.spyOn(posthogStore, 'isVariantEnabled').mockReturnValue(true);
|
||||
|
||||
vi.spyOn(posthogStore, 'isVariantEnabled').mockImplementation((experiment) => {
|
||||
if (experiment === PRE_BUILT_AGENTS_EXPERIMENT.name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const templatesStore = useTemplatesStore();
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ import type { BaseTextKey } from '@n8n/i18n';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useEvaluationStore } from '@/stores/evaluation.store.ee';
|
||||
import { getAiTemplatesCallout, getPreBuiltAgentsCalloutWithDivider } from './utils';
|
||||
import { useCalloutHelpers } from '@/composables/useCalloutHelpers';
|
||||
|
||||
export interface NodeViewItemSection {
|
||||
key: string;
|
||||
title: string;
|
||||
@@ -79,6 +82,7 @@ export interface NodeViewItem {
|
||||
key: string;
|
||||
type: string;
|
||||
properties: {
|
||||
key?: string;
|
||||
name?: string;
|
||||
title?: string;
|
||||
icon?: Themed<string>;
|
||||
@@ -94,7 +98,7 @@ export interface NodeViewItem {
|
||||
description?: string;
|
||||
displayName?: string;
|
||||
tag?: {
|
||||
type: string;
|
||||
type?: string;
|
||||
text: string;
|
||||
};
|
||||
forceIncludeNodes?: string[];
|
||||
@@ -168,6 +172,7 @@ export function AIView(_nodes: SimplifiedNodeType[]): NodeView {
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const templatesStore = useTemplatesStore();
|
||||
const evaluationStore = useEvaluationStore();
|
||||
const calloutHelpers = useCalloutHelpers();
|
||||
const isEvaluationEnabled = evaluationStore.isEvaluationEnabled;
|
||||
|
||||
const evaluationNode = getEvaluationNode(nodeTypesStore, isEvaluationEnabled);
|
||||
@@ -186,26 +191,16 @@ export function AIView(_nodes: SimplifiedNodeType[]): NodeView {
|
||||
const aiTransformNode = nodeTypesStore.getNodeType(AI_TRANSFORM_NODE_TYPE);
|
||||
const transformNode = askAiEnabled && aiTransformNode ? [getNodeView(aiTransformNode)] : [];
|
||||
|
||||
const callouts: NodeViewItem[] = !calloutHelpers.isPreBuiltAgentsCalloutVisible.value
|
||||
? [getAiTemplatesCallout(aiTemplatesURL)]
|
||||
: [getPreBuiltAgentsCalloutWithDivider()];
|
||||
|
||||
return {
|
||||
value: AI_NODE_CREATOR_VIEW,
|
||||
title: i18n.baseText('nodeCreator.aiPanel.aiNodes'),
|
||||
subtitle: i18n.baseText('nodeCreator.aiPanel.selectAiNode'),
|
||||
items: [
|
||||
{
|
||||
key: 'ai_templates_root',
|
||||
type: 'link',
|
||||
properties: {
|
||||
title: i18n.baseText('nodeCreator.aiPanel.linkItem.title'),
|
||||
icon: 'box-open',
|
||||
description: i18n.baseText('nodeCreator.aiPanel.linkItem.description'),
|
||||
name: 'ai_templates_root',
|
||||
url: aiTemplatesURL,
|
||||
tag: {
|
||||
type: 'info',
|
||||
text: i18n.baseText('nodeCreator.triggerHelperPanel.manualTriggerTag'),
|
||||
},
|
||||
},
|
||||
},
|
||||
...callouts,
|
||||
...agentNodes,
|
||||
...chainNodes,
|
||||
...transformNode,
|
||||
|
||||
@@ -55,7 +55,8 @@ export const TEST_PARAMETERS: INodeProperties[] = [
|
||||
typeOptions: {
|
||||
calloutAction: {
|
||||
label: 'and action!',
|
||||
type: 'openRagStarterTemplate',
|
||||
type: 'openSampleWorkflowTemplate',
|
||||
templateId: 'test-template-id',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
CalloutActionType,
|
||||
CalloutAction,
|
||||
INodeParameters,
|
||||
INodeProperties,
|
||||
NodeParameterValueType,
|
||||
@@ -47,6 +47,7 @@ import { storeToRefs } from 'pinia';
|
||||
import { useCalloutHelpers } from '@/composables/useCalloutHelpers';
|
||||
import { getParameterTypeOption } from '@/utils/nodeSettingsUtils';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import type { IconName } from '@n8n/design-system/components/N8nIcon/icons';
|
||||
|
||||
const LazyFixedCollectionParameter = defineAsyncComponent(
|
||||
async () => await import('./FixedCollectionParameter.vue'),
|
||||
@@ -86,8 +87,14 @@ const nodeSettingsParameters = useNodeSettingsParameters();
|
||||
const asyncLoadingError = ref(false);
|
||||
const workflowHelpers = useWorkflowHelpers();
|
||||
const i18n = useI18n();
|
||||
const { dismissCallout, isCalloutDismissed, openRagStarterTemplate, isRagStarterCalloutVisible } =
|
||||
useCalloutHelpers();
|
||||
const {
|
||||
dismissCallout,
|
||||
isCalloutDismissed,
|
||||
openPreBuiltAgentsCollection,
|
||||
openSampleWorkflowTemplate,
|
||||
isRagStarterCalloutVisible,
|
||||
isPreBuiltAgentsCalloutVisible,
|
||||
} = useCalloutHelpers();
|
||||
|
||||
const { activeNode } = storeToRefs(ndvStore);
|
||||
|
||||
@@ -154,12 +161,22 @@ const credentialsParameterIndex = computed(() => {
|
||||
return filteredParameters.value.findIndex((parameter) => parameter.type === 'credentials');
|
||||
});
|
||||
|
||||
const calloutParameterIndex = computed(() => {
|
||||
return filteredParameters.value.findIndex((parameter) => parameter.type === 'callout');
|
||||
});
|
||||
|
||||
const indexToShowSlotAt = computed(() => {
|
||||
if (credentialsParameterIndex.value !== -1) {
|
||||
return credentialsParameterIndex.value;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
|
||||
// If the node has a callout don't show credentials before it
|
||||
if (calloutParameterIndex.value !== -1) {
|
||||
index = calloutParameterIndex.value + 1;
|
||||
}
|
||||
|
||||
// For nodes that use old credentials UI, keep credentials below authentication field in NDV
|
||||
// otherwise credentials will use auth filed position since the auth field is moved to credentials modal
|
||||
const fieldOffset = KEEP_AUTH_IN_NDV_FOR_NODES.includes(nodeType.value?.name || '') ? 1 : 0;
|
||||
@@ -405,6 +422,14 @@ function isRagStarterCallout(parameter: INodeProperties): boolean {
|
||||
return parameter.type === 'callout' && parameter.name === 'ragStarterCallout';
|
||||
}
|
||||
|
||||
function isAgentDefaultCallout(parameter: INodeProperties): boolean {
|
||||
return parameter.type === 'callout' && parameter.name === 'aiAgentStarterCallout';
|
||||
}
|
||||
|
||||
function isPreBuiltAgentsCallout(parameter: INodeProperties): boolean {
|
||||
return parameter.type === 'callout' && parameter.name.startsWith('preBuiltAgentsCallout');
|
||||
}
|
||||
|
||||
function isCalloutVisible(parameter: INodeProperties): boolean {
|
||||
if (isCalloutDismissed(parameter.name)) return false;
|
||||
|
||||
@@ -412,12 +437,38 @@ function isCalloutVisible(parameter: INodeProperties): boolean {
|
||||
return isRagStarterCalloutVisible.value;
|
||||
}
|
||||
|
||||
if (isAgentDefaultCallout(parameter)) {
|
||||
return !isPreBuiltAgentsCalloutVisible.value;
|
||||
}
|
||||
|
||||
if (isPreBuiltAgentsCallout(parameter)) {
|
||||
return isPreBuiltAgentsCalloutVisible.value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function onCalloutAction(action: CalloutActionType) {
|
||||
if (action === 'openRagStarterTemplate') {
|
||||
openRagStarterTemplate(activeNode.value?.type ?? 'no active node');
|
||||
function onCalloutAction(action: CalloutAction) {
|
||||
switch (action.type) {
|
||||
case 'openPreBuiltAgentsCollection':
|
||||
void openPreBuiltAgentsCollection({
|
||||
telemetry: {
|
||||
source: 'ndv',
|
||||
nodeType: activeNode.value?.type,
|
||||
},
|
||||
resetStacks: false,
|
||||
});
|
||||
break;
|
||||
case 'openSampleWorkflowTemplate':
|
||||
void openSampleWorkflowTemplate(action.templateId, {
|
||||
telemetry: {
|
||||
source: 'ndv',
|
||||
nodeType: activeNode.value?.type,
|
||||
},
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,6 +534,8 @@ async function onCalloutDismiss(parameter: INodeProperties) {
|
||||
<template v-else-if="parameter.type === 'callout'">
|
||||
<N8nCallout
|
||||
v-if="isCalloutVisible(parameter)"
|
||||
:icon="(parameter.typeOptions?.calloutAction?.icon as IconName) || 'info'"
|
||||
icon-size="large"
|
||||
:class="['parameter-item', parameter.typeOptions?.containerClass ?? '']"
|
||||
theme="secondary"
|
||||
>
|
||||
@@ -499,7 +552,7 @@ async function onCalloutDismiss(parameter: INodeProperties) {
|
||||
size="small"
|
||||
:bold="true"
|
||||
:underline="true"
|
||||
@click="onCalloutAction(parameter.typeOptions.calloutAction.type)"
|
||||
@click="onCalloutAction(parameter.typeOptions.calloutAction)"
|
||||
>
|
||||
{{ parameter.typeOptions.calloutAction.label }}
|
||||
</N8nLink>
|
||||
|
||||
Reference in New Issue
Block a user