diff --git a/.gitignore b/.gitignore index a1dc9e0..d2cbbe7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .vscode/ node_modules/ dist/ +logs/ *.vsix .DS_Store filter_exception_decoder.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 640e51b..3f86100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.26.5] - 2026-05-xx + +### New Feature** + - **Filter save** - operations now provide visual feedback to users, confirming successful saves with "Saved ✓" messages or displaying error details if an issue occurs. + - Save button shows a disabled state while processing and displays timed feedback that clearly indicates whether the operation succeeded or failed. + ## [0.26.4] - 2026-05-18 ### Changed diff --git a/src/webviewPanel.ts b/src/webviewPanel.ts index 8aed785..b030e78 100644 --- a/src/webviewPanel.ts +++ b/src/webviewPanel.ts @@ -471,12 +471,21 @@ export class EspDecoderWebviewPanel implements vscode.WebviewViewProvider { this.crashCapturer.reset(); break; case 'saveFilters': { - const cfg = vscode.workspace.getConfiguration('esp-decoder'); - await cfg.update('serialFilters.timestamp', message.timestamp, vscode.ConfigurationTarget.Global); - await cfg.update('serialFilters.suppressPattern', message.suppressPattern, vscode.ConfigurationTarget.Global); - await cfg.update('serialFilters.highlightPattern', message.highlightPattern, vscode.ConfigurationTarget.Global); - await cfg.update('serialFilters.dedupPattern', message.dedupPattern, vscode.ConfigurationTarget.Global); - await cfg.update('serialFilters.dedupThreshold', message.dedupThreshold, vscode.ConfigurationTarget.Global); + try { + const cfg = vscode.workspace.getConfiguration('esp-decoder'); + await cfg.update('serialFilters.timestamp', message.timestamp, vscode.ConfigurationTarget.Global); + await cfg.update('serialFilters.suppressPattern', message.suppressPattern, vscode.ConfigurationTarget.Global); + await cfg.update('serialFilters.highlightPattern', message.highlightPattern, vscode.ConfigurationTarget.Global); + await cfg.update('serialFilters.dedupPattern', message.dedupPattern, vscode.ConfigurationTarget.Global); + await cfg.update('serialFilters.dedupThreshold', message.dedupThreshold, vscode.ConfigurationTarget.Global); + this.postMessage({ type: 'filtersSaved', ok: true }); + } catch (err) { + this.postMessage({ + type: 'filtersSaved', + ok: false, + error: err instanceof Error ? err.message : String(err), + }); + } break; } case 'decodeCrash': { @@ -1309,6 +1318,16 @@ export class EspDecoderWebviewPanel implements vscode.WebviewViewProvider { border-color: var(--error-fg); } .filter-toolbar .filter-label { opacity: 0.6; } + button.feedback-saved { + background: var(--vscode-testing-iconPassed, #28a745) !important; + color: var(--vscode-button-foreground); + transition: background 0.2s ease; + } + button.feedback-error { + background: var(--error-fg, #d73a49) !important; + color: var(--vscode-button-foreground); + transition: background 0.2s ease; + } .filter-toolbar button.log-active { background: var(--success-fg); color: #000; @@ -2133,7 +2152,45 @@ export class EspDecoderWebviewPanel implements vscode.WebviewViewProvider { filterState.dedupThreshold = (isNaN(v) || v < 1) ? 3 : v; }); - document.getElementById('filter-save').addEventListener('click', () => { + var filterSaveBtn = document.getElementById('filter-save'); + var filterSaveOriginalLabel = filterSaveBtn.textContent; + var filterSaveFeedbackTimer = null; + var filterSaveTimeout = null; + function showFilterSaveFeedback(ok, errorText) { + if (filterSaveTimeout) { + clearTimeout(filterSaveTimeout); + filterSaveTimeout = null; + } + if (filterSaveFeedbackTimer) { + clearTimeout(filterSaveFeedbackTimer); + filterSaveFeedbackTimer = null; + } + filterSaveBtn.classList.remove('feedback-saved', 'feedback-error'); + if (ok) { + filterSaveBtn.classList.add('feedback-saved'); + filterSaveBtn.textContent = 'Saved \u2713'; + } else { + filterSaveBtn.classList.add('feedback-error'); + filterSaveBtn.textContent = 'Failed'; + if (errorText) { filterSaveBtn.title = 'Save failed: ' + errorText; } + } + filterSaveFeedbackTimer = setTimeout(function() { + filterSaveBtn.classList.remove('feedback-saved', 'feedback-error'); + filterSaveBtn.textContent = filterSaveOriginalLabel; + filterSaveBtn.title = 'Save current filter settings to VS Code settings'; + filterSaveBtn.disabled = false; + filterSaveFeedbackTimer = null; + }, 1500); + } + filterSaveBtn.addEventListener('click', () => { + filterSaveBtn.disabled = true; + if (filterSaveTimeout) { + clearTimeout(filterSaveTimeout); + } + filterSaveTimeout = setTimeout(function() { + filterSaveTimeout = null; + showFilterSaveFeedback(false, 'No response from extension'); + }, 5000); vscode.postMessage({ type: 'saveFilters', timestamp: filterState.timestamp, @@ -2382,6 +2439,9 @@ export class EspDecoderWebviewPanel implements vscode.WebviewViewProvider { case 'error': appendError(msg.message); break; + case 'filtersSaved': + showFilterSaveFeedback(msg.ok !== false, msg.error); + break; } });