2024-08-14 05:59:11 -07:00
|
|
|
|
<script setup lang="ts">
|
2024-09-18 00:19:33 -07:00
|
|
|
|
import Markdown from 'markdown-it';
|
|
|
|
|
import markdownLink from 'markdown-it-link-attributes';
|
2024-08-14 05:59:11 -07:00
|
|
|
|
import { computed, ref } from 'vue';
|
2024-09-18 00:19:33 -07:00
|
|
|
|
|
|
|
|
|
import { useI18n } from '../../composables/useI18n';
|
|
|
|
|
import type { ChatUI } from '../../types/assistant';
|
2024-08-14 05:59:11 -07:00
|
|
|
|
import AssistantAvatar from '../AskAssistantAvatar/AssistantAvatar.vue';
|
2024-09-18 00:19:33 -07:00
|
|
|
|
import AssistantIcon from '../AskAssistantIcon/AssistantIcon.vue';
|
2024-09-03 01:43:24 -07:00
|
|
|
|
import AssistantLoadingMessage from '../AskAssistantLoadingMessage/AssistantLoadingMessage.vue';
|
2024-09-18 00:19:33 -07:00
|
|
|
|
import AssistantText from '../AskAssistantText/AssistantText.vue';
|
|
|
|
|
import BetaTag from '../BetaTag/BetaTag.vue';
|
2024-08-14 05:59:11 -07:00
|
|
|
|
import BlinkingCursor from '../BlinkingCursor/BlinkingCursor.vue';
|
2024-09-18 00:19:33 -07:00
|
|
|
|
import CodeDiff from '../CodeDiff/CodeDiff.vue';
|
2024-08-14 05:59:11 -07:00
|
|
|
|
import InlineAskAssistantButton from '../InlineAskAssistantButton/InlineAskAssistantButton.vue';
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
|
|
|
|
const md = new Markdown({
|
|
|
|
|
breaks: true,
|
2024-09-23 04:05:15 -07:00
|
|
|
|
});
|
|
|
|
|
md.use(markdownLink, {
|
2024-08-14 05:59:11 -07:00
|
|
|
|
attrs: {
|
|
|
|
|
target: '_blank',
|
|
|
|
|
rel: 'noopener',
|
|
|
|
|
},
|
|
|
|
|
});
|
2024-09-23 04:05:15 -07:00
|
|
|
|
// Wrap tables in div
|
|
|
|
|
md.renderer.rules.table_open = function () {
|
|
|
|
|
return '<div class="table-wrapper"><table>';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
md.renderer.rules.table_close = function () {
|
|
|
|
|
return '</table></div>';
|
|
|
|
|
};
|
2024-08-14 05:59:11 -07:00
|
|
|
|
|
|
|
|
|
const MAX_CHAT_INPUT_HEIGHT = 100;
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
user?: {
|
|
|
|
|
firstName: string;
|
|
|
|
|
lastName: string;
|
|
|
|
|
};
|
|
|
|
|
messages?: ChatUI.AssistantMessage[];
|
|
|
|
|
streaming?: boolean;
|
2024-09-03 01:43:24 -07:00
|
|
|
|
loadingMessage?: string;
|
|
|
|
|
sessionId?: string;
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
close: [];
|
2024-09-03 01:43:24 -07:00
|
|
|
|
message: [string, string?, boolean?];
|
2024-08-14 05:59:11 -07:00
|
|
|
|
codeReplace: [number];
|
|
|
|
|
codeUndo: [number];
|
|
|
|
|
}>();
|
|
|
|
|
|
|
|
|
|
const onClose = () => emit('close');
|
|
|
|
|
|
|
|
|
|
const props = defineProps<Props>();
|
|
|
|
|
|
|
|
|
|
const textInputValue = ref<string>('');
|
|
|
|
|
|
|
|
|
|
const chatInput = ref<HTMLTextAreaElement | null>(null);
|
|
|
|
|
|
|
|
|
|
const sessionEnded = computed(() => {
|
|
|
|
|
return isEndOfSessionEvent(props.messages?.[props.messages.length - 1]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const sendDisabled = computed(() => {
|
|
|
|
|
return !textInputValue.value || props.streaming || sessionEnded.value;
|
|
|
|
|
});
|
|
|
|
|
|
2024-09-03 01:43:24 -07:00
|
|
|
|
const showPlaceholder = computed(() => {
|
|
|
|
|
return !props.messages?.length && !props.loadingMessage && !props.sessionId;
|
|
|
|
|
});
|
|
|
|
|
|
2024-09-23 04:05:15 -07:00
|
|
|
|
const isClipboardSupported = computed(() => {
|
|
|
|
|
return navigator.clipboard?.writeText;
|
|
|
|
|
});
|
|
|
|
|
|
2024-08-14 05:59:11 -07:00
|
|
|
|
function isEndOfSessionEvent(event?: ChatUI.AssistantMessage) {
|
|
|
|
|
return event?.type === 'event' && event?.eventName === 'end-session';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onQuickReply(opt: ChatUI.QuickReply) {
|
2024-09-03 01:43:24 -07:00
|
|
|
|
emit('message', opt.text, opt.type, opt.isFeedback);
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onSendMessage() {
|
|
|
|
|
if (sendDisabled.value) return;
|
2024-09-03 01:43:24 -07:00
|
|
|
|
emit('message', textInputValue.value);
|
2024-08-14 05:59:11 -07:00
|
|
|
|
textInputValue.value = '';
|
|
|
|
|
if (chatInput.value) {
|
|
|
|
|
chatInput.value.style.height = 'auto';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderMarkdown(content: string) {
|
|
|
|
|
try {
|
|
|
|
|
return md.render(content);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(`Error parsing markdown content ${content}`);
|
|
|
|
|
return `<p>${t('assistantChat.errorParsingMarkdown')}</p>`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function growInput() {
|
|
|
|
|
if (!chatInput.value) return;
|
|
|
|
|
chatInput.value.style.height = 'auto';
|
|
|
|
|
const scrollHeight = chatInput.value.scrollHeight;
|
|
|
|
|
chatInput.value.style.height = `${Math.min(scrollHeight, MAX_CHAT_INPUT_HEIGHT)}px`;
|
|
|
|
|
}
|
2024-09-23 04:05:15 -07:00
|
|
|
|
|
|
|
|
|
async function onCopyButtonClick(content: string, e: MouseEvent) {
|
|
|
|
|
const button = e.target as HTMLButtonElement;
|
|
|
|
|
await navigator.clipboard.writeText(content);
|
|
|
|
|
button.innerText = t('assistantChat.copied');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
button.innerText = t('assistantChat.copy');
|
|
|
|
|
}, 2000);
|
|
|
|
|
}
|
2024-08-14 05:59:11 -07:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div :class="$style.container">
|
|
|
|
|
<div :class="$style.header">
|
|
|
|
|
<div :class="$style.chatTitle">
|
|
|
|
|
<div :class="$style.headerText">
|
|
|
|
|
<AssistantIcon size="large" />
|
|
|
|
|
<AssistantText size="large" :text="t('assistantChat.aiAssistantLabel')" />
|
|
|
|
|
</div>
|
|
|
|
|
<BetaTag />
|
|
|
|
|
</div>
|
2024-08-20 04:05:09 -07:00
|
|
|
|
<div :class="$style.back" data-test-id="close-chat-button" @click="onClose">
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<n8n-icon icon="arrow-right" color="text-base" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div :class="$style.body">
|
|
|
|
|
<div v-if="messages?.length" :class="$style.messages">
|
2024-08-20 04:05:09 -07:00
|
|
|
|
<div
|
|
|
|
|
v-for="(message, i) in messages"
|
|
|
|
|
:key="i"
|
|
|
|
|
:class="$style.message"
|
|
|
|
|
:data-test-id="
|
|
|
|
|
message.role === 'assistant' ? 'chat-message-assistant' : 'chat-message-user'
|
|
|
|
|
"
|
|
|
|
|
>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<div
|
|
|
|
|
v-if="
|
|
|
|
|
!isEndOfSessionEvent(message) && (i === 0 || message.role !== messages[i - 1].role)
|
|
|
|
|
"
|
|
|
|
|
:class="{ [$style.roleName]: true, [$style.userSection]: i > 0 }"
|
|
|
|
|
>
|
|
|
|
|
<AssistantAvatar v-if="message.role === 'assistant'" />
|
|
|
|
|
<n8n-avatar
|
|
|
|
|
v-else
|
|
|
|
|
:first-name="user?.firstName"
|
|
|
|
|
:last-name="user?.lastName"
|
|
|
|
|
size="xsmall"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<span v-if="message.role === 'assistant'">{{
|
|
|
|
|
t('assistantChat.aiAssistantName')
|
|
|
|
|
}}</span>
|
|
|
|
|
<span v-else>{{ t('assistantChat.you') }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="message.type === 'block'">
|
|
|
|
|
<div :class="$style.block">
|
|
|
|
|
<div :class="$style.blockTitle">
|
|
|
|
|
{{ message.title }}
|
|
|
|
|
<BlinkingCursor
|
|
|
|
|
v-if="streaming && i === messages?.length - 1 && !message.content"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div :class="$style.blockBody">
|
2024-09-23 04:05:15 -07:00
|
|
|
|
<span
|
|
|
|
|
v-n8n-html="renderMarkdown(message.content)"
|
|
|
|
|
:class="$style['rendered-content']"
|
|
|
|
|
></span>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<BlinkingCursor
|
|
|
|
|
v-if="streaming && i === messages?.length - 1 && message.title && message.content"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else-if="message.type === 'text'" :class="$style.textMessage">
|
2024-09-17 23:49:41 -07:00
|
|
|
|
<span
|
|
|
|
|
v-if="message.role === 'user'"
|
|
|
|
|
v-n8n-html="renderMarkdown(message.content)"
|
2024-09-23 04:05:15 -07:00
|
|
|
|
:class="$style['rendered-content']"
|
2024-09-17 23:49:41 -07:00
|
|
|
|
></span>
|
2024-09-05 01:54:35 -07:00
|
|
|
|
<div
|
2024-08-14 05:59:11 -07:00
|
|
|
|
v-else
|
2024-09-17 23:49:41 -07:00
|
|
|
|
v-n8n-html="renderMarkdown(message.content)"
|
2024-09-23 04:05:15 -07:00
|
|
|
|
:class="[$style.assistantText, $style['rendered-content']]"
|
2024-09-05 01:54:35 -07:00
|
|
|
|
></div>
|
|
|
|
|
<div
|
|
|
|
|
v-if="message?.codeSnippet"
|
|
|
|
|
:class="$style['code-snippet']"
|
2024-09-16 03:27:44 -07:00
|
|
|
|
data-test-id="assistant-code-snippet"
|
2024-09-23 04:05:15 -07:00
|
|
|
|
>
|
|
|
|
|
<header v-if="isClipboardSupported">
|
|
|
|
|
<n8n-button
|
|
|
|
|
type="tertiary"
|
|
|
|
|
text="true"
|
|
|
|
|
size="mini"
|
|
|
|
|
data-test-id="assistant-copy-snippet-button"
|
|
|
|
|
@click="onCopyButtonClick(message.codeSnippet, $event)"
|
|
|
|
|
>
|
|
|
|
|
{{ t('assistantChat.copy') }}
|
|
|
|
|
</n8n-button>
|
|
|
|
|
</header>
|
|
|
|
|
<div
|
|
|
|
|
v-n8n-html="renderMarkdown(message.codeSnippet).trim()"
|
|
|
|
|
data-test-id="assistant-code-snippet-content"
|
|
|
|
|
:class="[$style['snippet-content'], $style['rendered-content']]"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<BlinkingCursor
|
|
|
|
|
v-if="streaming && i === messages?.length - 1 && message.role === 'assistant'"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2024-08-20 04:05:09 -07:00
|
|
|
|
<div
|
|
|
|
|
v-else-if="message.type === 'error'"
|
|
|
|
|
:class="$style.error"
|
|
|
|
|
data-test-id="chat-message-system"
|
|
|
|
|
>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<span>⚠️ {{ message.content }}</span>
|
2024-10-25 06:35:46 -07:00
|
|
|
|
<n8n-button
|
|
|
|
|
v-if="message.retry"
|
|
|
|
|
type="secondary"
|
|
|
|
|
size="mini"
|
|
|
|
|
:class="$style.retryButton"
|
|
|
|
|
data-test-id="error-retry-button"
|
|
|
|
|
@click="() => message.retry?.()"
|
|
|
|
|
>
|
|
|
|
|
{{ t('generic.retry') }}
|
|
|
|
|
</n8n-button>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
</div>
|
|
|
|
|
<div v-else-if="message.type === 'code-diff'">
|
|
|
|
|
<CodeDiff
|
|
|
|
|
:title="message.description"
|
|
|
|
|
:content="message.codeDiff"
|
|
|
|
|
:replacing="message.replacing"
|
|
|
|
|
:replaced="message.replaced"
|
|
|
|
|
:error="message.error"
|
|
|
|
|
:streaming="streaming && i === messages?.length - 1"
|
|
|
|
|
@replace="() => emit('codeReplace', i)"
|
|
|
|
|
@undo="() => emit('codeUndo', i)"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2024-08-20 04:05:09 -07:00
|
|
|
|
<div
|
|
|
|
|
v-else-if="isEndOfSessionEvent(message)"
|
|
|
|
|
:class="$style.endOfSessionText"
|
|
|
|
|
data-test-id="chat-message-system"
|
|
|
|
|
>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<span>
|
|
|
|
|
{{ t('assistantChat.sessionEndMessage.1') }}
|
|
|
|
|
</span>
|
|
|
|
|
<InlineAskAssistantButton size="small" :static="true" />
|
|
|
|
|
<span>
|
|
|
|
|
{{ t('assistantChat.sessionEndMessage.2') }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
v-if="
|
|
|
|
|
!streaming &&
|
|
|
|
|
'quickReplies' in message &&
|
|
|
|
|
message.quickReplies?.length &&
|
|
|
|
|
i === messages?.length - 1
|
|
|
|
|
"
|
|
|
|
|
:class="$style.quickReplies"
|
|
|
|
|
>
|
|
|
|
|
<div :class="$style.quickRepliesTitle">{{ t('assistantChat.quickRepliesTitle') }}</div>
|
2024-08-20 04:05:09 -07:00
|
|
|
|
<div v-for="opt in message.quickReplies" :key="opt.type" data-test-id="quick-replies">
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<n8n-button
|
|
|
|
|
v-if="opt.text"
|
|
|
|
|
type="secondary"
|
|
|
|
|
size="mini"
|
|
|
|
|
@click="() => onQuickReply(opt)"
|
|
|
|
|
>
|
|
|
|
|
{{ opt.text }}
|
|
|
|
|
</n8n-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-09-03 01:43:24 -07:00
|
|
|
|
<div v-if="loadingMessage" :class="$style.messages">
|
|
|
|
|
<AssistantLoadingMessage :message="loadingMessage" />
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-else-if="showPlaceholder"
|
|
|
|
|
:class="$style.placeholder"
|
|
|
|
|
data-test-id="placeholder-message"
|
|
|
|
|
>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
<div :class="$style.greeting">Hi {{ user?.firstName }} 👋</div>
|
|
|
|
|
<div :class="$style.info">
|
|
|
|
|
<p>
|
2024-09-03 01:43:24 -07:00
|
|
|
|
{{ t('assistantChat.placeholder.1') }}
|
2024-08-14 05:59:11 -07:00
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
{{ t('assistantChat.placeholder.2') }}
|
2024-09-03 01:43:24 -07:00
|
|
|
|
<InlineAskAssistantButton size="small" :static="true" />
|
2024-09-05 01:54:35 -07:00
|
|
|
|
{{ t('assistantChat.placeholder.3') }}
|
2024-08-14 05:59:11 -07:00
|
|
|
|
</p>
|
2024-09-03 01:43:24 -07:00
|
|
|
|
<p>
|
2024-09-05 01:54:35 -07:00
|
|
|
|
{{ t('assistantChat.placeholder.4') }}
|
2024-09-03 01:43:24 -07:00
|
|
|
|
</p>
|
2024-08-14 05:59:11 -07:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
:class="{ [$style.inputWrapper]: true, [$style.disabledInput]: sessionEnded }"
|
2024-08-20 04:05:09 -07:00
|
|
|
|
data-test-id="chat-input-wrapper"
|
2024-08-14 05:59:11 -07:00
|
|
|
|
>
|
|
|
|
|
<textarea
|
|
|
|
|
ref="chatInput"
|
|
|
|
|
v-model="textInputValue"
|
2024-11-07 07:22:13 -08:00
|
|
|
|
class="ignore-key-press-node-creator ignore-key-press-canvas"
|
2024-08-14 05:59:11 -07:00
|
|
|
|
:disabled="sessionEnded"
|
|
|
|
|
:placeholder="t('assistantChat.inputPlaceholder')"
|
|
|
|
|
rows="1"
|
|
|
|
|
wrap="hard"
|
2024-08-20 04:05:09 -07:00
|
|
|
|
data-test-id="chat-input"
|
2024-08-14 05:59:11 -07:00
|
|
|
|
@keydown.enter.exact.prevent="onSendMessage"
|
|
|
|
|
@input.prevent="growInput"
|
|
|
|
|
@keydown.stop
|
|
|
|
|
/>
|
|
|
|
|
<n8n-icon-button
|
|
|
|
|
:class="{ [$style.sendButton]: true }"
|
|
|
|
|
icon="paper-plane"
|
|
|
|
|
type="text"
|
|
|
|
|
size="large"
|
2024-08-20 04:05:09 -07:00
|
|
|
|
data-test-id="send-message-button"
|
2024-08-14 05:59:11 -07:00
|
|
|
|
:disabled="sendDisabled"
|
|
|
|
|
@click="onSendMessage"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" module>
|
|
|
|
|
.container {
|
|
|
|
|
height: 100%;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p {
|
|
|
|
|
line-height: var(--font-line-height-xloose);
|
|
|
|
|
margin: var(--spacing-2xs) 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
height: 65px; // same as header height in editor
|
|
|
|
|
padding: 0 var(--spacing-l);
|
|
|
|
|
background-color: var(--color-background-xlight);
|
|
|
|
|
border: var(--border-base);
|
|
|
|
|
border-top: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
|
|
div {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
> div:first-of-type {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.body {
|
|
|
|
|
background-color: var(--color-background-light);
|
|
|
|
|
border: var(--border-base);
|
|
|
|
|
border-top: 0;
|
|
|
|
|
height: 100%;
|
2024-08-21 02:03:59 -07:00
|
|
|
|
overflow-x: hidden;
|
|
|
|
|
overflow-y: auto;
|
2024-08-14 05:59:11 -07:00
|
|
|
|
padding-bottom: 250px; // make scrollable at the end
|
|
|
|
|
position: relative;
|
|
|
|
|
|
2024-08-21 02:03:59 -07:00
|
|
|
|
pre,
|
|
|
|
|
code {
|
|
|
|
|
text-wrap: wrap;
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.placeholder {
|
|
|
|
|
padding: var(--spacing-s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.messages {
|
|
|
|
|
padding: var(--spacing-xs);
|
2024-09-03 01:43:24 -07:00
|
|
|
|
|
|
|
|
|
& + & {
|
|
|
|
|
padding-top: 0;
|
|
|
|
|
}
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message {
|
|
|
|
|
margin-bottom: var(--spacing-xs);
|
|
|
|
|
font-size: var(--font-size-2xs);
|
|
|
|
|
line-height: var(--font-line-height-xloose);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.roleName {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: var(--spacing-3xs);
|
|
|
|
|
|
|
|
|
|
font-weight: var(--font-weight-bold);
|
|
|
|
|
font-size: var(--font-size-2xs);
|
|
|
|
|
|
|
|
|
|
> * {
|
|
|
|
|
margin-right: var(--spacing-3xs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.userSection {
|
|
|
|
|
margin-top: var(--spacing-l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chatTitle {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-3xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.headerText {
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.greeting {
|
|
|
|
|
color: var(--color-text-dark);
|
|
|
|
|
font-size: var(--font-size-m);
|
|
|
|
|
margin-bottom: var(--spacing-s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info {
|
|
|
|
|
font-size: var(--font-size-s);
|
|
|
|
|
color: var(--color-text-base);
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.back:hover {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.quickReplies {
|
|
|
|
|
margin-top: var(--spacing-s);
|
|
|
|
|
> * {
|
|
|
|
|
margin-bottom: var(--spacing-3xs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.quickRepliesTitle {
|
|
|
|
|
font-size: var(--font-size-3xs);
|
|
|
|
|
color: var(--color-text-base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.textMessage {
|
2024-09-03 01:43:24 -07:00
|
|
|
|
display: flex;
|
2024-09-05 01:54:35 -07:00
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
2024-08-14 05:59:11 -07:00
|
|
|
|
font-size: var(--font-size-2xs);
|
2024-09-05 01:54:35 -07:00
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 04:05:15 -07:00
|
|
|
|
code[class^='language-'] {
|
|
|
|
|
display: block;
|
|
|
|
|
padding: var(--spacing-4xs);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-05 01:54:35 -07:00
|
|
|
|
.code-snippet {
|
2024-09-23 04:05:15 -07:00
|
|
|
|
position: relative;
|
2024-09-05 01:54:35 -07:00
|
|
|
|
border: var(--border-base);
|
|
|
|
|
background-color: var(--color-foreground-xlight);
|
|
|
|
|
border-radius: var(--border-radius-base);
|
|
|
|
|
font-family: var(--font-family-monospace);
|
2024-09-23 04:05:15 -07:00
|
|
|
|
font-size: var(--font-size-3xs);
|
2024-09-05 01:54:35 -07:00
|
|
|
|
max-height: 218px; // 12 lines
|
|
|
|
|
overflow: auto;
|
2024-09-23 04:05:15 -07:00
|
|
|
|
margin: var(--spacing-4s) 0;
|
|
|
|
|
|
|
|
|
|
header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
padding: var(--spacing-4xs);
|
|
|
|
|
border-bottom: var(--border-base);
|
|
|
|
|
|
|
|
|
|
button:active,
|
|
|
|
|
button:focus {
|
|
|
|
|
outline: none !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.snippet-content {
|
|
|
|
|
padding: var(--spacing-2xs);
|
|
|
|
|
}
|
2024-09-05 01:54:35 -07:00
|
|
|
|
|
|
|
|
|
pre {
|
|
|
|
|
white-space-collapse: collapse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code {
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
font-size: var(--font-size-3xs);
|
|
|
|
|
}
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.block {
|
|
|
|
|
font-size: var(--font-size-2xs);
|
|
|
|
|
background-color: var(--color-foreground-xlight);
|
|
|
|
|
border: var(--border-base);
|
|
|
|
|
border-radius: var(--border-radius-base);
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
|
|
|
|
|
li {
|
|
|
|
|
margin-left: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.blockTitle {
|
|
|
|
|
border-bottom: var(--border-base);
|
|
|
|
|
padding: var(--spacing-2xs);
|
|
|
|
|
font-weight: var(--font-weight-bold);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.blockBody {
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.inputWrapper {
|
|
|
|
|
position: absolute;
|
|
|
|
|
display: flex;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
background-color: var(--color-foreground-xlight);
|
|
|
|
|
border: var(--border-base);
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding-top: 1px;
|
|
|
|
|
|
|
|
|
|
textarea {
|
|
|
|
|
border: none;
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
width: 100%;
|
|
|
|
|
font-size: var(--spacing-xs);
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
outline: none;
|
|
|
|
|
color: var(--color-text-dark);
|
|
|
|
|
resize: none;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sendButton {
|
|
|
|
|
color: var(--color-text-base) !important;
|
|
|
|
|
|
|
|
|
|
&[disabled] {
|
|
|
|
|
color: var(--color-text-light) !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.error {
|
|
|
|
|
color: var(--color-danger);
|
2024-10-25 06:35:46 -07:00
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.retryButton {
|
|
|
|
|
margin-top: var(--spacing-3xs);
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.assistantText {
|
2024-09-23 04:05:15 -07:00
|
|
|
|
display: inline-flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
2024-08-14 05:59:11 -07:00
|
|
|
|
|
2024-09-23 04:05:15 -07:00
|
|
|
|
.rendered-content {
|
2024-08-14 05:59:11 -07:00
|
|
|
|
p {
|
2024-09-23 04:05:15 -07:00
|
|
|
|
margin: 0;
|
|
|
|
|
margin: var(--spacing-4xs) 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h1,
|
|
|
|
|
h2,
|
|
|
|
|
h3 {
|
|
|
|
|
font-weight: var(--font-weight-bold);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
margin: var(--spacing-xs) 0 var(--spacing-4xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h4,
|
|
|
|
|
h5,
|
|
|
|
|
h6 {
|
|
|
|
|
font-weight: var(--font-weight-bold);
|
|
|
|
|
font-size: var(--font-size-2xs);
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ul,
|
|
|
|
|
ol {
|
2024-09-23 04:05:15 -07:00
|
|
|
|
margin: var(--spacing-4xs) 0 var(--spacing-4xs) var(--spacing-l);
|
|
|
|
|
|
|
|
|
|
ul,
|
|
|
|
|
ol {
|
|
|
|
|
margin-left: var(--spacing-xs);
|
|
|
|
|
margin-top: var(--spacing-4xs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:global(.table-wrapper) {
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table {
|
|
|
|
|
margin: var(--spacing-4xs) 0;
|
|
|
|
|
|
|
|
|
|
th {
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
min-width: 120px;
|
|
|
|
|
width: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
th,
|
|
|
|
|
td {
|
|
|
|
|
border: var(--border-base);
|
|
|
|
|
padding: var(--spacing-4xs);
|
|
|
|
|
}
|
2024-08-14 05:59:11 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.endOfSessionText {
|
|
|
|
|
margin-top: var(--spacing-l);
|
|
|
|
|
padding-top: var(--spacing-3xs);
|
|
|
|
|
border-top: var(--border-base);
|
|
|
|
|
color: var(--color-text-base);
|
|
|
|
|
|
|
|
|
|
> button,
|
|
|
|
|
> span {
|
|
|
|
|
margin-right: var(--spacing-3xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.disabledInput {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
|
|
|
|
* {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|