From a4f0a0a7424fefcc19e52b871c531ce54cb06964 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Mon, 22 Jun 2026 03:06:52 +0900 Subject: feat(chat): stop generation button — abort without closing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consume the stop-generation handoff (no version bumps, no new types). - App store: stopGeneration() → POST /conversations/:id/stop (fire-and-forget) - Composer: stop button (square, error color) visible only while generating, next to the send/queue button - Existing event flow handles the rest: done with reason 'aborted' clears generating; conversation.statusChanged: idle updates the tab spinner 686 tests green. --- src/app/App.svelte | 5 +++++ src/app/store.svelte.ts | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'src/app') diff --git a/src/app/App.svelte b/src/app/App.svelte index 350db49..3024a44 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -195,6 +195,10 @@ store.queueMessage(text); } + function handleStop() { + store.stopGeneration(); + } + function handleSelectModel(model: string) { store.selectModel(model); } @@ -372,6 +376,7 @@ ; + /** + * Stop an in-flight generation (`POST /conversations/:id/stop`). Aborts the + * turn without closing the conversation — partial messages are persisted, the + * turn seals with `reason: "aborted"`, and the conversation goes `active → idle`. + * Returns null when no conversation is focused. + */ + stopGeneration(): void; /** * The workspace conversation's auto-compact threshold (tokens). `0` = disabled * (manual only); a positive number = auto-compact triggers when the last @@ -934,6 +941,16 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore { } }, + stopGeneration(): void { + const conversationId = tabsStore.activeConversationId; + if (conversationId === null) return; + void fetchImpl(`${httpBase}/conversations/${encodeURIComponent(conversationId)}/stop`, { + method: "POST", + }).catch(() => { + // Non-fatal — the existing event flow handles the turn settle. + }); + }, + async compactNow(keepLastN?: number): Promise { const conversationId = tabsStore.activeConversationId; if (conversationId === null) return null; -- cgit v1.2.3