feat(editor): Update icons to Lucide icons (#16231)

Co-authored-by: Mutasem Aldmour <mutasem@n8n.io>
This commit is contained in:
Alex Grozav
2025-06-30 18:11:09 +03:00
committed by GitHub
parent 3e04566845
commit ed2cb3c701
262 changed files with 2872 additions and 3443 deletions

View File

@@ -70,6 +70,7 @@ import type { BulkCommand, Undoable } from '@/models/history';
import type { ProjectSharingData } from '@/types/projects.types';
import type { PathItem } from '@n8n/design-system/components/N8nBreadcrumbs/Breadcrumbs.vue';
import { type IconName } from '@n8n/design-system/src/components/N8nIcon/icons';
export * from '@n8n/design-system/types';
@@ -668,7 +669,7 @@ export type SimplifiedNodeType = Pick<
export interface SubcategoryItemProps {
description?: string;
iconType?: string;
icon?: string;
icon?: IconName;
iconProps?: {
color?: string;
};
@@ -1072,7 +1073,7 @@ export interface ITab<Value extends string | number = string | number> {
value: Value;
label?: string;
href?: string;
icon?: string;
icon?: IconName;
align?: 'right';
tooltip?: string;
}

View File

@@ -137,7 +137,7 @@ const onBlur = (): void => {
type="tertiary"
text
size="mini"
icon="trash"
icon="trash-2"
data-test-id="assignment-remove"
:class="[$style.iconButton, $style.extraTopPadding]"
@click="onRemove"

View File

@@ -3,6 +3,7 @@ import { useI18n } from '@n8n/i18n';
import type { BaseTextKey } from '@n8n/i18n';
import { ASSIGNMENT_TYPES } from './constants';
import { computed } from 'vue';
import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
interface Props {
modelValue: string;
@@ -19,7 +20,9 @@ const i18n = useI18n();
const types = ASSIGNMENT_TYPES;
const icon = computed(() => types.find((type) => type.type === props.modelValue)?.icon ?? 'cube');
const icon = computed(
(): IconName => types.find((type) => type.type === props.modelValue)?.icon ?? 'box',
);
const onTypeChange = (type: string): void => {
emit('update:model-value', type);

View File

@@ -1,7 +1,9 @@
export const ASSIGNMENT_TYPES = [
{ type: 'string', icon: 'font' },
{ type: 'number', icon: 'hashtag' },
{ type: 'boolean', icon: 'check-square' },
import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
export const ASSIGNMENT_TYPES: Array<{ type: string; icon: IconName }> = [
{ type: 'string', icon: 'case-upper' },
{ type: 'number', icon: 'hash' },
{ type: 'boolean', icon: 'square-check' },
{ type: 'array', icon: 'list' },
{ type: 'object', icon: 'cube' },
{ type: 'object', icon: 'box' },
];

View File

@@ -37,8 +37,8 @@ const onClick = () => {
<template>
<el-tag :type="theme" :disable-transitions="true" :class="$style.container">
<font-awesome-icon
:icon="theme === 'success' ? 'check-circle' : 'exclamation-triangle'"
<n8n-icon
:icon="theme === 'success' ? 'circle-check' : 'triangle-alert'"
:class="theme === 'success' ? $style.icon : $style.dangerIcon"
/>
<div :class="$style.banner">

View File

@@ -28,7 +28,7 @@ const onClick = () => {
data-test-id="close-become-template-creator-cta"
@click="store.dismissCta()"
>
<n8n-icon icon="times" size="xsmall" :title="i18n.baseText('generic.close')" />
<n8n-icon icon="x" size="xsmall" :title="i18n.baseText('generic.close')" />
</button>
</div>

View File

@@ -256,7 +256,7 @@ onMounted(() => {
v-text="`${prompt.length} / ${ASK_AI_MAX_PROMPT_LENGTH}`"
/>
<a href="https://docs.n8n.io/code-examples/ai-code" target="_blank" :class="$style.help">
<n8n-icon icon="question-circle" color="text-light" size="large" />{{
<n8n-icon icon="circle-help" color="text-light" size="large" />{{
i18n.baseText('codeNodeEditor.askAi.help')
}}
</a>

View File

@@ -133,7 +133,7 @@ watch(
{{ i18n.baseText('settings.communityNodes.failedToLoad.tooltip') }}
</div>
</template>
<n8n-icon icon="exclamation-triangle" color="danger" size="large" />
<n8n-icon icon="triangle-alert" color="danger" size="large" />
</n8n-tooltip>
<n8n-tooltip
v-else-if="hasUnverifiedPackagesUpdate || hasVerifiedPackageUpdate"
@@ -152,7 +152,7 @@ watch(
{{ i18n.baseText('settings.communityNodes.upToDate.tooltip') }}
</div>
</template>
<n8n-icon icon="check-circle" color="text-light" size="large" />
<n8n-icon icon="circle-check" color="text-light" size="large" />
</n8n-tooltip>
<div :class="$style.cardActions">
<n8n-action-toggle :actions="packageActions" @action="onAction"></n8n-action-toggle>

View File

@@ -110,7 +110,7 @@ const onLearnMoreLinkClick = () => {
</div>
<n8n-button
:label="i18n.baseText('settings.communityNodes.browseButton.label')"
icon="external-link-alt"
icon="external-link"
:class="$style.browseButton"
@click="openNPMPage"
/>

View File

@@ -225,7 +225,7 @@ watch(showOAuthSuccessBanner, (newValue, oldValue) => {
</script>
<template>
<n8n-callout v-if="isManaged" theme="warning" icon="exclamation-triangle">
<n8n-callout v-if="isManaged" theme="warning" icon="triangle-alert">
{{ i18n.baseText('freeAi.credits.credentials.edit') }}
</n8n-callout>
<div v-else>

View File

@@ -1106,7 +1106,7 @@ const { width } = useElementSize(credNameRef);
<n8n-icon-button
v-if="currentCredential && credentialPermissions.delete"
:title="i18n.baseText('credentialEdit.credentialEdit.delete')"
icon="trash"
icon="trash-2"
type="tertiary"
:disabled="isSaving"
:loading="isDeleting"

View File

@@ -23,7 +23,7 @@ export const TEST_CREDENTIALS: ICredentialMap = {
id: '2',
type: 'team',
name: 'Test Project',
icon: { type: 'icon', value: 'exchange-alt' },
icon: { type: 'icon', value: 'arrow-left-right' },
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
@@ -51,7 +51,7 @@ export const TEST_CREDENTIALS: ICredentialMap = {
id: '2',
type: 'team',
name: 'Test Project',
icon: { type: 'icon', value: 'exchange-alt' },
icon: { type: 'icon', value: 'arrow-left-right' },
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
@@ -107,7 +107,7 @@ export const TEST_CREDENTIALS: ICredentialMap = {
id: '2',
type: 'team',
name: 'Test Project',
icon: { type: 'icon', value: 'exchange-alt' },
icon: { type: 'icon', value: 'arrow-left-right' },
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},

View File

@@ -89,7 +89,7 @@ function openCredentialType() {
@update:model-value="onSelect"
>
<template #prefix>
<font-awesome-icon icon="search" />
<n8n-icon icon="search" />
</template>
<N8nOption
v-for="credential in credentialsStore.allCredentialTypes"

View File

@@ -462,7 +462,7 @@ async function onAskAssistantClick() {
>
<div class="copy-button">
<N8nIconButton
icon="copy"
icon="files"
type="secondary"
size="mini"
:text="true"
@@ -484,7 +484,7 @@ async function onAskAssistantClick() {
class="node-error-view__details"
>
<summary class="node-error-view__details-summary">
<font-awesome-icon class="node-error-view__details-icon" icon="angle-right" />
<n8n-icon class="node-error-view__details-icon" icon="chevron-right" />
{{
i18n.baseText('nodeErrorView.details.from', {
interpolate: { node: `${nodeDefaultName}` },
@@ -539,7 +539,7 @@ async function onAskAssistantClick() {
<details class="node-error-view__details">
<summary class="node-error-view__details-summary">
<font-awesome-icon class="node-error-view__details-icon" icon="angle-right" />
<n8n-icon class="node-error-view__details-icon" icon="chevron-right" />
{{ i18n.baseText('nodeErrorView.details.info') }}
</summary>
<div class="node-error-view__details-content">

View File

@@ -1,34 +1,37 @@
import type { TestRunRecord } from '@/api/evaluation.ee';
import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
import type { IconColor } from '@n8n/design-system/types/icon';
export const statusDictionary: Record<TestRunRecord['status'], { icon: string; color: IconColor }> =
{
new: {
icon: 'status-new',
color: 'foreground-xdark',
},
running: {
icon: 'spinner',
color: 'secondary',
},
completed: {
icon: 'status-completed',
color: 'success',
},
error: {
icon: 'exclamation-triangle',
color: 'danger',
},
cancelled: {
icon: 'status-canceled',
color: 'foreground-xdark',
},
warning: {
icon: 'status-warning',
color: 'warning',
},
success: {
icon: 'status-completed',
color: 'success',
},
};
export const statusDictionary: Record<
TestRunRecord['status'],
{ icon: IconName; color: IconColor }
> = {
new: {
icon: 'status-new',
color: 'foreground-xdark',
},
running: {
icon: 'spinner',
color: 'secondary',
},
completed: {
icon: 'status-completed',
color: 'success',
},
error: {
icon: 'triangle-alert',
color: 'danger',
},
cancelled: {
icon: 'status-canceled',
color: 'foreground-xdark',
},
warning: {
icon: 'status-warning',
color: 'warning',
},
success: {
icon: 'status-completed',
color: 'success',
},
};

View File

@@ -205,7 +205,7 @@ defineExpose({ focus, select });
square
outline
type="tertiary"
icon="external-link-alt"
icon="external-link"
size="mini"
:class="$style['expression-editor-modal-opener']"
data-test-id="expander"

View File

@@ -133,11 +133,8 @@ async function onActionDropdownClick(id: string) {
</span>
</n8n-text>
</div>
<div v-if="provider.name === 'infisical'">
<font-awesome-icon
:class="$style['warningTriangle']"
icon="exclamation-triangle"
></font-awesome-icon>
<div :class="$style.deprecationWarning" v-if="provider.name === 'infisical'">
<n8n-icon :class="$style['warningTriangle']" icon="triangle-alert" />
<N8nBadge class="mr-xs" theme="tertiary" bold data-test-id="card-badge">
{{ i18n.baseText('settings.externalSecrets.card.deprecated') }}
</N8nBadge>
@@ -193,6 +190,10 @@ async function onActionDropdownClick(id: string) {
margin-left: var(--spacing-s);
}
.deprecationWarning {
display: flex;
}
.warningTriangle {
color: var(--color-warning);
margin-right: var(--spacing-2xs);

View File

@@ -68,7 +68,7 @@ async function onUpdateConnected(value: boolean) {
<n8n-icon
v-if="provider.state === 'error'"
color="danger"
icon="exclamation-triangle"
icon="triangle-alert"
class="mr-2xs"
/>
<n8n-text :color="connectedTextColor" bold class="mr-2xs">

View File

@@ -21,10 +21,7 @@ function onFeedback(feedback: 'positive' | 'negative') {
{{ i18n.baseText('feedback.title') }}
</N8nText>
<N8nText v-else :color="modelValue === 'positive' ? 'success' : 'danger'">
<FontAwesomeIcon
:icon="modelValue === 'positive' ? 'thumbs-up' : 'thumbs-down'"
class="mr-2xs"
/>
<N8nIcon :icon="modelValue === 'positive' ? 'thumbs-up' : 'thumbs-down'" class="mr-2xs" />
{{ i18n.baseText(`feedback.${modelValue}`) }}
</N8nText>
<N8nTooltip v-if="!modelValue" :content="i18n.baseText('feedback.positive')">
@@ -33,7 +30,7 @@ function onFeedback(feedback: 'positive' | 'negative') {
data-test-id="feedback-button-positive"
@click="onFeedback('positive')"
>
<FontAwesomeIcon icon="thumbs-up" />
<N8nIcon icon="thumbs-up" />
</span>
</N8nTooltip>
<N8nTooltip v-if="!modelValue" :content="i18n.baseText('feedback.negative')">
@@ -42,7 +39,7 @@ function onFeedback(feedback: 'positive' | 'negative') {
data-test-id="feedback-button-negative"
@click="onFeedback('negative')"
>
<FontAwesomeIcon icon="thumbs-down" />
<N8nIcon icon="thumbs-down" />
</span>
</N8nTooltip>
</div>

View File

@@ -168,7 +168,7 @@ const onBlur = (): void => {
type="tertiary"
text
size="mini"
icon="trash"
icon="trash-2"
data-test-id="filter-remove-condition"
:title="i18n.baseText('filter.removeCondition')"
:class="[$style.iconButton, $style.extraTopPadding]"
@@ -231,7 +231,7 @@ const onBlur = (): void => {
<template #content>
{{ i18n.baseText('filter.condition.resolvedTrue') }}
</template>
<n8n-icon :class="$style.statusIcon" icon="check-circle" size="medium" color="text-light" />
<n8n-icon :class="$style.statusIcon" icon="circle-check" size="medium" color="text-light" />
</n8n-tooltip>
<n8n-tooltip
@@ -241,7 +241,7 @@ const onBlur = (): void => {
<template #content>
{{ i18n.baseText('filter.condition.resolvedFalse') }}
</template>
<n8n-icon :class="$style.statusIcon" icon="times-circle" size="medium" color="text-light" />
<n8n-icon :class="$style.statusIcon" icon="circle-x" size="medium" color="text-light" />
</n8n-tooltip>
</div>
</div>

View File

@@ -285,13 +285,13 @@ export const OPERATOR_GROUPS: FilterOperatorGroup[] = [
{
id: 'string',
name: 'type.string',
icon: 'font',
icon: 'case-upper',
children: OPERATORS.filter((operator) => operator.type === 'string'),
},
{
id: 'number',
name: 'type.number',
icon: 'hashtag',
icon: 'hash',
children: OPERATORS.filter((operator) => operator.type === 'number'),
},
{
@@ -303,7 +303,7 @@ export const OPERATOR_GROUPS: FilterOperatorGroup[] = [
{
id: 'boolean',
name: 'type.boolean',
icon: 'check-square',
icon: 'square-check',
children: OPERATORS.filter((operator) => operator.type === 'boolean'),
},
{
@@ -315,7 +315,7 @@ export const OPERATOR_GROUPS: FilterOperatorGroup[] = [
{
id: 'object',
name: 'type.object',
icon: 'cube',
icon: 'box',
children: OPERATORS.filter((operator) => operator.type === 'object'),
},
];

View File

@@ -1,3 +1,4 @@
import type { IconName } from '@n8n/design-system/components/N8nIcon/icons';
import type { BaseTextKey } from '@n8n/i18n';
import type { FilterConditionValue, FilterOperatorValue } from 'n8n-workflow';
@@ -8,7 +9,7 @@ export interface FilterOperator extends FilterOperatorValue {
export interface FilterOperatorGroup {
id: string;
name: BaseTextKey;
icon?: string;
icon?: IconName;
children: FilterOperator[];
}

View File

@@ -287,7 +287,7 @@ const trackWorkflowInputFieldAdded = () => {
type="tertiary"
text
size="mini"
icon="trash"
icon="trash-2"
data-test-id="fixed-collection-delete"
:title="locale.baseText('fixedCollectionParameter.deleteItem')"
@click="deleteOption(property.name, index)"
@@ -315,7 +315,7 @@ const trackWorkflowInputFieldAdded = () => {
type="tertiary"
text
size="mini"
icon="trash"
icon="trash-2"
data-test-id="fixed-collection-delete"
:title="locale.baseText('fixedCollectionParameter.deleteItem')"
@click="deleteOption(property.name)"

View File

@@ -130,6 +130,7 @@ const onBreadcrumbItemClick = async (item: PathItem) => {
:class="$style['folder-icon']"
icon="folder"
size="xlarge"
:strokeWidth="1"
/>
</template>
<template #header>

View File

@@ -1,11 +1,8 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useI18n } from '@n8n/i18n';
import {
type Project,
type ProjectIcon as ProjectIconType,
ProjectTypes,
} from '@/types/projects.types';
import { type Project, ProjectTypes } from '@/types/projects.types';
import { isIconOrEmoji, type IconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
type Props = {
currentProject: Project;
@@ -23,13 +20,15 @@ const emit = defineEmits<{
const i18n = useI18n();
const projectIcon = computed((): ProjectIconType => {
const projectIcon = computed((): IconOrEmoji => {
if (props.currentProject?.type === ProjectTypes.Personal) {
return { type: 'icon', value: 'user' };
} else if (props.currentProject?.name) {
return props.currentProject.icon ?? { type: 'icon', value: 'layer-group' };
return isIconOrEmoji(props.currentProject.icon)
? props.currentProject.icon
: { type: 'icon', value: 'layers' };
} else {
return { type: 'icon', value: 'home' };
return { type: 'icon', value: 'house' };
}
});

View File

@@ -93,7 +93,7 @@ const onClaimCreditsClicked = async () => {
<n8n-callout
v-if="userCanClaimOpenAiCredits && !showSuccessCallout"
theme="secondary"
icon="exclamation-circle"
icon="circle-alert"
>
{{
i18n.baseText('freeAi.credits.callout.claim.title', {
@@ -110,7 +110,7 @@ const onClaimCreditsClicked = async () => {
/>
</template>
</n8n-callout>
<n8n-callout v-else-if="showSuccessCallout" theme="success" icon="check-circle">
<n8n-callout v-else-if="showSuccessCallout" theme="success" icon="circle-check">
<n8n-text size="small">
{{
i18n.baseText('freeAi.credits.callout.success.title.part1', {

View File

@@ -298,7 +298,7 @@ const onUpdate = (change: FormFieldValueUpdate) => {
<el-col :span="5" :offset="19">
<n8n-button
data-test-id="execute-workflow-button"
icon="flask"
icon="flask-conical"
:label="i18n.baseText('fromAiParametersModal.execute')"
@click="onExecute"
/>

View File

@@ -1,40 +0,0 @@
<template>
<div :class="$style['gift-icon']">
<font-awesome-icon icon="gift" />
<div :class="$style['notification']">
<div></div>
</div>
</div>
</template>
<style lang="scss" module>
.gift-icon {
display: flex;
position: relative;
svg {
margin-right: 0 !important;
}
.notification {
height: 0.47em;
width: 0.47em;
border-radius: 50%;
color: $gift-notification-active-color;
position: absolute;
background-color: $gift-notification-outer-color;
right: -0.3em;
display: flex;
align-items: center;
justify-content: center;
top: -0.148em;
div {
height: 0.36em;
width: 0.36em;
background-color: $gift-notification-inner-color;
border-radius: 50%;
}
}
}
</style>

View File

@@ -13,7 +13,7 @@ const navigateTo = () => {
<template>
<div :class="$style.wrapper" @click="navigateTo">
<font-awesome-icon :class="$style.icon" icon="arrow-left" />
<n8n-icon :class="$style.icon" icon="arrow-left" />
<div :class="$style.text" v-text="i18n.baseText('template.buttons.goBackButton')" />
</div>
</template>

View File

@@ -1,164 +0,0 @@
<script setup lang="ts">
import { nextTick, ref } from 'vue';
import { useToast } from '@/composables/useToast';
import { onClickOutside } from '@vueuse/core';
import type { InputType } from '@n8n/design-system';
interface Props {
modelValue: string;
subtitle?: string;
type: InputType;
readonly?: boolean;
placeholder?: string;
maxlength?: number;
required?: boolean;
autosize?: boolean | { minRows: number; maxRows: number };
inputType?: InputType;
maxHeight?: string;
}
const props = withDefaults(defineProps<Props>(), {
placeholder: '',
maxlength: 64,
required: true,
autosize: false,
inputType: 'text',
maxHeight: '22px',
subtitle: '',
});
const emit = defineEmits<{
'update:modelValue': [value: string];
}>();
const isNameEdit = ref(false);
const nameInput = ref<HTMLInputElement | null>(null);
const { showToast } = useToast();
const onNameEdit = (value: string) => {
emit('update:modelValue', value);
};
const enableNameEdit = () => {
isNameEdit.value = true;
void nextTick(() => nameInput.value?.focus());
};
const disableNameEdit = () => {
if (!props.modelValue && props.required) {
emit('update:modelValue', `Untitled ${props.type}`);
showToast({
title: 'Error',
message: `${props.type} name cannot be empty`,
type: 'warning',
});
}
isNameEdit.value = false;
};
onClickOutside(nameInput, disableNameEdit);
</script>
<template>
<div :class="$style.container">
<span v-if="readonly" :class="$style.headline">
{{ modelValue }}
</span>
<div
v-else
:class="{
[$style.headline]: true,
[$style['headline-editable']]: true,
[$style.editing]: isNameEdit,
}"
@keydown.stop
@click="enableNameEdit"
>
<div v-if="!isNameEdit">
<span>
<n8n-text v-if="!modelValue" size="small" color="text-base">{{ placeholder }}</n8n-text>
<slot v-else>{{ modelValue }}</slot>
</span>
<i><font-awesome-icon icon="pen" /></i>
</div>
<div v-else :class="{ [$style.nameInput]: props.inputType !== 'textarea' }">
<n8n-input
ref="nameInput"
:model-value="modelValue"
size="large"
:type="inputType"
:maxlength
:placeholder
:autosize
@update:model-value="onNameEdit"
@change="disableNameEdit"
/>
</div>
</div>
<div v-if="!isNameEdit && subtitle" :class="$style.subtitle">
{{ subtitle }}
</div>
</div>
</template>
<style module lang="scss">
.container {
display: flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
min-height: 36px;
}
.headline {
font-size: var(--font-size-m);
line-height: 1.4;
margin-bottom: var(--spacing-5xs);
display: inline-block;
padding: 0 var(--spacing-4xs);
border-radius: var(--border-radius-base);
position: relative;
min-height: 22px;
max-height: v-bind(maxHeight);
font-weight: var(--font-weight-regular);
&.editing {
width: 100%;
}
i {
display: var(--headline-icon-display, none);
font-size: 0.75em;
margin-left: 8px;
color: var(--color-text-base);
}
:global(textarea) {
resize: none;
}
}
.headline-editable {
cursor: pointer;
&:hover {
background-color: var(--color-background-base);
--headline-icon-display: inline-flex;
}
}
.nameInput {
z-index: 1;
position: absolute;
margin-top: 1px;
top: 50%;
left: 0;
width: 400px;
transform: translateY(-50%);
}
.subtitle {
font-size: var(--font-size-2xs);
color: var(--color-text-light);
margin-left: 4px;
font-weight: var(--font-weight-regular);
}
</style>

View File

@@ -480,7 +480,7 @@ function activatePane() {
<template #content>
{{ i18n.baseText('ndv.input.noOutputData.hint.tooltip') }}
</template>
<N8nIcon icon="question-circle" />
<N8nIcon icon="circle-help" />
</N8nTooltip>
</template>
</i18n-t>

View File

@@ -286,7 +286,7 @@ function hideGithubButton() {
</GithubButton>
<N8nIcon
:class="$style['close-github-button']"
icon="times-circle"
icon="circle-x"
size="medium"
@click="hideGithubButton"
/>

View File

@@ -86,7 +86,7 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
{
// Link to in-app templates, available if custom templates are enabled
id: 'templates',
icon: 'box-open',
icon: 'package-open',
label: i18n.baseText('mainSidebar.templates'),
position: 'bottom',
available: settingsStore.isTemplatesEnabled && templatesStore.hasCustomTemplatesHost,
@@ -95,7 +95,7 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
{
// Link to website templates, available if custom templates are not enabled
id: 'templates',
icon: 'box-open',
icon: 'package-open',
label: i18n.baseText('mainSidebar.templates'),
position: 'bottom',
available: settingsStore.isTemplatesEnabled && !templatesStore.hasCustomTemplatesHost,
@@ -114,7 +114,7 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
},
{
id: 'insights',
icon: 'chart-bar',
icon: 'chart-column-decreasing',
label: 'Insights',
customIconSize: 'medium',
position: 'bottom',
@@ -125,7 +125,7 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
},
{
id: 'help',
icon: 'question',
icon: 'circle-help',
label: i18n.baseText('mainSidebar.help'),
position: 'bottom',
children: [
@@ -206,7 +206,7 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
),
{
id: 'full-changelog',
icon: 'external-link-alt',
icon: 'external-link',
label: i18n.baseText('mainSidebar.whatsNew.fullChangelog'),
link: {
href: RELEASE_NOTES_URL,

View File

@@ -128,7 +128,7 @@ async function pullWorkfolder() {
data-test-id="main-sidebar-source-control-connected"
>
<span :class="$style.branchName">
<n8n-icon icon="code-branch" />
<n8n-icon icon="git-branch" />
{{ currentBranch }}
</span>
<div :class="{ 'pt-xs': !isCollapsed }">

View File

@@ -241,7 +241,7 @@ onMounted(async () => {
<div>
<n8n-button
type="primary"
icon="download"
icon="hard-drive-download"
float="right"
:label="i18n.baseText('mfa.setup.step2.button.download')"
data-test-id="mfa-recovery-codes-button"

View File

@@ -9,7 +9,6 @@ import type { IUpdateInformation } from '@/Interface';
import CollectionParameter from '@/components/CollectionParameter.vue';
import ParameterInputFull from '@/components/ParameterInputFull.vue';
import { N8nButton, N8nInputLabel, N8nText } from '@n8n/design-system';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { useNDVStore } from '@/stores/ndv.store';
import { storeToRefs } from 'pinia';
@@ -127,22 +126,22 @@ const valueChanged = (parameterData: IUpdateInformation) => {
:class="parameter.type"
>
<div v-if="!isReadOnly" class="delete-item clickable">
<FontAwesomeIcon
icon="trash"
<N8nIcon
icon="trash-2"
:title="i18n.baseText('multipleParameter.deleteItem')"
@click="deleteItem(index)"
/>
<div v-if="sortable">
<FontAwesomeIcon
<N8nIcon
v-if="index !== 0"
icon="angle-up"
icon="chevron-up"
class="clickable"
:title="i18n.baseText('multipleParameter.moveUp')"
@click="moveOptionUp(index)"
/>
<FontAwesomeIcon
<N8nIcon
v-if="index !== mutableValues.length - 1"
icon="angle-down"
icon="chevron-down"
class="clickable"
:title="i18n.baseText('multipleParameter.moveDown')"
@click="moveOptionDown(index)"

View File

@@ -87,32 +87,17 @@ describe('NDVSubConnections', () => {
props: {
rootNode: node,
},
global: {
stubs: {
N8nButton: true,
},
},
});
vi.advanceTimersByTime(1000); // Event debounce time
await waitFor(() => {});
expect(getByTestId('subnode-connection-group-ai_tool-0')).toBeVisible();
expect(html()).toEqual(
`<div class="container">
<div class="connections" style="--possible-connections: 1;">
<div data-test-id="subnode-connection-group-ai_tool-0">
<div class="connectionType"><span class="connectionLabel">Tools</span>
<div>
<div class="connectedNodesWrapper" style="--nodes-length: 0;">
<div class="plusButton"><button class="button button tertiary medium withIcon square el-tooltip__trigger el-tooltip__trigger" aria-live="polite" data-test-id="add-subnode-ai_tool-0"><span class="icon"><span class="n8n-text compact size-medium regular n8n-icon n8n-icon"><!----></span></span>
<!--v-if-->
</button>
<!--teleport start-->
<!--teleport end-->
</div>
<!--v-if-->
</div>
</div>
</div>
</div>
</div>
</div>`,
);
expect(html()).toMatchSnapshot();
});
it('should not render container if no possible connections', async () => {

View File

@@ -109,7 +109,7 @@ function nodeTypeSelected(value: NodeTypeSelectedPayload[]) {
<n8n-icon-button
size="large"
type="tertiary"
:icon="['far', 'note-sticky']"
icon="sticky-note"
data-test-id="add-sticky-button"
@click="addStickyNote"
/>
@@ -131,14 +131,14 @@ function nodeTypeSelected(value: NodeTypeSelectedPayload[]) {
v-if="experimentalNdvStore.isEnabled"
type="tertiary"
size="large"
icon="expand"
icon="maximize-2"
@click="experimentalNdvStore.expandAllNodes"
/>
<n8n-icon-button
v-if="experimentalNdvStore.isEnabled"
type="tertiary"
size="large"
icon="compress"
icon="minimize-2"
@click="experimentalNdvStore.collapseAllNodes"
/>
</div>

View File

@@ -27,8 +27,8 @@ describe('CategoryItem', () => {
props: { name: 'Category Test', isTrigger: true },
});
expect(container.querySelector('[data-icon="bolt"]')).toBeInTheDocument();
expect(container.querySelector('[data-icon="zap"]')).toBeInTheDocument();
await rerender({ isTrigger: false });
expect(container.querySelector('[data-icon="bolt"]')).not.toBeInTheDocument();
expect(container.querySelector('[data-icon="zap"]')).not.toBeInTheDocument();
});
});

View File

@@ -28,11 +28,11 @@ const categoryName = computed(() => {
<div :class="{ [$style.category]: true, [$style.active]: active }">
<span :class="$style.name">
<span v-text="categoryName" />
<font-awesome-icon v-if="isTrigger" icon="bolt" size="xs" :class="$style.triggerIcon" />
<n8n-icon v-if="isTrigger" icon="zap" size="xsmall" :class="$style.triggerIcon" />
<slot />
</span>
<font-awesome-icon v-if="expanded" icon="chevron-down" :class="$style.arrow" />
<font-awesome-icon v-else :class="$style.arrow" icon="chevron-up" />
<n8n-icon v-if="expanded" icon="chevron-down" color="text-light" size="large" />
<n8n-icon v-else icon="chevron-up" color="text-light" size="large" />
</div>
</div>
</template>
@@ -72,10 +72,4 @@ const categoryName = computed(() => {
flex-grow: 1;
color: var(--color-text-dark);
}
.arrow {
font-size: var(--font-size-2xs);
width: 12px;
color: $node-creator-arrow-color;
}
</style>

View File

@@ -18,7 +18,13 @@ defineProps<Props>();
:show-action-arrow="true"
>
<template #icon>
<n8n-node-icon type="icon" :name="link.icon" :circle="false" :show-tooltip="false" />
<n8n-node-icon
type="icon"
:name="link.icon"
:circle="false"
:show-tooltip="false"
:use-updated-icons="true"
/>
</template>
</n8n-node-creator-node>
</template>

View File

@@ -212,7 +212,7 @@ function onCommunityNodeTooltipClick(event: MouseEvent) {
@click="onCommunityNodeTooltipClick"
/>
</template>
<n8n-icon size="small" :class="$style.icon" icon="cube" />
<n8n-icon size="small" :class="$style.icon" icon="box" />
</N8nTooltip>
</template>
<template #dragContent>

View File

@@ -18,7 +18,13 @@ defineProps<Props>();
:show-action-arrow="true"
>
<template #icon>
<n8n-node-icon type="icon" :name="openTemplate.icon" :circle="false" :show-tooltip="false" />
<n8n-node-icon
type="icon"
:name="openTemplate.icon"
:circle="false"
:show-tooltip="false"
:use-updated-icons="true"
/>
</template>
</n8n-node-creator-node>
</template>

View File

@@ -30,6 +30,7 @@ const subcategoryName = computed(() => camelCase(props.item.subcategory || props
:name="item.icon"
:circle="false"
:show-tooltip="false"
:use-updated-icons="true"
v-bind="item.iconProps"
/>
</template>

View File

@@ -18,7 +18,13 @@ defineProps<Props>();
:show-action-arrow="true"
>
<template #icon>
<n8n-node-icon type="icon" :name="view.icon" :circle="false" :show-tooltip="false" />
<n8n-node-icon
type="icon"
:name="view.icon"
:circle="false"
:show-tooltip="false"
:use-updated-icons="true"
/>
</template>
</n8n-node-creator-node>
</template>

View File

@@ -165,7 +165,7 @@ onBeforeUnmount(() => {
v-if="active"
:class="$style.close"
type="secondary"
icon="times"
icon="x"
aria-label="Close Node Creator"
@click="emit('closeNodeCreator')"
/>

View File

@@ -249,7 +249,7 @@ describe('NodesListPanel', () => {
expect(screen.queryAllByTestId('item-iterator-item')).toHaveLength(0);
expect(screen.queryByText("We didn't make that... yet")).toBeInTheDocument();
await fireEvent.click(container.querySelector('.clear')!);
await fireEvent.click(container.querySelector('svg[data-icon=circle-x]')!);
await nextTick();
expect(screen.queryAllByTestId('item-iterator-item')).toHaveLength(9);
});

View File

@@ -130,11 +130,7 @@ const onInstall = async () => {
</div>
<div>
<div v-if="communityNodeDetails.installed" :class="$style.installed">
<FontAwesomeIcon
v-if="!communityNodeDetails.official"
:class="$style.installedIcon"
icon="cube"
/>
<N8nIcon v-if="!communityNodeDetails.official" :class="$style.installedIcon" icon="box" />
<N8nText color="text-light" size="small" bold>
{{ i18n.baseText('communityNodeDetails.installed') }}
</N8nText>

View File

@@ -25,7 +25,7 @@ const openCommunityNodeDocsPage = () => {
<N8nText size="small" bold style="margin-right: 5px">
{{ i18n.baseText('communityNodesDocsLink.title') }}
</N8nText>
<FontAwesomeIcon icon="external-link-alt" />
<N8nIcon icon="external-link" />
</N8nLink>
</template>

View File

@@ -126,7 +126,7 @@ onMounted(async () => {
<N8nTooltip v-else placement="top">
<template #content>{{ i18n.baseText('communityNodeInfo.unverified') }}</template>
<div>
<FontAwesomeIcon :class="$style.tooltipIcon" icon="cube" />
<N8nIcon :class="$style.tooltipIcon" icon="box" />
<N8nText color="text-light" size="xsmall" bold>
{{ i18n.baseText('communityNodeInfo.unverified.label') }}
</N8nText>
@@ -134,21 +134,21 @@ onMounted(async () => {
</N8nTooltip>
<div v-if="downloads">
<FontAwesomeIcon :class="$style.tooltipIcon" icon="download" />
<N8nIcon :class="$style.tooltipIcon" icon="hard-drive-download" />
<N8nText color="text-light" size="xsmall" bold data-test-id="number-of-downloads">
{{ i18n.baseText('communityNodeInfo.downloads', { interpolate: { downloads } }) }}
</N8nText>
</div>
<div v-if="publisherName">
<FontAwesomeIcon :class="$style.tooltipIcon" icon="user" />
<N8nIcon :class="$style.tooltipIcon" icon="user" />
<N8nText color="text-light" size="xsmall" bold data-test-id="publisher-name">
{{ i18n.baseText('communityNodeInfo.publishedBy', { interpolate: { publisherName } }) }}
</N8nText>
</div>
</div>
<div v-if="!isOwner && !communityNodeDetails?.installed" :class="$style.contactOwnerHint">
<N8nIcon color="text-light" icon="info-circle" size="large" />
<N8nIcon color="text-light" icon="info" size="large" />
<N8nText color="text-base" size="medium">
<div style="padding-bottom: 8px">
{{ i18n.baseText('communityNodeInfo.contact.admin') }}

View File

@@ -15,7 +15,7 @@ defineProps<Props>();
<template>
<div v-if="isOwner" :class="$style.container">
<N8nIcon color="text-light" icon="info-circle" size="large" />
<N8nIcon color="text-light" icon="info" size="large" />
<N8nText color="text-base" size="medium"> {{ hint }} </N8nText>
</div>
</template>

View File

@@ -53,9 +53,9 @@ const i18n = useI18n();
<span>{{ i18n.baseText('nodeCreator.noResults.requestTheNode') }}</span
>&nbsp;
<span>
<font-awesome-icon
<n8n-icon
:class="$style.external"
icon="external-link-alt"
icon="external-link"
:title="i18n.baseText('nodeCreator.noResults.requestTheNode')"
/>
</span>

View File

@@ -184,7 +184,7 @@ function onBackButton() {
:class="$style.backButton"
@click="onBackButton"
>
<font-awesome-icon :class="$style.backButtonIcon" icon="arrow-left" size="2x" />
<n8n-icon :class="$style.backButtonIcon" icon="arrow-left" :size="22" />
</button>
<NodeIcon
v-if="activeViewStack.nodeIcon"
@@ -193,6 +193,7 @@ function onBackButton() {
:circle="false"
:show-tooltip="false"
:size="20"
:use-updated-icons="true"
/>
<p v-if="activeViewStack.title" :class="$style.title" v-text="activeViewStack.title" />
@@ -275,12 +276,11 @@ function onBackButton() {
background: transparent;
border: none;
cursor: pointer;
padding: 0 var(--spacing-xs) 0 0;
padding: var(--spacing-2xs) var(--spacing-xs) 0 0;
}
.backButtonIcon {
color: $node-creator-arrow-color;
height: 16px;
padding: 0;
}
.nodeIcon {

View File

@@ -53,7 +53,7 @@ defineExpose({
<template>
<div :class="$style.searchContainer" data-test-id="search-bar">
<div :class="{ [$style.prefix]: true, [$style.active]: modelValue.length > 0 }">
<font-awesome-icon icon="search" size="sm" />
<n8n-icon icon="search" size="small" />
</div>
<div :class="$style.text">
<input
@@ -67,10 +67,8 @@ defineExpose({
@input="onInput"
/>
</div>
<div v-if="modelValue.length > 0" :class="$style.suffix" @click="clear">
<button :class="[$style.clear, $style.clickable]">
<font-awesome-icon icon="times-circle" />
</button>
<div v-if="modelValue.length > 0" :class="[$style.suffix, $style.clickable]" @click="clear">
<n8n-icon size="small" icon="circle-x" />
</div>
</div>
</template>
@@ -143,4 +141,8 @@ defineExpose({
fill: $node-creator-search-clear-background-color-hover;
}
}
.clickable {
cursor: pointer;
}
</style>

View File

@@ -113,7 +113,7 @@ registerKeyHook(`CategoryLeft_${props.category}`, {
>
<span v-if="mouseOverTooltip" :class="$style.mouseOverTooltip">
<n8n-tooltip placement="top" :popper-class="$style.tooltipPopper">
<n8n-icon icon="question-circle" size="small" />
<n8n-icon icon="circle-help" size="small" />
<template #content>
<div v-n8n-html="mouseOverTooltip" />
</template>

View File

@@ -55,7 +55,7 @@ const mockSubcategoryItemProps = (
): SubcategoryItemProps => ({
description: 'Sample description',
iconType: 'sampleIconType',
icon: 'sampleIcon',
icon: 'smile',
title: 'Sample title',
subcategory: 'sampleSubcategory',
defaults: { color: '#ffffff' },

View File

@@ -255,7 +255,7 @@ export function AINodesView(_nodes: SimplifiedNodeType[]): NodeView {
properties: {
title: AI_CATEGORY_DOCUMENT_LOADERS,
info: getSubcategoryInfo(AI_CATEGORY_DOCUMENT_LOADERS),
icon: 'file-import',
icon: 'file-input',
...getAISubcategoryProperties(NodeConnectionTypes.AiDocument),
},
},
@@ -343,7 +343,7 @@ export function AINodesView(_nodes: SimplifiedNodeType[]): NodeView {
properties: {
title: AI_CATEGORY_VECTOR_STORES,
info: getSubcategoryInfo(AI_CATEGORY_VECTOR_STORES),
icon: 'project-diagram',
icon: 'waypoints',
...getAISubcategoryProperties(NodeConnectionTypes.AiVectorStore),
},
},
@@ -571,7 +571,7 @@ export function RegularView(nodes: SimplifiedNodeType[]) {
category: CORE_NODES_CATEGORY,
properties: {
title: FLOWS_CONTROL_SUBCATEGORY,
icon: 'code-branch',
icon: 'git-branch',
sections: [
{
key: 'popular',

View File

@@ -582,7 +582,7 @@ async function onClickCreateCredential(type: ICredentialType | INodeCredentialDe
:items="getIssues(type.name)"
/>
</template>
<N8nIcon icon="exclamation-triangle" />
<N8nIcon icon="triangle-alert" />
</N8nTooltip>
</div>

View File

@@ -870,7 +870,7 @@ onBeforeUnmount(() => {
target="_blank"
@click="onFeatureRequestClick"
>
<font-awesome-icon icon="lightbulb" />
<n8n-icon icon="lightbulb" />
{{ i18n.baseText('ndv.featureRequest') }}
</a>
</template>

View File

@@ -31,6 +31,7 @@ import { generateCodeForAiTransform } from '@/components/ButtonParameter/utils';
import { needsAgentInput } from '@/utils/nodes/nodeTransforms';
import { useUIStore } from '@/stores/ui.store';
import type { ButtonType } from '@n8n/design-system';
import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
const NODE_TEST_STEP_POPUP_COUNT_KEY = 'N8N_NODE_TEST_STEP_POPUP_COUNT';
const MAX_POPUP_COUNT = 10;
@@ -221,9 +222,9 @@ const isLoading = computed(
(isNodeRunning.value && !isListeningForEvents.value && !isListeningForWorkflowEvents.value),
);
const buttonIcon = computed(() => {
const buttonIcon = computed((): IconName | undefined => {
if (shouldGenerateCode.value) return 'terminal';
if (!isListeningForEvents.value && !props.hideIcon) return 'flask';
if (!isListeningForEvents.value && !props.hideIcon) return 'flask-conical';
return undefined;
});

View File

@@ -801,7 +801,7 @@ function handleWheelEvent(event: WheelEvent) {
</div>
<div v-if="node && !nodeValid" class="node-is-not-valid">
<p :class="$style.warningIcon">
<font-awesome-icon icon="exclamation-triangle" />
<n8n-icon icon="triangle-alert" />
</p>
<div class="missingNodeTitleContainer mt-s mb-xs">
<n8n-text size="large" color="text-dark" bold>

View File

@@ -95,7 +95,7 @@ const options = computed<ITab[]>(() => {
if (isCommunityNode.value) {
options.push({
icon: 'cube',
icon: 'box',
value: 'communityNode',
align: 'right',
tooltip: i18n.baseText('generic.communityNode.tooltip', {

View File

@@ -188,7 +188,7 @@ watch(
:title="isMinimized ? baseText.clickToDisplay : baseText.clickToHide"
@click="isMinimized = !isMinimized"
>
<font-awesome-icon icon="angle-right" class="minimize-button minimize-icon" />
<n8n-icon icon="chevron-right" class="minimize-button minimize-icon" />
{{ baseText.toggleTitle }}
</div>
<el-collapse-transition>

View File

@@ -40,13 +40,13 @@ const onDragStart = () => {
v-if="canMoveLeft"
:class="{ [$style.leftArrow]: true, [$style.visible]: isDragging }"
>
<font-awesome-icon icon="arrow-left" />
<n8n-icon icon="arrow-left" />
</span>
<span
v-if="canMoveRight"
:class="{ [$style.rightArrow]: true, [$style.visible]: isDragging }"
>
<font-awesome-icon icon="arrow-right" />
<n8n-icon icon="arrow-right" />
</span>
<div :class="$style.grid">
<div>

View File

@@ -1399,7 +1399,7 @@ onUpdated(async () => {
<N8nIcon
v-if="!editorIsReadOnly"
data-test-id="code-editor-fullscreen-button"
icon="external-link-alt"
icon="external-link"
size="xsmall"
class="textarea-modal-opener"
:title="i18n.baseText('parameterInput.openEditWindow')"
@@ -1420,7 +1420,7 @@ onUpdated(async () => {
<template #suffix>
<N8nIcon
data-test-id="code-editor-fullscreen-button"
icon="external-link-alt"
icon="external-link"
size="xsmall"
class="textarea-modal-opener"
:title="i18n.baseText('parameterInput.openEditWindow')"
@@ -1439,7 +1439,7 @@ onUpdated(async () => {
<template #suffix>
<N8nIcon
data-test-id="code-editor-fullscreen-button"
icon="external-link-alt"
icon="external-link"
size="xsmall"
class="textarea-modal-opener"
:title="i18n.baseText('parameterInput.openEditWindow')"
@@ -1459,7 +1459,7 @@ onUpdated(async () => {
<template #suffix>
<N8nIcon
data-test-id="code-editor-fullscreen-button"
icon="external-link-alt"
icon="external-link"
size="xsmall"
class="textarea-modal-opener"
:title="i18n.baseText('parameterInput.openEditWindow')"
@@ -1480,7 +1480,7 @@ onUpdated(async () => {
<N8nIcon
v-if="!editorIsReadOnly"
data-test-id="code-editor-fullscreen-button"
icon="external-link-alt"
icon="external-link"
size="xsmall"
class="textarea-modal-opener"
:title="i18n.baseText('parameterInput.openEditWindow')"
@@ -1499,7 +1499,7 @@ onUpdated(async () => {
<template #suffix>
<N8nIcon
data-test-id="code-editor-fullscreen-button"
icon="external-link-alt"
icon="external-link"
size="xsmall"
class="textarea-modal-opener"
:title="i18n.baseText('parameterInput.openEditWindow')"
@@ -1545,7 +1545,7 @@ onUpdated(async () => {
<template #suffix>
<N8nIcon
v-if="!isReadOnly && !isSecretParameter"
icon="external-link-alt"
icon="external-link"
size="xsmall"
class="edit-window-button textarea-modal-opener"
:class="{

View File

@@ -647,7 +647,7 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
<template #trailingContent>
<N8nIcon
icon="times"
icon="x"
title="Dismiss"
size="medium"
type="secondary"
@@ -694,7 +694,7 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
issue
}}</span>
</template>
<N8nIcon icon="exclamation-triangle" size="small" color="danger" />
<N8nIcon icon="triangle-alert" size="small" color="danger" />
</N8nTooltip>
</template>
</N8nInputLabel>
@@ -721,13 +721,13 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
</template>
<template #fallback>
<N8nText size="small" class="async-notice">
<N8nIcon icon="sync-alt" size="xsmall" :spin="true" />
<N8nIcon icon="refresh-cw" size="xsmall" :spin="true" />
{{ i18n.baseText('parameterInputList.loadingFields') }}
</N8nText>
</template>
</Suspense>
<N8nText v-else size="small" color="danger" class="async-notice">
<N8nIcon icon="exclamation-triangle" size="xsmall" />
<N8nIcon icon="triangle-alert" size="xsmall" />
{{ i18n.baseText('parameterInputList.loadingError') }}
</N8nText>
<N8nIconButton
@@ -735,7 +735,7 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
type="tertiary"
text
size="mini"
icon="trash"
icon="trash-2"
class="icon-button"
:title="i18n.baseText('parameterInputList.delete')"
@click="deleteOption(parameter.name)"
@@ -778,7 +778,7 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
type="tertiary"
text
size="mini"
icon="trash"
icon="trash-2"
class="icon-button"
:title="i18n.baseText('parameterInputList.delete')"
@click="deleteOption(parameter.name)"

View File

@@ -27,7 +27,7 @@ const emit = defineEmits<{
type="tertiary"
:class="['n8n-input', $style.overrideCloseButton]"
:outline="false"
icon="xmark"
icon="x"
size="mini"
@click="emit('close')"
/>

View File

@@ -16,7 +16,7 @@ const i18n = useI18n();
<template #content>
<TitledList :title="`${i18n.baseText('parameterInput.issues')}:`" :items="issues" />
</template>
<N8nIcon icon="exclamation-triangle" />
<N8nIcon icon="triangle-alert" />
</N8nTooltip>
</div>
</template>

View File

@@ -77,7 +77,7 @@ describe('ParameterOptions', () => {
iconOrientation: 'horizontal',
},
});
expect(container.querySelector('[data-icon="ellipsis-h"]')).toBeInTheDocument();
expect(container.querySelector('[data-icon="ellipsis"]')).toBeInTheDocument();
});
it('should render custom actions', async () => {

View File

@@ -177,7 +177,7 @@ const getArgument = (argumentName: string) => {
<div :class="$style.container" data-test-id="parameter-options-container">
<div v-if="loading" :class="$style.loader" data-test-id="parameter-options-loader">
<n8n-text v-if="loading" size="small">
<n8n-icon icon="sync-alt" size="xsmall" :spin="true" />
<n8n-icon icon="refresh-cw" size="xsmall" :spin="true" />
{{ loadingMessage }}
</n8n-text>
</div>

View File

@@ -2,10 +2,11 @@
import { computed } from 'vue';
import { useI18n } from '@n8n/i18n';
import { ResourceType, splitName } from '@/utils/projects.utils';
import type { Project, ProjectIcon as BadgeIcon } from '@/types/projects.types';
import type { Project } from '@/types/projects.types';
import { ProjectTypes } from '@/types/projects.types';
import type { CredentialsResource, FolderResource, WorkflowResource } from '@/Interface';
import { VIEWS } from '@/constants';
import { type IconOrEmoji, isIconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
type Props = {
resource: WorkflowResource | CredentialsResource | FolderResource;
@@ -76,7 +77,7 @@ const badgeText = computed(() => {
}
});
const badgeIcon = computed<BadgeIcon>(() => {
const badgeIcon = computed<IconOrEmoji>(() => {
switch (projectState.value) {
case ProjectState.Owned:
case ProjectState.SharedOwned:
@@ -85,9 +86,11 @@ const badgeIcon = computed<BadgeIcon>(() => {
return { type: 'icon', value: 'user' };
case ProjectState.Team:
case ProjectState.SharedTeam:
return props.resource.homeProject?.icon ?? { type: 'icon', value: 'layer-group' };
return isIconOrEmoji(props.resource.homeProject?.icon)
? props.resource.homeProject?.icon
: { type: 'icon', value: 'layers' };
default:
return { type: 'icon', value: 'layer-group' };
return { type: 'icon', value: 'layers' };
}
});
const badgeTooltip = computed(() => {

View File

@@ -35,7 +35,7 @@ defineExpose({
<N8nIconButton
:disabled="disabled"
:class="[$style.buttonGroupDropdown]"
icon="angle-down"
icon="chevron-down"
:type="type ?? 'primary'"
/>
</N8nActionToggle>

View File

@@ -76,7 +76,7 @@ describe('ProjectHeader', () => {
vi.spyOn(projectPages, 'isOverviewSubPage', 'get').mockReturnValue(true);
const { container } = renderComponent();
expect(container.querySelector('.fa-home')).not.toBeInTheDocument();
expect(container.querySelector('svg[data-icon=home]')).not.toBeInTheDocument();
});
it('should render the correct icon', async () => {
@@ -86,12 +86,12 @@ describe('ProjectHeader', () => {
// We no longer render icon for personal project
projectsStore.currentProject = { type: ProjectTypes.Personal } as Project;
await rerender({});
expect(container.querySelector('.fa-user')).not.toBeInTheDocument();
expect(container.querySelector('svg[data-icon=user]')).not.toBeInTheDocument();
const projectName = 'My Project';
projectsStore.currentProject = { name: projectName } as Project;
await rerender({});
expect(container.querySelector('.fa-layer-group')).toBeVisible();
expect(container.querySelector('svg[data-icon=layers]')).toBeVisible();
});
it('Overview: should render the correct title and subtitle', async () => {

View File

@@ -5,7 +5,7 @@ import { useElementSize, useResizeObserver } from '@vueuse/core';
import type { UserAction } from '@n8n/design-system';
import { N8nButton, N8nTooltip } from '@n8n/design-system';
import { useI18n } from '@n8n/i18n';
import { type ProjectIcon as ProjectIconType, ProjectTypes } from '@/types/projects.types';
import { ProjectTypes } from '@/types/projects.types';
import { useProjectsStore } from '@/stores/projects.store';
import ProjectTabs from '@/components/Projects/ProjectTabs.vue';
import ProjectIcon from '@/components/Projects/ProjectIcon.vue';
@@ -16,7 +16,9 @@ import ProjectCreateResource from '@/components/Projects/ProjectCreateResource.v
import { useSettingsStore } from '@/stores/settings.store';
import { useProjectPages } from '@/composables/useProjectPages';
import { truncateTextToFitWidth } from '@/utils/formatters/textFormatter';
import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
import type { IUser } from 'n8n-workflow';
import { type IconOrEmoji, isIconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
const route = useRoute();
const router = useRouter();
@@ -30,13 +32,15 @@ const emit = defineEmits<{
createFolder: [];
}>();
const headerIcon = computed((): ProjectIconType => {
const headerIcon = computed((): IconOrEmoji => {
if (projectsStore.currentProject?.type === ProjectTypes.Personal) {
return { type: 'icon', value: 'user' };
} else if (projectsStore.currentProject?.name) {
return projectsStore.currentProject.icon ?? { type: 'icon', value: 'layer-group' };
return isIconOrEmoji(projectsStore.currentProject.icon)
? projectsStore.currentProject.icon
: { type: 'icon', value: 'layers' };
} else {
return { type: 'icon', value: 'home' };
return { type: 'icon', value: 'house' };
}
});
@@ -89,7 +93,7 @@ type ActionTypes = (typeof ACTION_TYPES)[keyof typeof ACTION_TYPES];
const createWorkflowButton = computed(() => ({
value: ACTION_TYPES.WORKFLOW,
label: i18n.baseText('projects.header.create.workflow'),
icon: sourceControlStore.preferences.branchReadOnly ? 'lock' : undefined,
icon: sourceControlStore.preferences.branchReadOnly ? ('lock' as IconName) : undefined,
size: 'mini' as const,
disabled:
sourceControlStore.preferences.branchReadOnly ||

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import type { ProjectIcon } from '@/types/projects.types';
import { type IconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
type Props = {
icon: ProjectIcon;
icon: IconOrEmoji;
size?: 'mini' | 'small' | 'medium' | 'large';
round?: boolean;
borderLess?: boolean;

View File

@@ -28,7 +28,7 @@ const isFoldersFeatureEnabled = computed(() => settingsStore.isFoldersFeatureEna
const home = computed<IMenuItem>(() => ({
id: 'home',
label: locale.baseText('projects.menu.overview'),
icon: 'home',
icon: 'house',
route: {
to: { name: VIEWS.HOMEPAGE },
},

View File

@@ -4,12 +4,12 @@ import { computed, ref, watch } from 'vue';
import { useI18n } from '@n8n/i18n';
import {
ProjectTypes,
type ProjectIcon as ProjectIconItem,
type ProjectListItem,
type ProjectSharingData,
} from '@/types/projects.types';
import ProjectSharingInfo from '@/components/Projects/ProjectSharingInfo.vue';
import { sortByProperty } from '@n8n/utils/sort/sortByProperty';
import { isIconOrEmoji, type IconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
const locale = useI18n();
@@ -51,14 +51,14 @@ const filteredProjects = computed(() =>
),
);
const projectIcon = computed<ProjectIconItem>(() => {
const defaultIcon: ProjectIconItem = { type: 'icon', value: 'layer-group' };
const projectIcon = computed<IconOrEmoji>(() => {
const defaultIcon: IconOrEmoji = { type: 'icon', value: 'layers' };
const project = props.projects.find((p) => p.id === selectedProject.value);
if (project?.type === ProjectTypes.Personal) {
return { type: 'icon', value: 'user' };
} else if (project?.type === ProjectTypes.Team) {
return project.icon ?? defaultIcon;
return isIconOrEmoji(project.icon) ? project.icon : defaultIcon;
}
return defaultIcon;
@@ -171,7 +171,7 @@ watch(
type="tertiary"
native-type="button"
square
icon="trash"
icon="trash-2"
:disabled="props.readonly"
data-test-id="project-sharing-remove"
@click="onRoleAction(project, 'remove')"

View File

@@ -2,6 +2,7 @@
import { computed } from 'vue';
import type { ProjectListItem, ProjectSharingData } from '@/types/projects.types';
import { splitName } from '@/utils/projects.utils';
import { isIconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
type Props = {
project: ProjectListItem | ProjectSharingData;
@@ -21,7 +22,7 @@ const processedName = computed(() => {
});
const projectIcon = computed(() => {
if (props.project.icon) {
if (props.project.icon && isIconOrEmoji(props.project.icon)) {
return props.project.icon;
}
return null;

View File

@@ -29,7 +29,9 @@ describe('PushConnectionTracker', () => {
});
setActivePinia(pinia);
return createComponentRenderer(PushConnectionTracker)();
return createComponentRenderer(PushConnectionTracker, {
global: { stubs: { N8nIcon: true } },
})();
};
it('should not render error when connected and connection requested', () => {

View File

@@ -23,7 +23,7 @@ const showConnectionLostError = computed(() => {
<div v-n8n-html="i18n.baseText('pushConnectionTracker.cannotConnectToServer')"></div>
</template>
<span>
<font-awesome-icon icon="exclamation-triangle" />&nbsp;
<n8n-icon icon="triangle-alert" />&nbsp;
{{ i18n.baseText('pushConnectionTracker.connectionLost') }}
</span>
</n8n-tooltip>

View File

@@ -1069,7 +1069,7 @@ function removeOverride() {
/>
<div v-else-if="urlValue" :class="$style.openResourceLink">
<n8n-link theme="text" @click.stop="openResource(urlValue)">
<font-awesome-icon icon="external-link-alt" :title="getLinkAlt(valueToDisplay)" />
<n8n-icon icon="external-link" :title="getLinkAlt(valueToDisplay)" />
</n8n-link>
</div>
</div>

View File

@@ -244,7 +244,7 @@ defineExpose({ isWithinDropdown });
@update:model-value="onFilterInput"
>
<template #prefix>
<font-awesome-icon :class="$style.searchIcon" icon="search" />
<n8n-icon :class="$style.searchIcon" icon="search" />
</template>
</N8nInput>
</div>
@@ -278,7 +278,7 @@ defineExpose({ isWithinDropdown });
>
<div :class="$style.resourceNameContainer">
<span :class="$style.addResourceText">{{ allowNewResources.label }}</span>
<font-awesome-icon :class="$style.addResourceIcon" :icon="['fa', 'plus']" />
<n8n-icon :class="$style.addResourceIcon" icon="plus" />
</div>
</div>
<div
@@ -304,9 +304,9 @@ defineExpose({ isWithinDropdown });
</span>
</div>
<div :class="$style.urlLink">
<font-awesome-icon
<n8n-icon
v-if="showHoverUrl && result.url && hoverIndex === i + 1"
icon="external-link-alt"
icon="external-link"
:title="result.linkAlt || i18n.baseText('resourceLocator.mode.list.openUrl')"
@click="openUrl($event, result.url)"
/>

View File

@@ -319,10 +319,10 @@ defineExpose({
})
}}</span>
</template>
<N8nIcon icon="exclamation-triangle" size="small" color="warning" />
<N8nIcon icon="triangle-alert" size="small" color="warning" />
</N8nTooltip>
<N8nIconButton
icon="refresh"
icon="refresh-cw"
type="tertiary"
size="small"
:text="true"
@@ -368,7 +368,7 @@ defineExpose({
})
}}</span>
</template>
<font-awesome-icon icon="question-circle" />
<N8nIcon icon="circle-help" />
</N8nTooltip>
</div>
<div
@@ -385,7 +385,7 @@ defineExpose({
type="tertiary"
text
size="mini"
icon="trash"
icon="trash-2"
:data-test-id="`remove-field-button-${getParsedFieldName(field.name)}`"
:title="
locale.baseText('resourceMapper.removeField', {

View File

@@ -138,7 +138,7 @@ defineExpose({
</div>
<div class="mt-5xs">
<N8nText v-if="loading" size="small">
<N8nIcon icon="sync-alt" size="xsmall" :spin="true" />
<N8nIcon icon="refresh-cw" size="xsmall" :spin="true" />
{{
locale.baseText('resourceMapper.fetchingFields.message', {
interpolate: {
@@ -148,7 +148,7 @@ defineExpose({
}}
</N8nText>
<N8nText v-else-if="errorMessage !== ''" size="small" color="danger">
<N8nIcon icon="exclamation-triangle" size="xsmall" />
<N8nIcon icon="triangle-alert" size="xsmall" />
{{ errorMessage }}
<N8nLink size="small" theme="danger" :underline="true" @click="onRetryClick">
{{ locale.baseText('generic.retry') }}

View File

@@ -598,7 +598,7 @@ defineExpose({
@refresh-field-list="initFetching(true)"
/>
<N8nText v-if="!showMappingModeSelect && state.loading" size="small">
<N8nIcon icon="sync-alt" size="xsmall" :spin="true" />
<N8nIcon icon="refresh-cw" size="xsmall" :spin="true" />
{{
locale.baseText('resourceMapper.fetchingFields.message', {
interpolate: {
@@ -639,7 +639,7 @@ defineExpose({
<template #trailingContent>
<N8nButton
size="mini"
icon="refresh"
icon="refresh-cw"
type="secondary"
:loading="state.refreshInProgress"
@click="initFetching(true)"

View File

@@ -1355,7 +1355,7 @@ defineExpose({ enterEditMode });
!isProductionExecutionPreview
"
theme="secondary"
icon="thumbtack"
icon="pin"
:class="$style.pinnedDataCallout"
data-test-id="ndv-pinned-data-callout"
>
@@ -1444,7 +1444,7 @@ defineExpose({ enterEditMode });
:title="i18n.baseText('runData.editOutput')"
:circle="false"
:disabled="node?.disabled"
icon="pencil-alt"
icon="pencil"
type="tertiary"
data-test-id="ndv-edit-pinned-data"
@click="enterEditMode({ origin: 'editIconButton' })"

View File

@@ -7,7 +7,6 @@ import type { NodeConnectionType, NodeError } from 'n8n-workflow';
import RunDataAi from '@/components/RunDataParsedAiContent.vue';
import { parseAiContent } from '@/utils/aiUtils';
import { N8nRadioButtons } from '@n8n/design-system';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
const props = defineProps<{
runData: IAiDataContent;
@@ -52,7 +51,7 @@ function onRenderTypeChange(value: 'rendered' | 'json') {
<div :class="$style.block">
<header :class="$style.blockHeader" @click="onBlockHeaderClick">
<button :class="$style.blockToggle">
<FontAwesomeIcon :icon="isExpanded ? 'angle-down' : 'angle-right'" size="lg" />
<N8nIcon :icon="isExpanded ? 'chevron-down' : 'chevron-right'" size="large" />
</button>
<p :class="$style.blockTitle">{{ capitalize(runData.inOut) }}</p>
<N8nRadioButtons

View File

@@ -117,7 +117,7 @@ watch(() => props.runIndex, selectFirst, { immediate: true });
:class="$style.treeToggle"
@click="toggleTreeItem(currentNode)"
>
<font-awesome-icon :icon="currentNode.expanded ? 'angle-down' : 'angle-right'" />
<n8n-icon :icon="currentNode.expanded ? 'chevron-down' : 'chevron-right'" />
</button>
<n8n-tooltip :disabled="!slim" placement="right">
<template #content>

View File

@@ -65,7 +65,7 @@ watch(
<N8nIcon v-else-if="option.value === 'json'" icon="json" size="small" />
<N8nIcon v-else-if="option.value === 'binary'" icon="binary" size="small" />
<N8nIcon v-else-if="option.value === 'schema'" icon="schema" size="small" />
<N8nIcon v-else-if="option.value === 'html'" icon="html" size="small" />
<N8nIcon v-else-if="option.value === 'html'" icon="file-code" size="small" />
<N8nIcon v-else-if="option.value === 'ai'" icon="text" size="small" />
<span v-else>{{ option.label }}</span>
</template>

View File

@@ -180,7 +180,7 @@ function handleCopyClick(commandData: { command: string }) {
<n8n-icon-button
v-if="noSelection"
:title="i18n.baseText('runData.copyToClipboard')"
icon="copy"
icon="files"
type="tertiary"
:circle="false"
@click="handleCopyClick({ command: 'value' })"
@@ -189,7 +189,7 @@ function handleCopyClick(commandData: { command: string }) {
<span class="el-dropdown-link">
<n8n-icon-button
:title="i18n.baseText('runData.copyToClipboard')"
icon="copy"
icon="files"
type="tertiary"
:circle="false"
/>

View File

@@ -128,7 +128,7 @@ function onCopyToClipboard(object: IDataObject | IDataObject[]) {
:class="$style.copyToClipboard"
type="secondary"
:title="i18n.baseText('nodeErrorView.copyToClipboard')"
icon="copy"
icon="files"
@click="onCopyToClipboard(raw)"
/>
<VueMarkdown :source="jsonToMarkdown(raw as JsonMarkdown)" :class="$style.markdown" />

View File

@@ -56,7 +56,7 @@ const visible = computed(() =>
:class="$style.pinDataButton"
type="tertiary"
:active="props.pinnedData.hasData.value"
icon="thumbtack"
icon="pin"
:disabled="props.disabled"
data-test-id="ndv-pin-data"
@click="emit('togglePinData')"

View File

@@ -479,7 +479,7 @@ watch(focusedMappableInput, (curr) => {
v-show="showExecutionLink(index1)"
element="a"
type="secondary"
icon="external-link-alt"
icon="external-link"
data-test-id="debug-sub-execution"
size="mini"
target="_blank"
@@ -541,7 +541,7 @@ watch(focusedMappableInput, (curr) => {
:search="search"
/>
<div :class="$style.dragButton">
<font-awesome-icon icon="grip-vertical" />
<n8n-icon icon="grip-vertical" />
</div>
</div>
</template>
@@ -563,10 +563,7 @@ watch(focusedMappableInput, (curr) => {
</div>
</template>
<span>
<font-awesome-icon
:class="$style['warningTooltip']"
icon="exclamation-triangle"
></font-awesome-icon>
<n8n-icon :class="$style['warningTooltip']" icon="triangle-alert" />
{{ i18n.baseText('dataMapping.tableView.tableColumnsExceeded') }}
</span>
</N8nTooltip>
@@ -618,7 +615,7 @@ watch(focusedMappableInput, (curr) => {
v-show="showExecutionLink(index1)"
element="a"
type="secondary"
icon="external-link-alt"
icon="external-link"
data-test-id="debug-sub-execution"
size="mini"
target="_blank"

View File

@@ -52,7 +52,12 @@ const runMetadata = computed(() => {
></span>
</N8nInfoTip>
<div v-else-if="runMetadata" :class="$style.tooltipRow">
<N8nInfoTip type="note" :theme="theme" :data-test-id="`node-run-status-${theme}`" />
<N8nInfoTip
type="note"
:theme="theme"
:data-test-id="`node-run-status-${theme}`"
size="large"
/>
<N8nInfoTip
type="tooltip"
theme="info"

View File

@@ -397,7 +397,7 @@ const { width } = useElementSize(defNameRef);
<div :class="$style.destinationActions">
<n8n-button
v-if="nodeParameters && hasOnceBeenSaved && unchanged"
:icon="testMessageSent ? (testMessageResult ? 'check' : 'exclamation-triangle') : ''"
:icon="testMessageSent ? (testMessageResult ? 'check' : 'triangle-alert') : undefined"
:title="
testMessageSent && testMessageResult
? 'Event sent and returned OK'
@@ -413,7 +413,7 @@ const { width } = useElementSize(defNameRef);
<n8n-icon-button
v-if="nodeParameters && hasOnceBeenSaved"
:title="i18n.baseText('settings.log-streaming.delete')"
icon="trash"
icon="trash-2"
type="tertiary"
:disabled="isSaving"
:loading="isDeleting"

View File

@@ -85,7 +85,7 @@ export default defineComponent({
:popper-class="$style.tooltipPopper"
class="ml-xs"
>
<n8n-icon icon="question-circle" size="small" class="ml-4xs" />
<n8n-icon icon="circle-help" size="small" class="ml-4xs" />
<template #content>
{{ groupLabelInfo(group.name) }}
</template>
@@ -100,7 +100,7 @@ export default defineComponent({
>
{{ i18n.baseText('settings.log-streaming.tab.events.anonymize') }}
<n8n-tooltip placement="top" :popper-class="$style.tooltipPopper">
<n8n-icon icon="question-circle" size="small" class="ml-4xs" />
<n8n-icon icon="circle-help" size="small" class="ml-4xs" />
<template #content>
{{ i18n.baseText('settings.log-streaming.tab.events.anonymize.info') }}
</template>

View File

@@ -28,7 +28,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
const menuItems: IMenuItem[] = [
{
id: 'settings-usage-and-plan',
icon: 'chart-bar',
icon: 'chart-column-decreasing',
label: i18n.baseText('settings.usageAndPlan.title'),
position: 'top',
available: canUserAccessRouteByName(VIEWS.USAGE),
@@ -36,7 +36,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
},
{
id: 'settings-personal',
icon: 'user-circle',
icon: 'circle-user-round',
label: i18n.baseText('settings.personal'),
position: 'top',
available: canUserAccessRouteByName(VIEWS.PERSONAL_SETTINGS),
@@ -44,7 +44,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
},
{
id: 'settings-users',
icon: 'user-friends',
icon: 'user-round',
label: i18n.baseText('settings.users'),
position: 'top',
available: canUserAccessRouteByName(VIEWS.USERS_SETTINGS),
@@ -69,7 +69,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
{
id: 'settings-source-control',
icon: 'code-branch',
icon: 'git-branch',
label: i18n.baseText('settings.sourceControl.title'),
position: 'top',
available: canUserAccessRouteByName(VIEWS.SOURCE_CONTROL),
@@ -85,7 +85,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
},
{
id: 'settings-ldap',
icon: 'network-wired',
icon: 'network',
label: i18n.baseText('settings.ldap'),
position: 'top',
available: canUserAccessRouteByName(VIEWS.LDAP_SETTINGS),
@@ -93,7 +93,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
},
{
id: 'settings-workersview',
icon: 'project-diagram',
icon: 'waypoints',
label: i18n.baseText('mainSidebar.workersView'),
position: 'top',
available:
@@ -105,7 +105,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
menuItems.push({
id: 'settings-log-streaming',
icon: 'sign-in-alt',
icon: 'log-in',
label: i18n.baseText('settings.log-streaming'),
position: 'top',
available: canUserAccessRouteByName(VIEWS.LOG_STREAMING_SETTINGS),
@@ -114,7 +114,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
menuItems.push({
id: 'settings-community-nodes',
icon: 'cube',
icon: 'box',
label: i18n.baseText('settings.communityNodes'),
position: 'top',
available: canUserAccessRouteByName(VIEWS.COMMUNITY_NODES),
@@ -131,7 +131,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
<template #header>
<div :class="$style.returnButton" data-test-id="settings-back" @click="emit('return')">
<i class="mr-xs">
<font-awesome-icon icon="arrow-left" />
<n8n-icon icon="arrow-left" />
</i>
<n8n-heading size="large" :bold="true">{{ i18n.baseText('settings') }}</n8n-heading>
</div>

View File

@@ -63,7 +63,7 @@ onBeforeUnmount(() => {
:label="i18n.baseText('nodeView.setupTemplate')"
data-test-id="setup-credentials-button"
size="large"
icon="box-open"
icon="package-open"
type="secondary"
@click="handleClick()"
/>

View File

@@ -595,7 +595,7 @@ function castProject(project: ProjectListItem) {
<N8nPopover trigger="click" width="304" style="align-self: normal">
<template #reference>
<N8nButton
icon="filter"
icon="funnel"
type="tertiary"
style="height: 100%"
:active="Boolean(filterCount)"
@@ -658,7 +658,7 @@ function castProject(project: ProjectListItem) {
{{ i18n.baseText('settings.sourceControl.modals.push.projectAdmin.callout') }}
<template #trailingContent>
<N8nIcon
icon="times"
icon="x"
title="Dismiss"
size="medium"
type="secondary"

View File

@@ -233,7 +233,7 @@ onClickOutside(
:value="CREATE_KEY"
class="ops"
>
<font-awesome-icon icon="plus-circle" />
<n8n-icon icon="circle-plus" />
<span>
{{ i18n.baseText('tagsDropdown.createTag', { interpolate: { filter } }) }}
</span>
@@ -257,7 +257,7 @@ onClickOutside(
/>
<N8nOption v-if="manageEnabled" :key="MANAGE_KEY" :value="MANAGE_KEY" class="ops manage-tags">
<font-awesome-icon icon="cog" />
<n8n-icon icon="cog" />
<span>{{ i18n.baseText('tagsDropdown.manageTags') }}</span>
</N8nOption>
</N8nSelect>

Some files were not shown because too many files have changed in this diff Show More