From db85fcd3b681e5ccb969dd9ae1f5f9240e1c419d Mon Sep 17 00:00:00 2001 From: Jonas Kunert Date: Wed, 22 Jul 2026 17:21:11 +0200 Subject: [PATCH] feat: persist the unsent input draft per workspace The webview already reported the input text to the extension via the debounced saveInputText message and got it back through restoreInputText on panel init - but _draftMessage lived only in memory, so closing the panel's extension host or restarting VS Code lost the draft (#84). - load _draftMessage from workspaceState ('claudeCodeChat.inputDraft') in the constructor, - persist non-empty drafts in _saveInputText, delete the key for empty/whitespace-only text, - clear the persisted draft when a message is sent (extension side and webview side: cancel the pending debounce and post an empty saveInputText right after the field is cleared), - restoreInputText no longer overwrites a non-empty field, so text held alive by retainContextWhenHidden wins over the persisted fallback. No new message types, settings, or dependencies. Co-Authored-By: Claude Fable 5 --- src/extension.ts | 11 +++++++++++ src/script.ts | 13 ++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 8fa37fb..336e9cc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -215,6 +215,10 @@ class ClaudeChatProvider { // Load saved model preference this._selectedModel = this._context.workspaceState.get('claude.selectedModel', 'default'); + // Load persisted input draft so it survives panel disposal / VS Code + // restarts, restored to the webview once ready + this._draftMessage = this._context.workspaceState.get('claudeCodeChat.inputDraft', ''); + // Load cached subscription type (will be refreshed on first message) this._subscriptionType = this._context.globalState.get('claude.subscriptionType'); @@ -895,6 +899,7 @@ class ClaudeChatProvider { // Clear draft message since we're sending it this._draftMessage = ''; + this._context.workspaceState.update('claudeCodeChat.inputDraft', undefined); // Show original user input in chat and save to conversation (without mode prefixes) this._sendAndSaveMessage({ @@ -3428,6 +3433,12 @@ class ClaudeChatProvider { private _saveInputText(text: string): void { this._draftMessage = text || ''; + // Persist so the draft survives panel disposal and VS Code restarts + if (this._draftMessage.trim()) { + this._context.workspaceState.update('claudeCodeChat.inputDraft', this._draftMessage); + } else { + this._context.workspaceState.update('claudeCodeChat.inputDraft', undefined); + } } private async _updateSettings(settings: { [key: string]: any }): Promise { diff --git a/src/script.ts b/src/script.ts index 4c949e2..708413c 100644 --- a/src/script.ts +++ b/src/script.ts @@ -949,6 +949,14 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt messageInput.value = ''; attachedImages = []; renderImagePreviews(); + + // Clear the persisted draft immediately so no stale text survives + // a restart shortly after sending + clearTimeout(saveInputTimeout); + vscode.postMessage({ + type: 'saveInputText', + text: '' + }); } } @@ -3553,7 +3561,10 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt case 'restoreInputText': const inputField = document.getElementById('messageInput'); - if (inputField && message.data) { + // retainContextWhenHidden already keeps in-progress typing across tab + // switches, so this restore is only the fallback for a fresh panel / + // VS Code restart — don't clobber text the user already has + if (inputField && message.data && !inputField.value.trim()) { inputField.value = message.data; // Auto-resize the textarea inputField.style.height = 'auto';