mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
fix(editor): Replace v-html with custom directive to sanitize html (#10804)
Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
This commit is contained in:
parent
989f69d1f4
commit
44e5fb9b06
|
@ -29,7 +29,6 @@
|
|||
"@types/sanitize-html": "^2.11.0",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vitest/coverage-v8": "catalog:frontend",
|
||||
"@vue/test-utils": "^2.4.3",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.38",
|
||||
"sass": "^1.64.1",
|
||||
|
|
|
@ -151,8 +151,7 @@ function growInput() {
|
|||
/>
|
||||
</div>
|
||||
<div :class="$style.blockBody">
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<span v-html="renderMarkdown(message.content)"></span>
|
||||
<span v-n8n-html="renderMarkdown(message.content)"></span>
|
||||
<BlinkingCursor
|
||||
v-if="streaming && i === messages?.length - 1 && message.title && message.content"
|
||||
/>
|
||||
|
@ -160,19 +159,20 @@ function growInput() {
|
|||
</div>
|
||||
</div>
|
||||
<div v-else-if="message.type === 'text'" :class="$style.textMessage">
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<span v-if="message.role === 'user'" v-html="renderMarkdown(message.content)"></span>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<span
|
||||
v-if="message.role === 'user'"
|
||||
v-n8n-html="renderMarkdown(message.content)"
|
||||
></span>
|
||||
<div
|
||||
v-else
|
||||
v-n8n-html="renderMarkdown(message.content)"
|
||||
:class="$style.assistantText"
|
||||
v-html="renderMarkdown(message.content)"
|
||||
></div>
|
||||
<div
|
||||
v-if="message?.codeSnippet"
|
||||
:class="$style['code-snippet']"
|
||||
data-test-id="assistant-code-snippet"
|
||||
v-html="renderMarkdown(message.codeSnippet).trim()"
|
||||
v-n8n-html="renderMarkdown(message.codeSnippet).trim()"
|
||||
></div>
|
||||
<BlinkingCursor
|
||||
v-if="streaming && i === messages?.length - 1 && message.role === 'assistant'"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { render } from '@testing-library/vue';
|
||||
import AskAssistantChat from '../AskAssistantChat.vue';
|
||||
import { n8nHtml } from 'n8n-design-system/directives';
|
||||
|
||||
describe('AskAssistantChat', () => {
|
||||
it('renders default placeholder chat correctly', () => {
|
||||
|
@ -12,6 +13,11 @@ describe('AskAssistantChat', () => {
|
|||
});
|
||||
it('renders chat with messages correctly', () => {
|
||||
const { container } = render(AskAssistantChat, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
user: { firstName: 'Kobi', lastName: 'Dog' },
|
||||
messages: [
|
||||
|
@ -86,6 +92,11 @@ describe('AskAssistantChat', () => {
|
|||
});
|
||||
it('renders streaming chat correctly', () => {
|
||||
const { container } = render(AskAssistantChat, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
user: { firstName: 'Kobi', lastName: 'Dog' },
|
||||
messages: [
|
||||
|
@ -105,6 +116,11 @@ describe('AskAssistantChat', () => {
|
|||
});
|
||||
it('renders end of session chat correctly', () => {
|
||||
const { container } = render(AskAssistantChat, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
user: { firstName: 'Kobi', lastName: 'Dog' },
|
||||
messages: [
|
||||
|
@ -130,6 +146,11 @@ describe('AskAssistantChat', () => {
|
|||
});
|
||||
it('renders message with code snippet', () => {
|
||||
const { container } = render(AskAssistantChat, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
user: { firstName: 'Kobi', lastName: 'Dog' },
|
||||
messages: [
|
||||
|
|
|
@ -129,9 +129,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
|
|||
<div
|
||||
class="textMessage"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div
|
||||
class="assistantText"
|
||||
>
|
||||
|
@ -145,7 +142,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
|
|||
|
||||
|
||||
</div>
|
||||
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
|
@ -438,7 +434,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
|
|||
<div
|
||||
class="textMessage"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<span>
|
||||
<p>
|
||||
Give it to me
|
||||
|
@ -516,7 +511,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
|
|||
<div
|
||||
class="blockBody"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<span>
|
||||
<p>
|
||||
Solution steps:
|
||||
|
@ -1060,9 +1054,6 @@ exports[`AskAssistantChat > renders end of session chat correctly 1`] = `
|
|||
<div
|
||||
class="textMessage"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div
|
||||
class="assistantText"
|
||||
>
|
||||
|
@ -1076,7 +1067,6 @@ exports[`AskAssistantChat > renders end of session chat correctly 1`] = `
|
|||
|
||||
|
||||
</div>
|
||||
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
|
@ -1309,9 +1299,6 @@ exports[`AskAssistantChat > renders message with code snippet 1`] = `
|
|||
<div
|
||||
class="textMessage"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div
|
||||
class="assistantText"
|
||||
>
|
||||
|
@ -1325,7 +1312,6 @@ exports[`AskAssistantChat > renders message with code snippet 1`] = `
|
|||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="code-snippet"
|
||||
data-test-id="assistant-code-snippet"
|
||||
|
@ -1552,9 +1538,6 @@ exports[`AskAssistantChat > renders streaming chat correctly 1`] = `
|
|||
<div
|
||||
class="textMessage"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div
|
||||
class="assistantText"
|
||||
>
|
||||
|
@ -1568,7 +1551,6 @@ exports[`AskAssistantChat > renders streaming chat correctly 1`] = `
|
|||
|
||||
|
||||
</div>
|
||||
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
|
|
|
@ -37,7 +37,7 @@ withDefaults(defineProps<ActionBoxProps>(), {
|
|||
<div :class="$style.description" @click="$emit('descriptionClick', $event)">
|
||||
<N8nText color="text-base">
|
||||
<slot name="description">
|
||||
<span v-html="description"></span>
|
||||
<span v-n8n-html="description"></span>
|
||||
</slot>
|
||||
</N8nText>
|
||||
</div>
|
||||
|
@ -61,7 +61,7 @@ withDefaults(defineProps<ActionBoxProps>(), {
|
|||
:class="$style.callout"
|
||||
>
|
||||
<N8nText color="text-base">
|
||||
<span size="small" v-html="calloutText"></span>
|
||||
<span size="small" v-n8n-html="calloutText"></span>
|
||||
</N8nText>
|
||||
</N8nCallout>
|
||||
</div>
|
||||
|
|
|
@ -75,7 +75,7 @@ const onTooltipClick = (item: string, event: MouseEvent) => emit('tooltipClick',
|
|||
<div v-for="item in items" :key="item.id" :class="$style.accordionItem">
|
||||
<n8n-tooltip :disabled="!item.tooltip">
|
||||
<template #content>
|
||||
<div @click="onTooltipClick(item.id, $event)" v-html="item.tooltip"></div>
|
||||
<div @click="onTooltipClick(item.id, $event)" v-n8n-html="item.tooltip"></div>
|
||||
</template>
|
||||
<N8nIcon :icon="item.icon" :color="item.iconColor" size="small" class="mr-2xs" />
|
||||
</n8n-tooltip>
|
||||
|
@ -83,7 +83,7 @@ const onTooltipClick = (item: string, event: MouseEvent) => emit('tooltipClick',
|
|||
</div>
|
||||
</div>
|
||||
<N8nText color="text-base" size="small" align="left">
|
||||
<span v-html="description"></span>
|
||||
<span v-n8n-html="description"></span>
|
||||
</N8nText>
|
||||
<slot name="customContent"></slot>
|
||||
</div>
|
||||
|
|
|
@ -58,7 +58,7 @@ const addTargetBlank = (html: string) =>
|
|||
<N8nTooltip placement="top" :popper-class="$style.tooltipPopper" :show-after="300">
|
||||
<N8nIcon icon="question-circle" size="small" />
|
||||
<template #content>
|
||||
<div v-html="addTargetBlank(tooltipText)" />
|
||||
<div v-n8n-html="addTargetBlank(tooltipText)" />
|
||||
</template>
|
||||
</N8nTooltip>
|
||||
</span>
|
||||
|
|
|
@ -202,7 +202,7 @@ const onCheckboxChange = (index: number) => {
|
|||
@click="onClick"
|
||||
@mousedown="onMouseDown"
|
||||
@change="onChange"
|
||||
v-html="htmlContent"
|
||||
v-n8n-html="htmlContent"
|
||||
/>
|
||||
<div v-else :class="$style.markdown">
|
||||
<div v-for="(_, index) in loadingBlocks" :key="index">
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import { render, fireEvent } from '@testing-library/vue';
|
||||
import N8nMarkdown from '../Markdown.vue';
|
||||
import { n8nHtml } from 'n8n-design-system/directives';
|
||||
|
||||
describe('components', () => {
|
||||
describe('N8nMarkdown', () => {
|
||||
it('should render unchecked checkboxes', () => {
|
||||
const wrapper = render(N8nMarkdown, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
content: '__TODO__\n- [ ] Buy milk\n- [ ] Buy socks\n',
|
||||
},
|
||||
|
@ -18,6 +24,11 @@ describe('components', () => {
|
|||
|
||||
it('should render checked checkboxes', () => {
|
||||
const wrapper = render(N8nMarkdown, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
content: '__TODO__\n- [X] Buy milk\n- [X] Buy socks\n',
|
||||
},
|
||||
|
@ -31,6 +42,11 @@ describe('components', () => {
|
|||
|
||||
it('should toggle checkboxes when clicked', async () => {
|
||||
const wrapper = render(N8nMarkdown, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
content: '__TODO__\n- [ ] Buy milk\n- [ ] Buy socks\n',
|
||||
},
|
||||
|
@ -50,6 +66,11 @@ describe('components', () => {
|
|||
|
||||
it('should render inputs as plain text', () => {
|
||||
const wrapper = render(N8nMarkdown, {
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
content:
|
||||
'__TODO__\n- [X] Buy milk\n- <input type="text" data-testid="text-input" value="Something"/>\n',
|
||||
|
|
|
@ -73,7 +73,7 @@ const onClick = (event: MouseEvent) => {
|
|||
:id="`${id}-content`"
|
||||
:class="showFullContent ? $style['expanded'] : $style['truncated']"
|
||||
role="region"
|
||||
v-html="displayContent"
|
||||
v-n8n-html="displayContent"
|
||||
/>
|
||||
</slot>
|
||||
</N8nText>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { render } from '@testing-library/vue';
|
||||
import N8nNotice from '../Notice.vue';
|
||||
import { N8nText } from 'n8n-design-system/components';
|
||||
import { n8nHtml } from 'n8n-design-system/directives';
|
||||
|
||||
describe('components', () => {
|
||||
describe('N8nNotice', () => {
|
||||
|
@ -41,6 +42,9 @@ describe('components', () => {
|
|||
content: '<strong>Hello world!</strong> This is a notice.',
|
||||
},
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
components: {
|
||||
'n8n-text': N8nText,
|
||||
},
|
||||
|
|
|
@ -116,7 +116,7 @@ const onInputScroll = (event: WheelEvent) => {
|
|||
</div>
|
||||
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
|
||||
<N8nText size="xsmall" align="right">
|
||||
<span v-html="t('sticky.markdownHint')"></span>
|
||||
<span v-n8n-html="t('sticky.markdownHint')"></span>
|
||||
</N8nText>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -89,7 +89,7 @@ const scrollRight = () => scroll(50);
|
|||
>
|
||||
<N8nTooltip :disabled="!option.tooltip" placement="bottom">
|
||||
<template #content>
|
||||
<div @click="handleTooltipClick(option.value, $event)" v-html="option.tooltip" />
|
||||
<div @click="handleTooltipClick(option.value, $event)" v-n8n-html="option.tooltip" />
|
||||
</template>
|
||||
<a
|
||||
v-if="option.href"
|
||||
|
|
|
@ -42,7 +42,7 @@ defineOptions({
|
|||
<slot />
|
||||
<template #content>
|
||||
<slot name="content">
|
||||
<div v-html="props.content"></div>
|
||||
<div v-n8n-html="props.content"></div>
|
||||
</slot>
|
||||
<div
|
||||
v-if="props.buttons.length"
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
export { n8nTruncate } from './n8n-truncate';
|
||||
export { n8nHtml } from './n8n-html';
|
||||
|
|
45
packages/design-system/src/directives/n8n-html.test.ts
Normal file
45
packages/design-system/src/directives/n8n-html.test.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { render } from '@testing-library/vue';
|
||||
import { n8nHtml } from './n8n-html';
|
||||
|
||||
const TestComponent = {
|
||||
props: {
|
||||
html: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
template: '<div v-n8n-html="html"></div>',
|
||||
};
|
||||
|
||||
describe('Directive n8n-html', () => {
|
||||
it('should sanitize html', async () => {
|
||||
const { html } = render(TestComponent, {
|
||||
props: {
|
||||
html: '<span>text</span><a href="https://malicious.com" onclick="alert(1)">malicious</a><img alt="Ok" src="./images/logo.svg" onerror="alert(2)" /><script>alert(3)</script>',
|
||||
},
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(html()).toBe(
|
||||
'<div><span>text</span><a href="https://malicious.com">malicious</a><img alt="Ok" src="./images/logo.svg"></div>',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not touch safe html', async () => {
|
||||
const { html } = render(TestComponent, {
|
||||
props: {
|
||||
html: '<span>text</span><a href="https://safe.com">safe</a><img alt="Ok" src="./images/logo.svg" />',
|
||||
},
|
||||
global: {
|
||||
directives: {
|
||||
n8nHtml,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(html()).toBe(
|
||||
'<div><span>text</span><a href="https://safe.com">safe</a><img alt="Ok" src="./images/logo.svg"></div>',
|
||||
);
|
||||
});
|
||||
});
|
37
packages/design-system/src/directives/n8n-html.ts
Normal file
37
packages/design-system/src/directives/n8n-html.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import sanitize from 'sanitize-html';
|
||||
import type { DirectiveBinding, ObjectDirective } from 'vue';
|
||||
|
||||
/**
|
||||
* Custom directive `n8nHtml` to replace v-html from Vue to sanitize content.
|
||||
*
|
||||
* Usage:
|
||||
* In your Vue template, use the directive `v-n8n-html` passing the unsafe HTML.
|
||||
*
|
||||
* Example:
|
||||
* <p v-n8n-html="'<a href="https://site.com" onclick="alert(1)">link</a>'">
|
||||
*
|
||||
* Compiles to: <p><a href="https://site.com">link</a></p>
|
||||
*
|
||||
* Hint: Do not use it on components
|
||||
* https://vuejs.org/guide/reusability/custom-directives#usage-on-components
|
||||
*/
|
||||
|
||||
const configuredSanitize = (html: string) =>
|
||||
sanitize(html, {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat(['img', 'input']),
|
||||
allowedAttributes: {
|
||||
...sanitize.defaults.allowedAttributes,
|
||||
input: ['type', 'id', 'checked'],
|
||||
code: ['class'],
|
||||
a: sanitize.defaults.allowedAttributes.a.concat(['data-*']),
|
||||
},
|
||||
});
|
||||
|
||||
export const n8nHtml: ObjectDirective = {
|
||||
beforeMount(el: HTMLElement, binding: DirectiveBinding<string>) {
|
||||
el.innerHTML = configuredSanitize(binding.value);
|
||||
},
|
||||
beforeUpdate(el: HTMLElement, binding: DirectiveBinding<string>) {
|
||||
el.innerHTML = configuredSanitize(binding.value);
|
||||
},
|
||||
};
|
|
@ -445,7 +445,7 @@ async function onAskAssistantClick() {
|
|||
v-if="error.description || error.context?.descriptionKey"
|
||||
data-test-id="node-error-description"
|
||||
class="node-error-view__header-description"
|
||||
v-html="getErrorDescription()"
|
||||
v-n8n-html="getErrorDescription()"
|
||||
></div>
|
||||
<div
|
||||
v-if="isAskAssistantAvailable"
|
||||
|
|
|
@ -166,7 +166,7 @@ async function onDrop(expression: string, event: MouseEvent) {
|
|||
<N8nText
|
||||
:class="$style.tip"
|
||||
size="small"
|
||||
v-html="i18n.baseText('expressionTip.javascript')"
|
||||
v-n8n-html="i18n.baseText('expressionTip.javascript')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ export default defineComponent({
|
|||
</div>
|
||||
<div v-if="featureInfo.infoText" class="mb-l">
|
||||
<n8n-info-tip theme="info" type="note">
|
||||
<span v-html="$locale.baseText(featureInfo.infoText)"></span>
|
||||
<span v-n8n-html="$locale.baseText(featureInfo.infoText)"></span>
|
||||
</n8n-info-tip>
|
||||
</div>
|
||||
<div :class="$style.actionBoxContainer">
|
||||
|
@ -68,7 +68,7 @@ export default defineComponent({
|
|||
@click:button="openLinkPage"
|
||||
>
|
||||
<template #heading>
|
||||
<span v-html="$locale.baseText(featureInfo.actionBoxTitle)" />
|
||||
<span v-n8n-html="$locale.baseText(featureInfo.actionBoxTitle)" />
|
||||
</template>
|
||||
</n8n-action-box>
|
||||
</div>
|
||||
|
|
|
@ -115,15 +115,15 @@ watchDebounced(
|
|||
</div>
|
||||
|
||||
<div v-else-if="tip === 'dotPrimitive'" :class="$style.content">
|
||||
<span v-html="i18n.baseText('expressionTip.typeDotPrimitive')" />
|
||||
<span v-n8n-html="i18n.baseText('expressionTip.typeDotPrimitive')" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="tip === 'dotObject'" :class="$style.content">
|
||||
<span v-html="i18n.baseText('expressionTip.typeDotObject')" />
|
||||
<span v-n8n-html="i18n.baseText('expressionTip.typeDotObject')" />
|
||||
</div>
|
||||
|
||||
<div v-else :class="$style.content">
|
||||
<span v-html="i18n.baseText('expressionTip.javascript')" />
|
||||
<span v-n8n-html="i18n.baseText('expressionTip.javascript')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -406,7 +406,7 @@ export default defineComponent({
|
|||
<n8n-tooltip v-if="!readOnly" :visible="showDraggableHint && showDraggableHintWithDelay">
|
||||
<template #content>
|
||||
<div
|
||||
v-html="
|
||||
v-n8n-html="
|
||||
$locale.baseText('dataMapping.dragFromPreviousHint', {
|
||||
interpolate: { name: focusedMappableInput },
|
||||
})
|
||||
|
|
|
@ -12,7 +12,7 @@ withDefaults(defineProps<Props>(), {
|
|||
<template>
|
||||
<div
|
||||
:class="[$style.dragPill, canDrop ? $style.droppablePill : $style.defaultPill]"
|
||||
v-html="html"
|
||||
v-n8n-html="html"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -633,7 +633,7 @@ function openContextMenu(event: MouseEvent, source: 'node-button' | 'node-right-
|
|||
<i v-if="isTriggerNode" class="trigger-icon">
|
||||
<n8n-tooltip placement="bottom">
|
||||
<template #content>
|
||||
<span v-html="i18n.baseText('node.thisIsATriggerNode')" />
|
||||
<span v-n8n-html="i18n.baseText('node.thisIsATriggerNode')" />
|
||||
</template>
|
||||
<FontAwesomeIcon icon="bolt" size="lg" />
|
||||
</n8n-tooltip>
|
||||
|
|
|
@ -163,7 +163,7 @@ function onCommunityNodeTooltipClick(event: MouseEvent) {
|
|||
<p
|
||||
:class="$style.communityNodeIcon"
|
||||
@click="onCommunityNodeTooltipClick"
|
||||
v-html="
|
||||
v-n8n-html="
|
||||
i18n.baseText('generic.communityNode.tooltip', {
|
||||
interpolate: {
|
||||
packageName: nodeType.name.split('.')[0],
|
||||
|
|
|
@ -258,7 +258,7 @@ onMounted(() => {
|
|||
data-test-id="actions-panel-no-triggers-callout"
|
||||
>
|
||||
<span
|
||||
v-html="
|
||||
v-n8n-html="
|
||||
i18n.baseText('nodeCreator.actionsCallout.noTriggerItems', {
|
||||
interpolate: { nodeName: subcategory ?? '' },
|
||||
})
|
||||
|
@ -271,7 +271,7 @@ onMounted(() => {
|
|||
<p
|
||||
:class="$style.resetSearch"
|
||||
@click="resetSearch"
|
||||
v-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingTriggers')"
|
||||
v-n8n-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingTriggers')"
|
||||
/>
|
||||
</template>
|
||||
</CategorizedItemsRenderer>
|
||||
|
@ -293,13 +293,13 @@ onMounted(() => {
|
|||
slim
|
||||
data-test-id="actions-panel-activation-callout"
|
||||
>
|
||||
<span v-html="i18n.baseText('nodeCreator.actionsCallout.triggersStartWorkflow')" />
|
||||
<span v-n8n-html="i18n.baseText('nodeCreator.actionsCallout.triggersStartWorkflow')" />
|
||||
</n8n-callout>
|
||||
<!-- Empty state -->
|
||||
<template #empty>
|
||||
<n8n-info-tip v-if="!search" theme="info" type="note" :class="$style.actionsEmpty">
|
||||
<span
|
||||
v-html="
|
||||
v-n8n-html="
|
||||
i18n.baseText('nodeCreator.actionsCallout.noActionItems', {
|
||||
interpolate: { nodeName: subcategory ?? '' },
|
||||
})
|
||||
|
@ -311,7 +311,7 @@ onMounted(() => {
|
|||
:class="$style.resetSearch"
|
||||
data-test-id="actions-panel-no-matching-actions"
|
||||
@click="resetSearch"
|
||||
v-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingActions')"
|
||||
v-n8n-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingActions')"
|
||||
/>
|
||||
</template>
|
||||
</CategorizedItemsRenderer>
|
||||
|
@ -320,7 +320,7 @@ onMounted(() => {
|
|||
<div v-if="containsAPIAction" :class="$style.apiHint">
|
||||
<span
|
||||
@click.prevent="addHttpNode"
|
||||
v-html="
|
||||
v-n8n-html="
|
||||
i18n.baseText('nodeCreator.actionsList.apiCall', {
|
||||
interpolate: { node: subcategory ?? '' },
|
||||
})
|
||||
|
|
|
@ -106,7 +106,7 @@ registerKeyHook(`CategoryLeft_${props.category}`, {
|
|||
<n8n-tooltip placement="top" :popper-class="$style.tooltipPopper">
|
||||
<n8n-icon icon="question-circle" size="small" />
|
||||
<template #content>
|
||||
<div v-html="mouseOverTooltip" />
|
||||
<div v-n8n-html="mouseOverTooltip" />
|
||||
</template>
|
||||
</n8n-tooltip>
|
||||
</span>
|
||||
|
|
|
@ -1391,7 +1391,7 @@ onUpdated(async () => {
|
|||
<div
|
||||
v-if="option.description"
|
||||
class="option-description"
|
||||
v-html="getOptionsOptionDescription(option)"
|
||||
v-n8n-html="getOptionsOptionDescription(option)"
|
||||
></div>
|
||||
</div>
|
||||
</n8n-option>
|
||||
|
@ -1424,7 +1424,7 @@ onUpdated(async () => {
|
|||
<div
|
||||
v-if="option.description"
|
||||
class="option-description"
|
||||
v-html="getOptionsOptionDescription(option)"
|
||||
v-n8n-html="getOptionsOptionDescription(option)"
|
||||
></div>
|
||||
</div>
|
||||
</n8n-option>
|
||||
|
|
|
@ -46,13 +46,13 @@ const simplyText = computed(() => {
|
|||
[$style.highlight]: highlight,
|
||||
}"
|
||||
>
|
||||
<span data-test-id="parameter-input-hint" v-html="simplyText"></span>
|
||||
<span data-test-id="parameter-input-hint" v-n8n-html="simplyText"></span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
ref="hintTextRef"
|
||||
:class="{ [$style.singleline]: singleLine, [$style.highlight]: highlight }"
|
||||
v-html="sanitizeHtml(hint)"
|
||||
v-n8n-html="sanitizeHtml(hint)"
|
||||
></div>
|
||||
</n8n-text>
|
||||
</template>
|
||||
|
|
|
@ -16,7 +16,7 @@ export default defineComponent({
|
|||
<div v-if="!rootStore.pushConnectionActive" class="push-connection-lost primary-color">
|
||||
<n8n-tooltip placement="bottom-end">
|
||||
<template #content>
|
||||
<div v-html="$locale.baseText('pushConnectionTracker.cannotConnectToServer')"></div>
|
||||
<div v-n8n-html="$locale.baseText('pushConnectionTracker.cannotConnectToServer')"></div>
|
||||
</template>
|
||||
<span>
|
||||
<font-awesome-icon icon="exclamation-triangle" />
|
||||
|
|
|
@ -131,7 +131,7 @@ defineExpose({
|
|||
<div class="option-headline">
|
||||
{{ option.name }}
|
||||
</div>
|
||||
<div class="option-description" v-html="option.description" />
|
||||
<div class="option-description" v-n8n-html="option.description" />
|
||||
</div>
|
||||
</N8nOption>
|
||||
</N8nSelect>
|
||||
|
|
|
@ -1350,7 +1350,7 @@ export default defineComponent({
|
|||
:class="$style.hintCallout"
|
||||
:theme="hint.type || 'info'"
|
||||
>
|
||||
<n8n-text size="small" v-html="hint.message"></n8n-text>
|
||||
<n8n-text size="small" v-n8n-html="hint.message"></n8n-text>
|
||||
</n8n-callout>
|
||||
|
||||
<div
|
||||
|
@ -1509,7 +1509,7 @@ export default defineComponent({
|
|||
<n8n-text :bold="true" color="text-dark" size="large">{{ tooMuchDataTitle }}</n8n-text>
|
||||
<n8n-text align="center" tag="div"
|
||||
><span
|
||||
v-html="
|
||||
v-n8n-html="
|
||||
$locale.baseText('ndv.output.tooMuchData.message', {
|
||||
interpolate: { size: dataSizeInMB },
|
||||
})
|
||||
|
|
|
@ -41,7 +41,7 @@ const runMetadata = computed(() => {
|
|||
data-test-id="node-run-info-stale"
|
||||
>
|
||||
<span
|
||||
v-html="
|
||||
v-n8n-html="
|
||||
i18n.baseText(
|
||||
hasPinData
|
||||
? 'ndv.output.staleDataWarning.pinData'
|
||||
|
|
|
@ -9,7 +9,7 @@ defineProps<{
|
|||
<div class="titled-list">
|
||||
<p v-text="title" />
|
||||
<ul>
|
||||
<li v-for="item in items" :key="item" class="titled-list-item" v-html="item" />
|
||||
<li v-for="item in items" :key="item" class="titled-list-item" v-n8n-html="item" />
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -441,7 +441,7 @@ export default defineComponent({
|
|||
</div>
|
||||
|
||||
<n8n-text v-if="activationHint" size="small" @click="onLinkClick">
|
||||
<span v-html="activationHint"></span>
|
||||
<span v-n8n-html="activationHint"></span>
|
||||
</n8n-text>
|
||||
<n8n-link
|
||||
v-if="activationHint && executionsHelp"
|
||||
|
|
|
@ -31,7 +31,7 @@ const nodeName = (node: IVersionNode): string => {
|
|||
{{ `${$locale.baseText('versionCard.version')} ${version.name}` }}
|
||||
</div>
|
||||
<WarningTooltip v-if="version.hasSecurityIssue">
|
||||
<span v-html="$locale.baseText('versionCard.thisVersionHasASecurityIssue')"></span>
|
||||
<span v-n8n-html="$locale.baseText('versionCard.thisVersionHasASecurityIssue')"></span>
|
||||
</WarningTooltip>
|
||||
<Badge
|
||||
v-if="version.hasSecurityFix"
|
||||
|
@ -56,7 +56,7 @@ const nodeName = (node: IVersionNode): string => {
|
|||
<div
|
||||
v-if="version.description"
|
||||
:class="$style.description"
|
||||
v-html="version.description"
|
||||
v-n8n-html="version.description"
|
||||
></div>
|
||||
<div v-if="version.nodes && version.nodes.length > 0" :class="$style.nodes">
|
||||
<NodeIcon
|
||||
|
|
|
@ -138,7 +138,7 @@ async function displayActivationError() {
|
|||
<template #content>
|
||||
<div
|
||||
@click="displayActivationError"
|
||||
v-html="i18n.baseText('workflowActivator.theWorkflowIsSetToBeActiveBut')"
|
||||
v-n8n-html="i18n.baseText('workflowActivator.theWorkflowIsSetToBeActiveBut')"
|
||||
></div>
|
||||
</template>
|
||||
<font-awesome-icon icon="exclamation-triangle" @click="displayActivationError" />
|
||||
|
|
|
@ -573,7 +573,7 @@ export default defineComponent({
|
|||
{{ $locale.baseText('workflowSettings.errorWorkflow') + ':' }}
|
||||
<n8n-tooltip placement="top">
|
||||
<template #content>
|
||||
<div v-html="helpTexts.errorWorkflow"></div>
|
||||
<div v-n8n-html="helpTexts.errorWorkflow"></div>
|
||||
</template>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
|
|
|
@ -17,14 +17,14 @@ const hasOwnerPermission = computed(() => hasPermission(['instanceOwner']));
|
|||
<template>
|
||||
<BaseBanner custom-icon="info-circle" theme="warning" name="V1" :class="$style.v1container">
|
||||
<template #mainContent>
|
||||
<span v-html="locale.baseText('banners.v1.message')"></span>
|
||||
<span v-n8n-html="locale.baseText('banners.v1.message')"></span>
|
||||
<a
|
||||
v-if="hasOwnerPermission"
|
||||
:class="$style.link"
|
||||
data-test-id="banner-confirm-v1"
|
||||
@click="dismissPermanently"
|
||||
>
|
||||
<span v-html="locale.baseText('generic.dontShowAgain')"></span>
|
||||
<span v-n8n-html="locale.baseText('generic.dontShowAgain')"></span>
|
||||
</a>
|
||||
</template>
|
||||
</BaseBanner>
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { render } from '@testing-library/vue';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import V1Banner from '../V1Banner.vue';
|
||||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { ROLE } from '@/constants';
|
||||
import type { IUser } from '@/Interface';
|
||||
|
||||
const renderComponent = createComponentRenderer(V1Banner);
|
||||
|
||||
describe('V1 Banner', () => {
|
||||
let pinia: ReturnType<typeof createPinia>;
|
||||
let usersStore: ReturnType<typeof useUsersStore>;
|
||||
|
@ -17,7 +19,7 @@ describe('V1 Banner', () => {
|
|||
});
|
||||
|
||||
it('should render banner', () => {
|
||||
const { container } = render(V1Banner);
|
||||
const { container } = renderComponent();
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(container.querySelectorAll('a')).toHaveLength(1);
|
||||
});
|
||||
|
@ -26,7 +28,7 @@ describe('V1 Banner', () => {
|
|||
usersStore.usersById = { '1': { role: ROLE.Owner } as IUser };
|
||||
usersStore.currentUserId = '1';
|
||||
|
||||
const { container } = render(V1Banner);
|
||||
const { container } = renderComponent();
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(container.querySelectorAll('a')).toHaveLength(2);
|
||||
});
|
||||
|
|
|
@ -2,69 +2,209 @@
|
|||
|
||||
exports[`V1 Banner > should render banner 1`] = `
|
||||
<div>
|
||||
<n8n-callout
|
||||
class="callout v1container"
|
||||
<div
|
||||
class="n8n-callout callout warning callout v1container"
|
||||
data-test-id="banners-V1"
|
||||
icon="info-circle"
|
||||
icon-size="medium"
|
||||
round-corners="false"
|
||||
theme="warning"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="mainContent keepSpace"
|
||||
class="messageSection"
|
||||
>
|
||||
|
||||
<span>
|
||||
n8n has been updated to version 1, introducing some breaking changes. Please consult the
|
||||
<a
|
||||
href="https://docs.n8n.io/1-0-migration-checklist"
|
||||
target="_blank"
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<span
|
||||
class="n8n-text compact size-medium regular n8n-icon n8n-icon"
|
||||
>
|
||||
migration guide
|
||||
</a>
|
||||
for more information.
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-info-circle fa-w-16 medium"
|
||||
data-icon="info-circle"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
class=""
|
||||
d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="n8n-text size-small regular"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="mainContent keepSpace"
|
||||
>
|
||||
|
||||
<span>
|
||||
n8n has been updated to version 1, introducing some breaking changes. Please consult the
|
||||
<a
|
||||
href="https://docs.n8n.io/1-0-migration-checklist"
|
||||
target="_blank"
|
||||
>
|
||||
migration guide
|
||||
</a>
|
||||
for more information.
|
||||
</span>
|
||||
<!--v-if-->
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</span>
|
||||
<!--v-if-->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</n8n-callout>
|
||||
|
||||
<div
|
||||
class="trailingContent"
|
||||
>
|
||||
|
||||
|
||||
<span
|
||||
class="n8n-text compact size-small regular n8n-icon clickable clickable n8n-icon clickable clickable"
|
||||
data-test-id="banner-V1-close"
|
||||
title="Dismiss"
|
||||
>
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-times fa-w-11 small"
|
||||
data-icon="times"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 352 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
class=""
|
||||
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`V1 Banner > should render banner with dismiss call if user is owner 1`] = `
|
||||
<div>
|
||||
<n8n-callout
|
||||
class="callout v1container"
|
||||
<div
|
||||
class="n8n-callout callout warning callout v1container"
|
||||
data-test-id="banners-V1"
|
||||
icon="info-circle"
|
||||
icon-size="medium"
|
||||
round-corners="false"
|
||||
theme="warning"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="mainContent keepSpace"
|
||||
class="messageSection"
|
||||
>
|
||||
|
||||
<span>
|
||||
n8n has been updated to version 1, introducing some breaking changes. Please consult the
|
||||
<a
|
||||
href="https://docs.n8n.io/1-0-migration-checklist"
|
||||
target="_blank"
|
||||
>
|
||||
migration guide
|
||||
</a>
|
||||
for more information.
|
||||
</span>
|
||||
<a
|
||||
class="link"
|
||||
data-test-id="banner-confirm-v1"
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<span>
|
||||
Don't show again
|
||||
<span
|
||||
class="n8n-text compact size-medium regular n8n-icon n8n-icon"
|
||||
>
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-info-circle fa-w-16 medium"
|
||||
data-icon="info-circle"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
class=""
|
||||
d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<span
|
||||
class="n8n-text size-small regular"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="mainContent keepSpace"
|
||||
>
|
||||
|
||||
<span>
|
||||
n8n has been updated to version 1, introducing some breaking changes. Please consult the
|
||||
<a
|
||||
href="https://docs.n8n.io/1-0-migration-checklist"
|
||||
target="_blank"
|
||||
>
|
||||
migration guide
|
||||
</a>
|
||||
for more information.
|
||||
</span>
|
||||
<a
|
||||
class="link"
|
||||
data-test-id="banner-confirm-v1"
|
||||
>
|
||||
<span>
|
||||
Don't show again
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</n8n-callout>
|
||||
|
||||
<div
|
||||
class="trailingContent"
|
||||
>
|
||||
|
||||
|
||||
<span
|
||||
class="n8n-text compact size-small regular n8n-icon clickable clickable n8n-icon clickable clickable"
|
||||
data-test-id="banner-V1-close"
|
||||
title="Dismiss"
|
||||
>
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-times fa-w-11 small"
|
||||
data-icon="times"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 352 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
class=""
|
||||
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -96,7 +96,7 @@ function openContextMenu(event: MouseEvent) {
|
|||
<slot />
|
||||
<N8nTooltip v-if="renderOptions.trigger" placement="bottom">
|
||||
<template #content>
|
||||
<span v-html="$locale.baseText('node.thisIsATriggerNode')" />
|
||||
<span v-n8n-html="$locale.baseText('node.thisIsATriggerNode')" />
|
||||
</template>
|
||||
<div :class="$style.triggerIcon">
|
||||
<FontAwesomeIcon icon="bolt" size="lg" />
|
||||
|
|
|
@ -179,7 +179,7 @@ const onTagsEditEsc = () => {
|
|||
</div>
|
||||
<div v-else :class="$style.noResultsContainer" data-test-id="execution-annotation-data-empty">
|
||||
<n8n-text color="text-base" size="small" align="center">
|
||||
<span v-html="$locale.baseText('executionAnnotationView.data.notFound')" />
|
||||
<span v-n8n-html="$locale.baseText('executionAnnotationView.data.notFound')" />
|
||||
</n8n-text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -633,7 +633,7 @@ export default defineComponent({
|
|||
</div>
|
||||
<div :class="$style.docsInfoTip">
|
||||
<n8n-info-tip theme="info" type="note">
|
||||
<span v-html="$locale.baseText('settings.ldap.infoTip')"></span>
|
||||
<span v-n8n-html="$locale.baseText('settings.ldap.infoTip')"></span>
|
||||
</n8n-info-tip>
|
||||
</div>
|
||||
<div :class="$style.settingsForm">
|
||||
|
|
|
@ -175,7 +175,7 @@ export default defineComponent({
|
|||
<template v-if="isLicensed">
|
||||
<div class="mb-l">
|
||||
<n8n-info-tip theme="info" type="note">
|
||||
<span v-html="$locale.baseText('settings.log-streaming.infoText')"></span>
|
||||
<span v-n8n-html="$locale.baseText('settings.log-streaming.infoText')"></span>
|
||||
</n8n-info-tip>
|
||||
</div>
|
||||
<template v-if="storeHasItems()">
|
||||
|
@ -207,7 +207,7 @@ export default defineComponent({
|
|||
@click:button="addDestination"
|
||||
>
|
||||
<template #heading>
|
||||
<span v-html="$locale.baseText(`settings.log-streaming.addFirstTitle`)" />
|
||||
<span v-n8n-html="$locale.baseText(`settings.log-streaming.addFirstTitle`)" />
|
||||
</template>
|
||||
</n8n-action-box>
|
||||
</div>
|
||||
|
@ -215,7 +215,7 @@ export default defineComponent({
|
|||
<template v-else>
|
||||
<div v-if="$locale.baseText('settings.log-streaming.infoText')" class="mb-l">
|
||||
<n8n-info-tip theme="info" type="note">
|
||||
<span v-html="$locale.baseText('settings.log-streaming.infoText')"></span>
|
||||
<span v-n8n-html="$locale.baseText('settings.log-streaming.infoText')"></span>
|
||||
</n8n-info-tip>
|
||||
</div>
|
||||
<div data-test-id="action-box-unlicensed">
|
||||
|
@ -225,7 +225,7 @@ export default defineComponent({
|
|||
@click:button="goToUpgrade"
|
||||
>
|
||||
<template #heading>
|
||||
<span v-html="$locale.baseText('settings.log-streaming.actionBox.title')" />
|
||||
<span v-n8n-html="$locale.baseText('settings.log-streaming.actionBox.title')" />
|
||||
</template>
|
||||
</n8n-action-box>
|
||||
</div>
|
||||
|
|
|
@ -28,7 +28,7 @@ const appNodeCounts = computed(() => {
|
|||
<template>
|
||||
<N8nNotice :class="$style.notice" theme="info">
|
||||
<i18n-t tag="span" keypath="templateSetup.instructions" scope="global">
|
||||
<span v-html="appNodeCounts" />
|
||||
<span v-n8n-html="appNodeCounts" />
|
||||
</i18n-t>
|
||||
</N8nNotice>
|
||||
</template>
|
||||
|
|
|
@ -95,7 +95,7 @@ const onCredentialModalOpened = () => {
|
|||
:plural="credentials.usedBy.length"
|
||||
scope="global"
|
||||
>
|
||||
<span v-html="nodeNames" />
|
||||
<span v-n8n-html="nodeNames" />
|
||||
</i18n-t>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -410,7 +410,7 @@ export default defineComponent({
|
|||
/>
|
||||
<div v-if="endOfSearchMessage" :class="$style.endText">
|
||||
<n8n-text size="medium" color="text-base">
|
||||
<span v-html="endOfSearchMessage" />
|
||||
<span v-n8n-html="endOfSearchMessage" />
|
||||
</n8n-text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1205,9 +1205,6 @@ importers:
|
|||
'@vitest/coverage-v8':
|
||||
specifier: catalog:frontend
|
||||
version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
|
||||
'@vue/test-utils':
|
||||
specifier: ^2.4.3
|
||||
version: 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
|
||||
autoprefixer:
|
||||
specifier: ^10.4.19
|
||||
version: 10.4.19(postcss@8.4.38)
|
||||
|
|
Loading…
Reference in a new issue