wip: thread revamp

This commit is contained in:
Shivering Ocelot committed 2026-09-23 13:44:22 -05:00
1 parent 5b3f8b0f09
commit 9277b5a770
11 files changed
+401 -205

No files matched your search

+64 -3
View File
@@ -79,6 +79,8 @@ export class ChatSession {
this._rescroll = callback;
}
// Call after changing the messages display length (change list or content)
// It protects the scroll position in the document
rescroll (options) {
if (this._rescroll) this._rescroll(options);
}
@@ -117,7 +119,9 @@ export class ChatSession {
this._unsubscribeChat = backend.onChatMessages(chatId, it => this.receiveMessage(it));
this._unsubscribeRuns = runs.onChatMessage(chatId, event => this.trackRun(event));
const response = await backend.sendAction("get-chat-messages", {chatId});
// Don't load all messages, only thread headers
//const response = await backend.sendAction("get-chat-messages", {chatId});
const response = await backend.sendAction("get-chat-thread-heads", {chatId});
if (this.chatId != chatId) return;
// Always good to duplicate when assigning to a ref
@@ -134,8 +138,7 @@ export class ChatSession {
// otherwise start in "New Thread" state
const lastMessage = response.messages[response.messages.length - 1];
if (lastMessage && lastMessage.threadId != null && !isMessageStale(lastMessage)) {
this.currentThreadId.value = lastMessage.threadId;
this.isNewThread.value = false;
this.activateThread(lastMessage.threadId);
}
// Must happen on the same async-thread that sets `messages`
@@ -317,6 +320,21 @@ export class ChatSession {
// Continuing a thread: it becomes the one open thread
this.currentThreadId.value = threadId;
this.isNewThread.value = false;
// Load thread messages asynchronously
backend.sendAction("get-thread-messages", {
chatId: this.chatId, threadId
}).then(res => {
this.addMessageBulk(res.messages.map(it => ({
id: it.id,
type: it.kind,
format: it.format,
subkind: it.subkind,
sender: it.sender,
content: it.content,
threadId: it.threadId,
})));
});
this.rescroll();
}
@@ -327,6 +345,49 @@ export class ChatSession {
this.rescroll();
}
// Adds a bulk of messages to the message list, in their correct
// chronological position and replacing duplicates
addMessageBulk (messages) {
const list = this.messages.value;
for (let message of messages) {
const id = message.id;
// Replace an already-known message in place
const existingIndex = list.findIndex(it => it.id == id);
if (existingIndex >= 0) {
list[existingIndex] = message;
continue;
}
// An unsent user message echoed back with an id: adopt it
if (message.sender == 'user') {
const unsentIndex = this.unsentMessages.value.findIndex(
it => it.content == message.content
);
if (unsentIndex >= 0) {
this.unsentMessages.value.splice(unsentIndex, 1);
}
}
// Insert after the last message known to belong to the same thread,
// keeping each thread's messages contiguous and ordered
let insertIndex = -1;
for (let i = list.length - 1; i >= 0; i--) {
if (list[i].threadId != null && list[i].threadId == message.threadId) {
insertIndex = i + 1;
break;
}
}
if (insertIndex < 0) insertIndex = list.length;
list.splice(insertIndex, 0, message);
}
// Refs need reassignment to notify watchers; duplicate the list
this.messages.value = list.slice();
}
// === Entry Computation === //
+5 -4
View File
@@ -114,8 +114,9 @@
async function chatEntryContextMenu (message, logMeta) {
let entries = [
{value: 'pin', title: "Pin message"},
{value: 'edit', title: "Edit message"},
{value: 'start-thread', title: "Start thread from here"},
{value: 'edit', title: "Edit in-place"},
{value: 'edit-fork', title: "Edit in new thread"},
//{value: 'start-thread', title: "Start thread from here"},
];
// Long logs can be expanded/collapsed
@@ -136,8 +137,8 @@
editMessage(message);
break;
}
case 'start-thread': {
alert("Start Thread (TODO)");
case 'edit-fork': {
editMessage(message, true);
break;
}
case 'toggle-log': {