Add new conversation button

This commit is contained in:
dwrz
2026-10-31 17:20:37 +00:00
parent d9ce81af9c
commit 456d9ee6f3
5 changed files with 98 additions and 22 deletions
+44 -21
View File
@@ -80,6 +80,7 @@ class Odidere {
onSelectConversation: (id) => this.switchConversation(id),
onRenameConversation: (id) => this.renameConversation(id),
onDeleteConversation: (id) => this.deleteConversation(id),
onNewConversation: () => this.newConversation(),
onPushToTalkStart: () => this.handlePushToTalkStart(),
onPushToTalkEnd: () => this.handlePushToTalkEnd(),
onTranscribeStart: () => this.handleTranscribeStart(),
@@ -700,16 +701,7 @@ class Odidere {
async reset() {
this.tts.stop();
const id = this.conversationId;
// Clear the selection first, so the aborted run's cleanup does
// not open a new subscription to a conversation being deleted.
this.conversationId = null;
this.syncController?.abort();
this.runController?.abort();
if (this.currentRun) {
try {
await this.currentRun;
} catch {}
}
await this.detachConversation();
if (id) {
try {
await this.api.deleteConversation(id);
@@ -742,6 +734,31 @@ class Odidere {
this.updateSendEnabled();
}
/**
* detachConversation unselects the conversation, aborts any open
* sync or inference stream, and waits for an in-flight run to
* finish its cleanup.
*
* It does not stop the run on the server; it only ends local
* stream delivery.
*
* conversationId is cleared before the aborts so that the ending
* streams do not persist the cache, open a new sync stream, or
* fetch approvals for the old conversation. Awaiting currentRun
* lets run's finally block clear busy and drafts before the caller
* clears local state or loads another conversation.
*/
async detachConversation() {
this.conversationId = null;
this.syncController?.abort();
this.runController?.abort();
if (this.currentRun) {
try {
await this.currentRun;
} catch {}
}
}
// ====================
// CONVERSATIONS
// ====================
@@ -873,23 +890,29 @@ class Odidere {
}
this.conversations = this.conversations.filter((c) => c.id !== id);
if (id === this.conversationId) {
// The server-side delete cancels an ongoing inference. Detach
// the local streams before the state goes, so the aborted
// run's cleanup does not open a new subscription.
// The server-side delete cancels an ongoing inference.
this.tts.stop();
this.conversationId = null;
this.syncController?.abort();
this.runController?.abort();
if (this.currentRun) {
try {
await this.currentRun;
} catch {}
}
await this.detachConversation();
this.clearLocalConversation();
}
this.filterConversations(this.conversationQuery);
}
/**
* newConversation closes the picker and clears the view for a
* new conversation. It sends no request: send() creates the
* conversation when the next message goes out, so an abandoned
* click leaves no empty conversation on the server. A run in
* flight on the old conversation keeps running there.
*/
async newConversation() {
this.ui.closeConversations();
if (!this.conversationId) return;
this.tts.stop();
await this.detachConversation();
this.clearLocalConversation();
}
// ====================
// SYNC
// ====================