diff --git a/src/lib/components/InputBar.svelte b/src/lib/components/InputBar.svelte index 62ea928..726f93e 100644 --- a/src/lib/components/InputBar.svelte +++ b/src/lib/components/InputBar.svelte @@ -164,6 +164,17 @@ attachments = storedAttachments; }); + // Per-tab draft persistence — restore the draft text whenever the active + // conversation changes, and save it back on every keystroke. + claudeStore.activeConversationId.subscribe((conversationId) => { + if (conversationId) { + const conv = get(claudeStore.conversations).get(conversationId); + inputValue = conv?.draftText ?? ""; + } else { + inputValue = ""; + } + }); + function handleInputChange() { // If input is empty, allow history navigation again // Otherwise, mark that user has manually typed @@ -176,6 +187,12 @@ historyIndex = -1; tempInput = ""; + // Save the current draft so it persists if the user switches tabs. + const activeId = get(claudeStore.activeConversationId); + if (activeId) { + claudeStore.setDraftText(activeId, inputValue); + } + if (isSlashCommand(inputValue)) { matchingCommands = getMatchingCommands(inputValue); showCommandMenu = matchingCommands.length > 0; diff --git a/src/lib/stores/claude.ts b/src/lib/stores/claude.ts index ccfc441..8220beb 100644 --- a/src/lib/stores/claude.ts +++ b/src/lib/stores/claude.ts @@ -66,6 +66,9 @@ export const claudeStore = { markSuccessSoundFired: conversationsStore.markSuccessSoundFired, markTaskStartSoundFired: conversationsStore.markTaskStartSoundFired, + // Draft text (per-tab input persistence) + setDraftText: conversationsStore.setDraftText, + // Conversation management createConversation: conversationsStore.createConversation, deleteConversation: conversationsStore.deleteConversation, diff --git a/src/lib/stores/conversations.test.ts b/src/lib/stores/conversations.test.ts index ddc2d0a..c587109 100644 --- a/src/lib/stores/conversations.test.ts +++ b/src/lib/stores/conversations.test.ts @@ -523,3 +523,41 @@ describe("pending retry message", () => { expect(pendingRetryMessage).toBeNull(); }); }); + +describe("draft text persistence", () => { + it("initialises draft text as empty string", () => { + const conversation = { draftText: "" }; + expect(conversation.draftText).toBe(""); + }); + + it("stores draft text per conversation", () => { + const conversations = new Map([ + ["conv-1", { draftText: "Hello world" }], + ["conv-2", { draftText: "" }], + ]); + + expect(conversations.get("conv-1")?.draftText).toBe("Hello world"); + expect(conversations.get("conv-2")?.draftText).toBe(""); + }); + + it("updates draft text independently per conversation", () => { + const conversations = new Map([ + ["conv-1", { draftText: "Draft A" }], + ["conv-2", { draftText: "Draft B" }], + ]); + + const convA = conversations.get("conv-1"); + if (convA) convA.draftText = "Updated A"; + + expect(conversations.get("conv-1")?.draftText).toBe("Updated A"); + expect(conversations.get("conv-2")?.draftText).toBe("Draft B"); + }); + + it("clears draft text after submission", () => { + const conversation = { draftText: "My prompt" }; + + conversation.draftText = ""; + + expect(conversation.draftText).toBe(""); + }); +}); diff --git a/src/lib/stores/conversations.ts b/src/lib/stores/conversations.ts index 3a3ff51..68ec630 100644 --- a/src/lib/stores/conversations.ts +++ b/src/lib/stores/conversations.ts @@ -40,6 +40,7 @@ export interface Conversation { taskStartTime: number | null; successSoundFired: boolean; taskStartSoundFired: boolean; + draftText: string; } function createConversationsStore() { @@ -81,6 +82,7 @@ function createConversationsStore() { taskStartTime: null, successSoundFired: false, taskStartSoundFired: false, + draftText: "", }; } @@ -871,6 +873,16 @@ function createConversationsStore() { }); }, + setDraftText: (conversationId: string, text: string) => { + conversations.update((convs) => { + const conv = convs.get(conversationId); + if (conv) { + conv.draftText = text; + } + return convs; + }); + }, + // Add initialization helper initialize: () => { ensureInitialized();