Files
n8n/packages/frontend/@n8n/chat/src/components/MessagesList.vue
T
alighasami 3d5eaf9445
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
first commit
2026-03-17 16:22:57 +03:30

112 lines
2.4 KiB
Vue

<script lang="ts" setup>
import { N8nIcon, N8nText } from '@n8n/design-system';
import { ref, watch } from 'vue';
import Message from '@n8n/chat/components/Message.vue';
import MessageTyping from '@n8n/chat/components/MessageTyping.vue';
import { useChat } from '@n8n/chat/composables';
import type { ChatMessage } from '@n8n/chat/types';
defineProps<{
messages: ChatMessage[];
emptyText?: string;
}>();
defineSlots<{
beforeMessage(props: { message: ChatMessage }): ChatMessage;
}>();
const chatStore = useChat();
const messageComponents = ref<Array<InstanceType<typeof Message>>>([]);
const { initialMessages, waitingForResponse } = chatStore;
watch(
() => messageComponents.value.length,
() => {
const lastMessageComponent = messageComponents.value[messageComponents.value.length - 1];
if (lastMessageComponent) {
lastMessageComponent.scrollToView();
}
},
);
</script>
<template>
<div
v-if="emptyText && initialMessages.length === 0 && messages.length === 0"
class="empty-container"
>
<div class="empty" data-test-id="chat-messages-empty">
<N8nIcon icon="message-circle" size="large" class="emptyIcon" />
<N8nText tag="p" size="medium" color="text-base">
{{ emptyText }}
</N8nText>
</div>
</div>
<div v-else class="chat-messages-list">
<Message
v-for="initialMessage in initialMessages"
:key="initialMessage.id"
:message="initialMessage"
/>
<template v-for="message in messages" :key="message.id">
<Message ref="messageComponents" :message="message">
<template #beforeMessage="{ message }">
<slot name="beforeMessage" v-bind="{ message }" />
</template>
</Message>
</template>
<MessageTyping v-if="waitingForResponse" />
</div>
</template>
<style lang="scss">
.chat-messages-list {
margin-top: auto;
display: block;
padding: var(--chat--messages-list--padding);
}
.empty-container {
container-type: size;
display: flex;
align-items: center;
justify-content: center;
p {
max-width: 16em;
margin: 0;
}
}
.empty {
text-align: center;
color: var(--color--text);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--spacing--xs);
padding-inline: var(--spacing--md);
padding-bottom: var(--spacing--lg);
overflow: hidden;
}
.emptyIcon {
zoom: 2.5;
color: var(--color-button-secondary-border);
}
@container (height < 150px) {
.empty {
flex-direction: row;
text-align: left;
}
.emptyIcon {
zoom: 1.5;
}
}
</style>