120 lines
4.4 KiB
Vue
120 lines
4.4 KiB
Vue
<script setup lang="ts">
|
|
import type { BaseMessage } from '~/composables/useChat';
|
|
import type { ModelWithProvider } from '~/composables/useModels';
|
|
|
|
const triplit = useTriplitClient();
|
|
|
|
const route = useRoute();
|
|
const inputValue = ref<BaseMessage>({ content: '', fileIds: [] });
|
|
const pendingMessage = ref<Message | null>(null);
|
|
|
|
const { open: sidebarOpen, openSidebar } = useSidebar();
|
|
const { createTopic, sendMessage, autoRename } = useChat(route.params.id as string);
|
|
const { getAgent } = useAgents();
|
|
const { providers } = useModels();
|
|
|
|
const agent = getAgent(route.params.id as string);
|
|
|
|
if (!agent.value) navigateTo('/');
|
|
|
|
useHead({ title: `${agent.value!.name} | Veridian` });
|
|
|
|
const handleSubmit = async (message: BaseMessage, model: ModelWithProvider | null) => {
|
|
if (!model) {
|
|
console.error('No model selected');
|
|
return;
|
|
}
|
|
|
|
const user = useAuth().user;
|
|
if (!user) {
|
|
console.error('No user');
|
|
return;
|
|
}
|
|
|
|
pendingMessage.value = {
|
|
id: '',
|
|
userId: user.value!.id,
|
|
topicId: null,
|
|
content: message.content,
|
|
role: 'user',
|
|
parts: [],
|
|
generation: null,
|
|
parentMessageId: null,
|
|
children: [],
|
|
generationId: null,
|
|
focusedIndex: null,
|
|
attachments: [],
|
|
deleted: false,
|
|
createdAt: new Date(),
|
|
}
|
|
|
|
const topic = await createTopic();
|
|
if (!topic) throw new Error('Failed to create topic');
|
|
|
|
autoRename(topic.id, message.content);
|
|
|
|
await navigateTo(`/agent/${route.params.id}/topic/${topic.id}`);
|
|
|
|
return sendMessage(message, topic, [], agent.value!, model.provider, model).then(async res => {
|
|
if (res.ok === false) {
|
|
console.error('Failed to send message:', res.error);
|
|
|
|
pendingMessage.value = null;
|
|
|
|
await navigateTo(`/agent/${route.params.id}`);
|
|
await triplit.delete('topics', topic.id);
|
|
|
|
// const chatInput = document.getElementById('chat') as HTMLInputElement;
|
|
// if (chatInput) {
|
|
// chatInput.value = message;
|
|
// chatInput.dispatchEvent(new Event('input'));
|
|
// nextTick(() => {
|
|
// chatInput.focus();
|
|
// });
|
|
// }
|
|
nextTick(() => {
|
|
inputValue.value = message;
|
|
});
|
|
}
|
|
});
|
|
};
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<div class="flex flex-col h-full w-full max-w-full overflow-x-hidden">
|
|
<div class="h-14 flex items-center justify-between px-4 border-b border-[var(--color-border)]">
|
|
<div class="flex items-center gap-2 max-w-full">
|
|
<button v-if="!sidebarOpen" @click="openSidebar"
|
|
class="flex p-2 rounded-lg text-[var(--text-primary)] bg-transparent hover:bg-[var(--color-hover)] transition-colors">
|
|
<span class="i-mynaui-panel-left-open text-5"></span>
|
|
</button>
|
|
<h4 class="text-lg text-ellipsis overflow-hidden whitespace-nowrap">
|
|
{{ agent?.name }}
|
|
</h4>
|
|
</div>
|
|
</div>
|
|
|
|
<div ref="chatPaneWrapper"
|
|
class="chat-scroll-container justify-center w-full h-full [scrollbar-width:thin] [scrollbar-color:#888_transparent] overflow-y-scroll overflow-x-hidden flex justify-center pt-4">
|
|
<div class="chatPane max-w-4xl w-full min-h-full flex flex-col px-4">
|
|
<div class="w-full px-px flex flex-col flex-grow gap-2"
|
|
:class="pendingMessage === null ? 'justify-end pb-28' : 'pb-9'">
|
|
<div v-if="pendingMessage === null">
|
|
<h1 v-if="agent" class="font-bold">{{ agent.name }}</h1>
|
|
<p class="text-[var(--text-secondary)]">Select a topic to continue or create a new one</p>
|
|
</div>
|
|
<div v-else class="opacity-70">
|
|
<Message :message="pendingMessage" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sticky bottom-0 z-10 bg-[var(--bg-surface)] pb-4 w-full rounded-t-2xl">
|
|
<ChatInput v-model="inputValue" class="[view-transition-name:chat-prompt] duration-150 ease-in-out"
|
|
:agent="agent" :providers="providers?.filter(p => p.enabled)" @submit="handleSubmit" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|