experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(extension): add success/error feedback to popup command bar

+99 -20
+45 -1
backend/extension/popup.html
··· 78 78 #command-text.placeholder { 79 79 color: rgba(255, 255, 255, 0.3); 80 80 } 81 + 82 + /* Feedback states */ 83 + .feedback { 84 + display: none; 85 + align-items: center; 86 + justify-content: center; 87 + gap: 8px; 88 + width: 100%; 89 + height: 100%; 90 + } 91 + 92 + .feedback.visible { 93 + display: flex; 94 + } 95 + 96 + .feedback.success { 97 + color: #4ade80; 98 + } 99 + 100 + .feedback.error { 101 + color: #f87171; 102 + } 103 + 104 + .feedback-icon { 105 + font-size: 20px; 106 + line-height: 1; 107 + } 108 + 109 + .feedback-text { 110 + font-size: 14px; 111 + font-weight: 500; 112 + max-width: 260px; 113 + overflow: hidden; 114 + text-overflow: ellipsis; 115 + white-space: nowrap; 116 + } 117 + 118 + .command-display.hidden { 119 + display: none; 120 + } 81 121 </style> 82 122 </head> 83 123 <body> 84 124 <div class="center-wrapper"> 85 - <div class="command-display"> 125 + <div class="command-display" id="command-display"> 86 126 <input id="command-input" type="text" autofocus spellcheck="false" /> 87 127 <div id="command-text" class="placeholder">tag, note, or search...</div> 128 + </div> 129 + <div class="feedback" id="feedback"> 130 + <span class="feedback-icon" id="feedback-icon"></span> 131 + <span class="feedback-text" id="feedback-text"></span> 88 132 </div> 89 133 </div> 90 134
+54 -19
backend/extension/popup.js
··· 1 1 /** 2 2 * Peek Browser Extension - Command Bar Popup 3 3 * Simple command bar: tag, note, search 4 - * UI closes immediately - backend saves async 4 + * Shows success/error feedback before closing 5 5 */ 6 6 7 7 const COMMANDS = ['tag', 'note', 'search']; ··· 14 14 15 15 const commandInput = document.getElementById('command-input'); 16 16 const commandText = document.getElementById('command-text'); 17 + const commandDisplay = document.getElementById('command-display'); 18 + const feedbackEl = document.getElementById('feedback'); 19 + const feedbackIcon = document.getElementById('feedback-icon'); 20 + const feedbackText = document.getElementById('feedback-text'); 17 21 18 22 async function init() { 19 23 try { ··· 80 84 } 81 85 } 82 86 87 + function showFeedback(type, message) { 88 + commandDisplay.classList.add('hidden'); 89 + feedbackEl.classList.remove('success', 'error'); 90 + feedbackEl.classList.add('visible', type); 91 + feedbackIcon.textContent = type === 'success' ? '✓' : '✗'; 92 + feedbackText.textContent = message; 93 + } 94 + 83 95 function executeCommand() { 84 96 const parts = state.typed.trim().split(/\s+/); 85 97 const cmd = parts[0]?.toLowerCase(); ··· 87 99 88 100 if (cmd === 'tag' && args) { 89 101 saveTag(args); 90 - window.close(); 91 102 } else if (cmd === 'note' && args) { 92 103 saveNote(args); 93 - window.close(); 94 104 } else if (cmd === 'search' && args) { 95 105 performSearch(args); 96 106 } else if (state.match && !args) { ··· 100 110 } 101 111 } 102 112 103 - function saveTag(tagString) { 113 + async function saveTag(tagString) { 104 114 const tags = tagString.split(',').map(t => t.trim()).filter(t => t); 105 115 if (tags.length === 0) return; 106 116 107 - chrome.runtime.sendMessage({ 108 - type: 'save-tags', 109 - data: { 110 - url: state.currentTab?.url, 111 - title: state.currentTab?.title, 112 - tags: tags 117 + try { 118 + const response = await chrome.runtime.sendMessage({ 119 + type: 'save-tags', 120 + data: { 121 + url: state.currentTab?.url, 122 + title: state.currentTab?.title, 123 + tags: tags 124 + } 125 + }); 126 + 127 + if (response?.success) { 128 + const tagList = tags.length === 1 ? tags[0] : `${tags.length} tags`; 129 + showFeedback('success', `Tagged: ${tagList}`); 130 + setTimeout(() => window.close(), 600); 131 + } else { 132 + showFeedback('error', response?.error || 'Failed to save tags'); 113 133 } 114 - }).catch(err => console.error('Failed to save tags:', err)); 134 + } catch (err) { 135 + console.error('Failed to save tags:', err); 136 + showFeedback('error', 'Failed to save tags'); 137 + } 115 138 } 116 139 117 - function saveNote(noteText) { 118 - chrome.runtime.sendMessage({ 119 - type: 'save-note', 120 - data: { 121 - url: state.currentTab?.url, 122 - title: state.currentTab?.title, 123 - note: noteText 140 + async function saveNote(noteText) { 141 + try { 142 + const response = await chrome.runtime.sendMessage({ 143 + type: 'save-note', 144 + data: { 145 + url: state.currentTab?.url, 146 + title: state.currentTab?.title, 147 + note: noteText 148 + } 149 + }); 150 + 151 + if (response?.success) { 152 + showFeedback('success', 'Note saved'); 153 + setTimeout(() => window.close(), 600); 154 + } else { 155 + showFeedback('error', response?.error || 'Failed to save note'); 124 156 } 125 - }).catch(err => console.error('Failed to save note:', err)); 157 + } catch (err) { 158 + console.error('Failed to save note:', err); 159 + showFeedback('error', 'Failed to save note'); 160 + } 126 161 } 127 162 128 163 function performSearch(query) {