mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 05:17:28 -08:00
fix(editor): Fix performance issue in credentials list (#10988)
This commit is contained in:
parent
d2bc0760e2
commit
7073ec6fe5
|
@ -30,18 +30,6 @@ function findReferencedMethods(obj, refs = {}, latestName = '') {
|
||||||
const loader = new PackageDirectoryLoader(packageDir);
|
const loader = new PackageDirectoryLoader(packageDir);
|
||||||
await loader.loadAll();
|
await loader.loadAll();
|
||||||
|
|
||||||
const knownCredentials = loader.known.credentials;
|
|
||||||
const credentialTypes = Object.values(loader.credentialTypes).map((data) => {
|
|
||||||
const credentialType = data.type;
|
|
||||||
if (
|
|
||||||
knownCredentials[credentialType.name].supportedNodes?.length > 0 &&
|
|
||||||
credentialType.httpRequestNode
|
|
||||||
) {
|
|
||||||
credentialType.httpRequestNode.hidden = true;
|
|
||||||
}
|
|
||||||
return credentialType;
|
|
||||||
});
|
|
||||||
|
|
||||||
const loaderNodeTypes = Object.values(loader.nodeTypes);
|
const loaderNodeTypes = Object.values(loader.nodeTypes);
|
||||||
|
|
||||||
const definedMethods = loaderNodeTypes.reduce((acc, cur) => {
|
const definedMethods = loaderNodeTypes.reduce((acc, cur) => {
|
||||||
|
@ -76,6 +64,36 @@ function findReferencedMethods(obj, refs = {}, latestName = '') {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const knownCredentials = loader.known.credentials;
|
||||||
|
const credentialTypes = Object.values(loader.credentialTypes).map((data) => {
|
||||||
|
const credentialType = data.type;
|
||||||
|
const supportedNodes = knownCredentials[credentialType.name].supportedNodes ?? [];
|
||||||
|
if (supportedNodes.length > 0 && credentialType.httpRequestNode) {
|
||||||
|
credentialType.httpRequestNode.hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
credentialType.supportedNodes = supportedNodes;
|
||||||
|
|
||||||
|
if (!credentialType.iconUrl && !credentialType.icon) {
|
||||||
|
for (const supportedNode of supportedNodes) {
|
||||||
|
const nodeType = loader.nodeTypes[supportedNode]?.type.description;
|
||||||
|
|
||||||
|
if (!nodeType) continue;
|
||||||
|
if (nodeType.icon) {
|
||||||
|
credentialType.icon = nodeType.icon;
|
||||||
|
credentialType.iconColor = nodeType.iconColor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nodeType.iconUrl) {
|
||||||
|
credentialType.iconUrl = nodeType.iconUrl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentialType;
|
||||||
|
});
|
||||||
|
|
||||||
const referencedMethods = findReferencedMethods(nodeTypes);
|
const referencedMethods = findReferencedMethods(nodeTypes);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
|
|
@ -1,50 +1,59 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
import { useRootStore } from '@/stores/root.store';
|
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import type { ICredentialType } from 'n8n-workflow';
|
import { useRootStore } from '@/stores/root.store';
|
||||||
import NodeIcon from '@/components/NodeIcon.vue';
|
|
||||||
import { getThemedValue } from '@/utils/nodeTypesUtils';
|
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
|
import { getThemedValue } from '@/utils/nodeTypesUtils';
|
||||||
|
import { N8nNodeIcon } from 'n8n-design-system';
|
||||||
|
import type { ICredentialType } from 'n8n-workflow';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
credentialTypeName: string | null;
|
credentialTypeName: string | null;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const credentialsStore = useCredentialsStore();
|
const credentialsStore = useCredentialsStore();
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
|
|
||||||
const credentialWithIcon = computed(() => getCredentialWithIcon(props.credentialTypeName));
|
const credentialWithIcon = computed(() => getCredentialWithIcon(props.credentialTypeName));
|
||||||
|
|
||||||
const filePath = computed(() => {
|
const nodeBasedIconUrl = computed(() => {
|
||||||
const themeIconUrl = getThemedValue(credentialWithIcon.value?.iconUrl, uiStore.appliedTheme);
|
const icon = getThemedValue(credentialWithIcon.value?.icon);
|
||||||
|
if (!icon?.startsWith('node:')) return null;
|
||||||
|
return nodeTypesStore.getNodeType(icon.replace('node:', ''))?.iconUrl;
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconSource = computed(() => {
|
||||||
|
const themeIconUrl = getThemedValue(
|
||||||
|
nodeBasedIconUrl.value ?? credentialWithIcon.value?.iconUrl,
|
||||||
|
uiStore.appliedTheme,
|
||||||
|
);
|
||||||
|
|
||||||
if (!themeIconUrl) {
|
if (!themeIconUrl) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootStore.baseUrl + themeIconUrl;
|
return rootStore.baseUrl + themeIconUrl;
|
||||||
});
|
});
|
||||||
|
|
||||||
const relevantNode = computed(() => {
|
const iconType = computed(() => {
|
||||||
const icon = credentialWithIcon.value?.icon;
|
if (iconSource.value) return 'file';
|
||||||
if (typeof icon === 'string' && icon.startsWith('node:')) {
|
else if (iconName.value) return 'icon';
|
||||||
const nodeType = icon.replace('node:', '');
|
return 'unknown';
|
||||||
return nodeTypesStore.getNodeType(nodeType);
|
});
|
||||||
}
|
|
||||||
if (!props.credentialTypeName) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodesWithAccess = credentialsStore.getNodesWithAccess(props.credentialTypeName);
|
const iconName = computed(() => {
|
||||||
if (nodesWithAccess.length) {
|
const icon = getThemedValue(credentialWithIcon.value?.icon, uiStore.appliedTheme);
|
||||||
return nodesWithAccess[0];
|
if (!icon || !icon?.startsWith('fa:')) return undefined;
|
||||||
}
|
return icon.replace('fa:', '');
|
||||||
|
});
|
||||||
|
|
||||||
return null;
|
const iconColor = computed(() => {
|
||||||
|
const { iconColor: color } = credentialWithIcon.value ?? {};
|
||||||
|
if (!color) return undefined;
|
||||||
|
return `var(--color-node-icon-${color})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
function getCredentialWithIcon(name: string | null): ICredentialType | null {
|
function getCredentialWithIcon(name: string | null): ICredentialType | null {
|
||||||
|
@ -64,8 +73,8 @@ function getCredentialWithIcon(name: string | null): ICredentialType | null {
|
||||||
|
|
||||||
if (type.extends) {
|
if (type.extends) {
|
||||||
let parentCred = null;
|
let parentCred = null;
|
||||||
type.extends.forEach((iconName) => {
|
type.extends.forEach((credType) => {
|
||||||
parentCred = getCredentialWithIcon(iconName);
|
parentCred = getCredentialWithIcon(credType);
|
||||||
if (parentCred !== null) return;
|
if (parentCred !== null) return;
|
||||||
});
|
});
|
||||||
return parentCred;
|
return parentCred;
|
||||||
|
@ -76,23 +85,18 @@ function getCredentialWithIcon(name: string | null): ICredentialType | null {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<N8nNodeIcon
|
||||||
<img v-if="filePath" :class="$style.credIcon" :src="filePath" />
|
:class="$style.icon"
|
||||||
<NodeIcon v-else-if="relevantNode" :node-type="relevantNode" :size="28" />
|
:type="iconType"
|
||||||
<span v-else :class="$style.fallback"></span>
|
:size="26"
|
||||||
</div>
|
:src="iconSource"
|
||||||
|
:name="iconName"
|
||||||
|
:color="iconColor"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.credIcon {
|
.icon {
|
||||||
height: 26px;
|
--node-icon-color: var(--color-foreground-dark);
|
||||||
}
|
|
||||||
|
|
||||||
.fallback {
|
|
||||||
height: 28px;
|
|
||||||
width: 28px;
|
|
||||||
display: flex;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--color-foreground-base);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -60,6 +60,7 @@ function getNodeTypeBase(nodeTypeDescription: INodeTypeDescription, label?: stri
|
||||||
categories: [category],
|
categories: [category],
|
||||||
},
|
},
|
||||||
iconUrl: nodeTypeDescription.iconUrl,
|
iconUrl: nodeTypeDescription.iconUrl,
|
||||||
|
iconColor: nodeTypeDescription.iconColor,
|
||||||
outputs: nodeTypeDescription.outputs,
|
outputs: nodeTypeDescription.outputs,
|
||||||
icon: nodeTypeDescription.icon,
|
icon: nodeTypeDescription.icon,
|
||||||
defaults: nodeTypeDescription.defaults,
|
defaults: nodeTypeDescription.defaults,
|
||||||
|
|
|
@ -1,66 +1,111 @@
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia, type TestingPinia } from '@pinia/testing';
|
||||||
|
import type { ICredentialType, INodeTypeDescription } from 'n8n-workflow';
|
||||||
import { mock } from 'vitest-mock-extended';
|
import { mock } from 'vitest-mock-extended';
|
||||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
|
||||||
|
|
||||||
import CredentialIcon from '@/components/CredentialIcon.vue';
|
import CredentialIcon from '@/components/CredentialIcon.vue';
|
||||||
import { STORES } from '@/constants';
|
|
||||||
import { groupNodeTypesByNameAndType } from '@/utils/nodeTypes/nodeTypeTransforms';
|
|
||||||
|
|
||||||
import { createComponentRenderer } from '@/__tests__/render';
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
|
import { useRootStore } from '@/stores/root.store';
|
||||||
|
import { useNodeTypesStore } from '../../stores/nodeTypes.store';
|
||||||
|
|
||||||
const twitterV1 = mock<INodeTypeDescription>({
|
describe('CredentialIcon', () => {
|
||||||
version: 1,
|
|
||||||
credentials: [{ name: 'twitterOAuth1Api', required: true }],
|
|
||||||
iconUrl: 'icons/n8n-nodes-base/dist/nodes/Twitter/x.svg',
|
|
||||||
});
|
|
||||||
|
|
||||||
const twitterV2 = mock<INodeTypeDescription>({
|
|
||||||
version: 2,
|
|
||||||
credentials: [{ name: 'twitterOAuth2Api', required: true }],
|
|
||||||
iconUrl: 'icons/n8n-nodes-base/dist/nodes/Twitter/x.svg',
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodeTypes = groupNodeTypesByNameAndType([twitterV1, twitterV2]);
|
|
||||||
const initialState = {
|
|
||||||
[STORES.CREDENTIALS]: {},
|
|
||||||
[STORES.NODE_TYPES]: { nodeTypes },
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderComponent = createComponentRenderer(CredentialIcon, {
|
const renderComponent = createComponentRenderer(CredentialIcon, {
|
||||||
pinia: createTestingPinia({ initialState }),
|
pinia: createTestingPinia(),
|
||||||
global: {
|
global: {
|
||||||
stubs: ['n8n-tooltip'],
|
stubs: ['n8n-tooltip'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
let pinia: TestingPinia;
|
||||||
|
|
||||||
describe('CredentialIcon', () => {
|
beforeEach(() => {
|
||||||
const findIcon = (baseElement: Element) => baseElement.querySelector('img');
|
pinia = createTestingPinia({ stubActions: false });
|
||||||
|
});
|
||||||
|
|
||||||
it('shows correct icon for credential type that is for the latest node type version', () => {
|
it('shows correct icon when iconUrl is set on credential', () => {
|
||||||
const { baseElement } = renderComponent({
|
const testIconUrl = 'icons/n8n-nodes-base/dist/nodes/Test/test.svg';
|
||||||
pinia: createTestingPinia({ initialState }),
|
useCredentialsStore().setCredentialTypes([
|
||||||
|
mock<ICredentialType>({
|
||||||
|
name: 'test',
|
||||||
|
iconUrl: testIconUrl,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { getByRole } = renderComponent({
|
||||||
|
pinia,
|
||||||
props: {
|
props: {
|
||||||
credentialTypeName: 'twitterOAuth2Api',
|
credentialTypeName: 'test',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(findIcon(baseElement)).toHaveAttribute(
|
expect(getByRole('img')).toHaveAttribute('src', useRootStore().baseUrl + testIconUrl);
|
||||||
'src',
|
|
||||||
'/icons/n8n-nodes-base/dist/nodes/Twitter/x.svg',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows correct icon for credential type that is for an older node type version', () => {
|
it('shows correct icon when icon is set on credential', () => {
|
||||||
const { baseElement } = renderComponent({
|
useCredentialsStore().setCredentialTypes([
|
||||||
pinia: createTestingPinia({ initialState }),
|
mock<ICredentialType>({
|
||||||
|
name: 'test',
|
||||||
|
icon: 'fa:clock',
|
||||||
|
iconColor: 'azure',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { getByRole } = renderComponent({
|
||||||
|
pinia,
|
||||||
props: {
|
props: {
|
||||||
credentialTypeName: 'twitterOAuth1Api',
|
credentialTypeName: 'test',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(findIcon(baseElement)).toHaveAttribute(
|
const icon = getByRole('img', { hidden: true });
|
||||||
'src',
|
expect(icon.tagName).toBe('svg');
|
||||||
'/icons/n8n-nodes-base/dist/nodes/Twitter/x.svg',
|
expect(icon).toHaveClass('fa-clock');
|
||||||
);
|
});
|
||||||
|
|
||||||
|
it('shows correct icon when credential has an icon with node: prefix', () => {
|
||||||
|
const testIconUrl = 'icons/n8n-nodes-base/dist/nodes/Test/test.svg';
|
||||||
|
useCredentialsStore().setCredentialTypes([
|
||||||
|
mock<ICredentialType>({
|
||||||
|
name: 'test',
|
||||||
|
icon: 'node:n8n-nodes-base.test',
|
||||||
|
iconColor: 'azure',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
useNodeTypesStore().setNodeTypes([
|
||||||
|
mock<INodeTypeDescription>({
|
||||||
|
version: 1,
|
||||||
|
name: 'n8n-nodes-base.test',
|
||||||
|
iconUrl: testIconUrl,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { getByRole } = renderComponent({
|
||||||
|
pinia,
|
||||||
|
props: {
|
||||||
|
credentialTypeName: 'test',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getByRole('img')).toHaveAttribute('src', useRootStore().baseUrl + testIconUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows fallback icon when icon is not found', () => {
|
||||||
|
useCredentialsStore().setCredentialTypes([
|
||||||
|
mock<ICredentialType>({
|
||||||
|
name: 'test',
|
||||||
|
icon: 'node:n8n-nodes-base.test',
|
||||||
|
iconColor: 'azure',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { baseElement } = renderComponent({
|
||||||
|
pinia,
|
||||||
|
props: {
|
||||||
|
credentialTypeName: 'test',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(baseElement.querySelector('.nodeIconPlaceholder')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,32 +1,31 @@
|
||||||
import type {
|
import type {
|
||||||
INodeUi,
|
|
||||||
IUsedCredential,
|
|
||||||
ICredentialMap,
|
ICredentialMap,
|
||||||
ICredentialsDecryptedResponse,
|
ICredentialsDecryptedResponse,
|
||||||
ICredentialsResponse,
|
ICredentialsResponse,
|
||||||
ICredentialsState,
|
ICredentialsState,
|
||||||
ICredentialTypeMap,
|
ICredentialTypeMap,
|
||||||
|
INodeUi,
|
||||||
|
IUsedCredential,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import * as credentialsApi from '@/api/credentials';
|
import * as credentialsApi from '@/api/credentials';
|
||||||
import * as credentialsEeApi from '@/api/credentials.ee';
|
import * as credentialsEeApi from '@/api/credentials.ee';
|
||||||
import { makeRestApiRequest } from '@/utils/apiUtils';
|
|
||||||
import { getAppNameFromCredType } from '@/utils/nodeTypesUtils';
|
|
||||||
import { EnterpriseEditionFeature, STORES } from '@/constants';
|
import { EnterpriseEditionFeature, STORES } from '@/constants';
|
||||||
import { i18n } from '@/plugins/i18n';
|
import { i18n } from '@/plugins/i18n';
|
||||||
|
import type { ProjectSharingData } from '@/types/projects.types';
|
||||||
|
import { makeRestApiRequest } from '@/utils/apiUtils';
|
||||||
|
import { getAppNameFromCredType } from '@/utils/nodeTypesUtils';
|
||||||
|
import { splitName } from '@/utils/projects.utils';
|
||||||
|
import { isEmpty, isPresent } from '@/utils/typesUtils';
|
||||||
import type {
|
import type {
|
||||||
ICredentialsDecrypted,
|
ICredentialsDecrypted,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
INodeCredentialTestResult,
|
INodeCredentialTestResult,
|
||||||
INodeTypeDescription,
|
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { useRootStore } from './root.store';
|
|
||||||
import { useNodeTypesStore } from './nodeTypes.store';
|
|
||||||
import { useSettingsStore } from './settings.store';
|
|
||||||
import { isEmpty } from '@/utils/typesUtils';
|
|
||||||
import type { ProjectSharingData } from '@/types/projects.types';
|
|
||||||
import { splitName } from '@/utils/projects.utils';
|
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import { useNodeTypesStore } from './nodeTypes.store';
|
||||||
|
import { useRootStore } from './root.store';
|
||||||
|
import { useSettingsStore } from './settings.store';
|
||||||
|
|
||||||
const DEFAULT_CREDENTIAL_NAME = 'Unnamed credential';
|
const DEFAULT_CREDENTIAL_NAME = 'Unnamed credential';
|
||||||
const DEFAULT_CREDENTIAL_POSTFIX = 'account';
|
const DEFAULT_CREDENTIAL_POSTFIX = 'account';
|
||||||
|
@ -131,22 +130,15 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, () => {
|
||||||
|
|
||||||
const getNodesWithAccess = computed(() => {
|
const getNodesWithAccess = computed(() => {
|
||||||
return (credentialTypeName: string) => {
|
return (credentialTypeName: string) => {
|
||||||
|
const credentialType = getCredentialTypeByName.value(credentialTypeName);
|
||||||
|
if (!credentialType) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
const allNodeTypes: INodeTypeDescription[] = nodeTypesStore.allNodeTypes;
|
|
||||||
|
|
||||||
return allNodeTypes.filter((nodeType: INodeTypeDescription) => {
|
return (credentialType.supportedNodes ?? [])
|
||||||
if (!nodeType.credentials) {
|
.map((nodeType) => nodeTypesStore.getNodeType(nodeType))
|
||||||
return false;
|
.filter(isPresent);
|
||||||
}
|
|
||||||
|
|
||||||
for (const credentialTypeDescription of nodeType.credentials) {
|
|
||||||
if (credentialTypeDescription.name === credentialTypeName) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -314,6 +314,7 @@ export interface ICredentialType {
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
icon?: Icon;
|
icon?: Icon;
|
||||||
|
iconColor?: ThemeIconColor;
|
||||||
iconUrl?: Themed<string>;
|
iconUrl?: Themed<string>;
|
||||||
extends?: string[];
|
extends?: string[];
|
||||||
properties: INodeProperties[];
|
properties: INodeProperties[];
|
||||||
|
@ -327,6 +328,7 @@ export interface ICredentialType {
|
||||||
test?: ICredentialTestRequest;
|
test?: ICredentialTestRequest;
|
||||||
genericAuth?: boolean;
|
genericAuth?: boolean;
|
||||||
httpRequestNode?: ICredentialHttpRequestNode;
|
httpRequestNode?: ICredentialHttpRequestNode;
|
||||||
|
supportedNodes?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICredentialTypes {
|
export interface ICredentialTypes {
|
||||||
|
@ -1617,7 +1619,7 @@ export interface IWorkflowIssues {
|
||||||
[key: string]: INodeIssues;
|
[key: string]: INodeIssues;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodeIconColor =
|
export type ThemeIconColor =
|
||||||
| 'gray'
|
| 'gray'
|
||||||
| 'black'
|
| 'black'
|
||||||
| 'blue'
|
| 'blue'
|
||||||
|
@ -1642,7 +1644,7 @@ export interface INodeTypeBaseDescription {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
name: string;
|
name: string;
|
||||||
icon?: Icon;
|
icon?: Icon;
|
||||||
iconColor?: NodeIconColor;
|
iconColor?: ThemeIconColor;
|
||||||
iconUrl?: Themed<string>;
|
iconUrl?: Themed<string>;
|
||||||
badgeIconUrl?: Themed<string>;
|
badgeIconUrl?: Themed<string>;
|
||||||
group: string[];
|
group: string[];
|
||||||
|
|
Loading…
Reference in a new issue