Per-conv streaming state, immediate message_count commit, auto-new-chat on open
stores/chat.ts: - Replace 7 global stream refs with convStreams: Record<number, ConvStreamState> - Each conversation tracks its own stream state; 7 names become computed getters - Add isStreamingConv(id) public helper - Increment message_count +2 immediately after POST 202 (not at done) so the cleanup watcher never deletes a mid-generation conversation - SSE reconnect retries now run for background conversations regardless of which conv is currently viewed - error toasts only shown when the erroring conv is currently viewed - deleteConversation cleans up orphaned stream state ChatView.vue: - Remove invalid write to store.lastContextMeta (now a read-only computed) - Add !store.isStreamingConv(newId) guard to watch(convId) fetch so navigating back to a generating conversation shows accumulated content without stale refetch - Add startNewConversation() helper; opening /chat with no ID auto-creates a new conversation and redirects to it (on mount and on navigation) - Deleting a conversation also auto-creates a new one Also committing previous-session changes (keyboard nav + task routing): - App.vue: e shortcut only on note-view (not task-view, which is removed) - TaskCard.vue: route directly to /tasks/:id (viewer), remove View buttons - router/index.ts: remove task-view route; /tasks/:id now maps to TaskEditorView - TasksListView.vue: Enter key pushes to /tasks/:id Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -85,8 +85,12 @@ const inputPlaceholder = computed(() => {
|
||||
onMounted(async () => {
|
||||
document.addEventListener("keydown", onGlobalKeydown);
|
||||
await store.fetchConversations();
|
||||
if (convId.value && store.currentConversation?.id !== convId.value) {
|
||||
await store.fetchConversation(convId.value);
|
||||
if (convId.value) {
|
||||
if (store.currentConversation?.id !== convId.value) {
|
||||
await store.fetchConversation(convId.value);
|
||||
}
|
||||
} else {
|
||||
await startNewConversation();
|
||||
}
|
||||
nextTick(() => inputEl.value?.focus());
|
||||
});
|
||||
@@ -106,16 +110,15 @@ watch(convId, async (newId) => {
|
||||
suggestedNotes.value = [];
|
||||
autoInjectedNotes.value = [];
|
||||
excludedNoteIds.value = [];
|
||||
store.lastContextMeta = null;
|
||||
if (newId) {
|
||||
// Skip re-fetch if this conversation is already loaded (avoids race
|
||||
// condition where a stale GET overwrites messages during streaming)
|
||||
if (store.currentConversation?.id !== newId) {
|
||||
// Skip re-fetch if this conversation is already loaded or still streaming
|
||||
// (avoids a stale GET overwriting in-progress messages)
|
||||
if (store.currentConversation?.id !== newId && !store.isStreamingConv(newId)) {
|
||||
await store.fetchConversation(newId);
|
||||
}
|
||||
scrollToBottom();
|
||||
} else {
|
||||
store.currentConversation = null;
|
||||
await startNewConversation();
|
||||
}
|
||||
nextTick(() => inputEl.value?.focus());
|
||||
});
|
||||
@@ -177,10 +180,14 @@ async function selectConversation(id: number) {
|
||||
router.push(`/chat/${id}`);
|
||||
}
|
||||
|
||||
async function newConversation() {
|
||||
async function startNewConversation() {
|
||||
const conv = await store.createConversation();
|
||||
await store.fetchConversation(conv.id);
|
||||
router.push(`/chat/${conv.id}`);
|
||||
// watch(convId) will fire and fetch the conversation
|
||||
}
|
||||
|
||||
async function newConversation() {
|
||||
await startNewConversation();
|
||||
}
|
||||
|
||||
async function removeConversation(id: number) {
|
||||
|
||||
@@ -39,7 +39,7 @@ function onKeydown(e: KeyboardEvent) {
|
||||
document.querySelector(".kb-active-item")?.scrollIntoView({ block: "nearest" });
|
||||
} else if (e.key === "Enter" && activeIndex.value >= 0) {
|
||||
const task = store.tasks[activeIndex.value];
|
||||
if (task) router.push(`/tasks/${task.id}/edit`);
|
||||
if (task) router.push(`/tasks/${task.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user