Support creating chats. Add agents in sidebar. Improve chat state management

This commit is contained in:
Shivering Ocelot committed 2025-07-12 19:27:15 -05:00
1 parent 73fe8a95d4
commit f8edd01c37
6 files changed
+184 -49

No files matched your search

+62 -38
View File
@@ -1,6 +1,6 @@
<script setup>
import {
ref, computed, reactive,
ref, watch,
onMounted, onUnmounted,
} from "vue";
@@ -24,16 +24,73 @@
const editingMessageId = ref(null);
const noteMode = ref(false);
let _isMounted = false;
let unsubscribeChat = null;
// === State Management === //
async function loadChatState (chatId) {
unsubscribeChat = backend.onChatMessages(chatId, it => {
if (it.sender == 'user') return;
addMessage({
type: it.kind,
sender: it.sender,
content: it.content,
threadId: it.threadId,
});
});
let response = await backend.sendAction("get-chat-messages", {chatId});
// Always good to duplicate when assigning to a ref
messages.value = response['messages'].map(it => ({
type: it.kind,
sender: it.sender,
content: it.content,
threadId: it.threadId,
}));
// This must happen on the same async-thread that sets `messages`
requestRescroll({forceBottom: true});
// Reset UI State
editingMessageId.value = null;
noteMode.value = false;
// no need to reset expandedThreads, it's nice that it will be remembered
}
async function unloadChatState () {
if (unsubscribeChat) unsubscribeChat();
unsubscribeChat = null;
}
// This won't fire when called, the mounted callback will take care
watch(() => props.id, newChatId => {
if (_isMounted) {
unloadChatState();
loadChatState(newChatId);
}
})
onMounted(async () => {
_isMounted = true;
loadChatState(props.id);
});
onUnmounted(async () => {
_isMounted = false;
unloadChatState();
});
// === Chat Functionality === //
const inputFieldElem = ref(null);
const chatContainerElem = ref(null);
async function getChatMessages() {
let response = await backend.sendAction("get-chat-messages", {'chatId': props.id});
async function getChatMessages(chatId) {
let response = await backend.sendAction("get-chat-messages", {chatId});
let messageObjects = response['messages'];
return messageObjects;
}
@@ -97,7 +154,7 @@
// === UI Helper Functions === //
function requestRescroll () {
function requestRescroll ({forceBottom=false, bottomRelative=false}) {
const elem = chatContainerElem.value;
const scroll = elem.scrollTop;
@@ -107,7 +164,7 @@
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
// Chat was fully scrolled down
if (scroll == max) {
if (forceBottom || scroll == max) {
elem.scrollTop = elem.scrollTopMax;
} else {
// Do nothing, scroll remains in place
@@ -133,39 +190,6 @@
}
}
// === Initialize === //
onMounted(async () => {
requestRescroll();
messages.value = (await getChatMessages()).map(it => {
return {
type: it.kind,
sender: it.sender,
content: it.content,
threadId: it.threadId,
};
});
if (unsubscribeChat) unsubscribeChat();
unsubscribeChat = backend.onChatMessages(props.id, it => {
if (it.sender == 'user') return;
addMessage({
type: it.kind,
sender: it.sender,
content: it.content,
threadId: it.threadId,
});
});
});
onMounted(async () => {
if (unsubscribeChat) {
unsubscribeChat();
unsubscribeChat = null;
}
});
</script>
<template>