From d11988c3d0dcd8fb6f1b3c3815d9c06dca170b98 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 7 Jun 2026 08:42:12 +0100 Subject: [PATCH] refac --- CHANGELOG.md | 6 +++++ cptr/frontend/package.json | 2 +- cptr/frontend/src/lib/apis/chat.ts | 2 ++ .../src/lib/components/chat/ChatPanel.svelte | 27 +++++++++++++++---- cptr/routers/chat.py | 11 +++++++- pyproject.toml | 2 +- 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16ef4aa1..710096ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.7] - 2026-06-07 + +### Fixed + +- 🔄 **Messages no longer disappear after sending.** Fixed a race condition where the frontend needed a separate GET request after sending a message, creating a window where socket events were dropped. The POST now returns the created messages directly, eliminating the round-trip and the race. + ## [0.1.6] - 2026-06-07 ### Added diff --git a/cptr/frontend/package.json b/cptr/frontend/package.json index deb494cb..4e2cb2b8 100644 --- a/cptr/frontend/package.json +++ b/cptr/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "0.1.4", + "version": "0.1.7", "type": "module", "scripts": { "dev": "vite dev", diff --git a/cptr/frontend/src/lib/apis/chat.ts b/cptr/frontend/src/lib/apis/chat.ts index ad4a84df..23906646 100644 --- a/cptr/frontend/src/lib/apis/chat.ts +++ b/cptr/frontend/src/lib/apis/chat.ts @@ -36,6 +36,8 @@ export interface SendMessageResult { chat_id: string; message_id: string; queued?: boolean; + user_message?: ChatMessageRow; + assistant_message?: ChatMessageRow; } // ── Queries ───────────────────────────────────────────────── diff --git a/cptr/frontend/src/lib/components/chat/ChatPanel.svelte b/cptr/frontend/src/lib/components/chat/ChatPanel.svelte index 0d6bef44..f852938a 100644 --- a/cptr/frontend/src/lib/components/chat/ChatPanel.svelte +++ b/cptr/frontend/src/lib/components/chat/ChatPanel.svelte @@ -541,8 +541,9 @@ created_at: Date.now() } ]; - } else { - await loadChat(result.chat_id); + } else if (result.assistant_message) { + allMessages = [...allMessages, result.assistant_message]; + currentMessageId = result.message_id; } } catch (e) { console.error('[chat] send (queue) error', e); @@ -588,7 +589,17 @@ undefined, files ); - await loadChat(result.chat_id); + + // Swap optimistic temp msg with real messages from backend. + chatId = result.chat_id; + const withoutTemp = allMessages.filter((m) => m.id !== tempId); + if (result.user_message && result.assistant_message) { + allMessages = [...withoutTemp, result.user_message, result.assistant_message]; + } else if (result.assistant_message) { + allMessages = [...withoutTemp, result.assistant_message]; + } + currentMessageId = result.message_id; + if (isNew && tabId) { updateTab(tabId, result.chat_id, text.slice(0, 40) || 'Chat'); } @@ -737,7 +748,10 @@ tool_approval_mode: mode, plan_mode: get(planMode) }); - await loadChat(result.chat_id); + if (result.assistant_message) { + allMessages = [...allMessages, result.assistant_message]; + currentMessageId = result.message_id; + } } catch (e) { console.error('[chat] regenerate error', e); } @@ -777,7 +791,10 @@ msg.parent_id, { tool_approval_mode: get(toolApprovalMode), plan_mode: get(planMode) } ); - await loadChat(result.chat_id); + if (result.user_message && result.assistant_message) { + allMessages = [...allMessages, result.user_message, result.assistant_message]; + currentMessageId = result.message_id; + } } catch (e) { console.error('[chat] edit-send error', e); } diff --git a/cptr/routers/chat.py b/cptr/routers/chat.py index a8640a61..d014dc1c 100644 --- a/cptr/routers/chat.py +++ b/cptr/routers/chat.py @@ -423,7 +423,16 @@ async def send_message(body: SendMessageRequest, request: Request): regeneration_prompt=body.regeneration_prompt, ) - return {"chat_id": chat.id, "message_id": assistant_msg.id} + # Return the created messages so the frontend can append them + # directly — no separate GET needed, no client-side construction. + resp: dict = { + "chat_id": chat.id, + "message_id": assistant_msg.id, + "assistant_message": _message_dict(assistant_msg), + } + if parent_msg is None or parent_msg.role != "user": + resp["user_message"] = _message_dict(user_msg) + return resp # ── Approve / reject a pending tool call ──────────────────── diff --git a/pyproject.toml b/pyproject.toml index aaa761fa..3e12edd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cptr" -version = "0.1.6" +version = "0.1.7" description = "Your computer, from anywhere. Code, manage, and control your machine from the web." license = {file = "LICENSE"} readme = "README.md"