diff --git a/src/routes/motd/motd.md b/src/routes/motd/motd.md index 2e58f8f5..a70dd5a2 100644 --- a/src/routes/motd/motd.md +++ b/src/routes/motd/motd.md @@ -8,6 +8,7 @@ - **Redesigned sidebar navigation** with improved accessibility and mobile experience. - **Copy & paste images** directly into the prompt field for quick image attachments. +- **Improved stop completion** now preserves partial responses instead of discarding them. #### Previously, in Hollama diff --git a/src/routes/sessions/[id]/+page.svelte b/src/routes/sessions/[id]/+page.svelte index 6133674a..a479c1ef 100644 --- a/src/routes/sessions/[id]/+page.svelte +++ b/src/routes/sessions/[id]/+page.svelte @@ -241,12 +241,25 @@ } function stopCompletion() { - editor.prompt = session.messages[session.messages.length - 1].content; // Reset the prompt to the last sent message editor.abortController?.abort(); + + // Add the incomplete message to session if there's any content + if (editor.completion || editor.reasoning) { + const message: Message = { + role: 'assistant', + content: editor.completion || '', + reasoning: editor.reasoning || '' + }; + session.messages = [...session.messages, message]; + session.updatedAt = new Date().toISOString(); + saveSession(session); + } + + // Clear editor state editor.completion = ''; + editor.reasoning = ''; editor.isCompletionInProgress = false; - session.messages = session.messages.slice(0, -1); // Remove the "incomplete" AI response - editor.isNewSession = !session.messages.length; + editor.shouldFocusTextarea = true; } function handleError(error: Error) { @@ -255,7 +268,18 @@ } else { toast.error($LL.genericError(), { description: error.toString() }); } - stopCompletion(); + + // For errors, restore the prompt so user can retry + const lastUserMessage = session.messages.filter((m) => m.role === 'user').at(-1); + if (lastUserMessage) { + editor.prompt = lastUserMessage.content; + } + + editor.abortController?.abort(); + editor.completion = ''; + editor.reasoning = ''; + editor.isCompletionInProgress = false; + editor.shouldFocusTextarea = true; } function handleScroll() { diff --git a/tests/session-interaction.test.ts b/tests/session-interaction.test.ts index 1a206130..1f199c34 100644 --- a/tests/session-interaction.test.ts +++ b/tests/session-interaction.test.ts @@ -234,11 +234,11 @@ test.describe('Session interaction', () => { await promptTextarea.fill(''); await stopButton.click(); - await expect(page.getByText('Write a prompt to start a new session')).toBeVisible(); - await expect(userMessage).not.toBeVisible(); - await expect(aiMessage).not.toBeVisible(); + await expect(page.getByText('Write a prompt to start a new session')).not.toBeVisible(); + await expect(userMessage).toBeVisible(); + // AI message may not be visible if no content was generated before stopping await expect(stopButton).not.toBeVisible(); - await expect(promptTextarea).toHaveValue('Hello world!'); + await expect(promptTextarea).toHaveValue(''); }); test('can toggle the prompt editor fullscreen', async ({ page }) => { @@ -578,7 +578,7 @@ test.describe('Session interaction', () => { // Check that the completion has stopped await page.goto('/sessions'); const sessionsCount = await page.locator('.session__history').count(); - expect(sessionsCount).toBe(0); + expect(sessionsCount).toBe(0); // No session saved since no content was generated }); test('warns when navigating away with unsaved prompt content', async ({ page }) => {