Skip to content

Commit 783eacb

Browse files
committed
refactor: consolidate ad-hoc toast notifications into centralized M.showToast()
- Replace showConversionToast() in file-converters.js with M.showToast('success') - Replace local showToast() in speechToText.js (3 call sites) with M.showToast() - Remove orphaned .conversion-toast CSS + keyframes from modals.css - Remove orphaned .speech-toast CSS + all variants from speech.css
1 parent c4e9c68 commit 783eacb

6 files changed

Lines changed: 41 additions & 103 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ TextAgent has undergone significant evolution since its inception. What started
373373

374374
| Date | Feature / Update |
375375
|------|-----------------|
376+
| **2026-03-09** | 🔔 **Toast Consolidation** — replaced ad-hoc `showConversionToast()` (file-converters) and local `showToast()` (speechToText) with centralized `M.showToast()`; removed orphaned `.conversion-toast` and `.speech-toast` CSS; all notifications now use a single, themed, accessible toast system |
376377
| **2026-03-09** | ✉️ **Email to Self** — share result modal includes "Email to Self" section; enter email → document is sent directly to inbox with share link + `.md` file attached; powered by Google Apps Script (free, 100 emails/day); loading state, success/error feedback; email persisted in localStorage; zero third-party dependencies |
377378
| **2026-03-09** | 🔑 **Centralized Storage Keys** — created `js/storage-keys.js` as single source of truth for all ~20 localStorage key strings; replaced scattered raw string literals across 12 JS files with `M.KEYS.*` constants; eliminates storage-key drift bugs (e.g., `mdview-preview-theme` vs `md-viewer-preview-theme`) |
378379
| **2026-03-09** | 🧩 **File Decomposition** — split 4 largest JS modules (~5,500 lines) into 14 focused files: `ai-assistant.js` → 4 modules (core, chat, actions, image); `ai-docgen.js` → 3 modules (core, generate, ui); `executable-blocks.js` → 4 modules (core bash, math, python, sandbox); `table-tools.js` → 3 modules (core, sort-filter, analytics); internal namespaces (`M._ai`, `M._docgen`, `M._exec`, `M._table`) for cross-module communication; phased dynamic imports in `main.js` |
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Toast Consolidation — Unified App-Native Notifications
2+
3+
- Replaced ad-hoc `showConversionToast()` in `file-converters.js` with centralized `M.showToast('success')`
4+
- Replaced local `showToast()` helper in `speechToText.js` (3 call sites) with centralized `M.showToast()`
5+
- Removed orphaned `.conversion-toast` CSS and `toastIn`/`toastOut` keyframes from `modals.css`
6+
- Removed orphaned `.speech-toast` CSS and all variants (error, info, dark theme) from `speech.css`
7+
8+
---
9+
10+
## Summary
11+
12+
Consolidated two ad-hoc toast notification implementations into the centralized `M.showToast()` system from `js/toast.js`. This eliminates duplicate toast DOM creation, inconsistent styling, and dead CSS — all notifications now flow through a single, themed, accessible toast component.
13+
14+
---
15+
16+
## 1. File Converters — Remove Ad-Hoc Toast
17+
**Files:** `js/file-converters.js`, `css/modals.css`
18+
**What:** Deleted `showConversionToast()` which manually created `.conversion-toast` DOM elements. Replaced the single call site (successful file conversion) with `M.showToast('✓ Converted … to Markdown', 'success')`. Removed the `.conversion-toast` CSS rule and its `toastIn`/`toastOut` keyframes from `modals.css`.
19+
**Impact:** File conversion success feedback now uses the same themed, animated, accessible toast as every other notification in the app.
20+
21+
## 2. Speech-to-Text — Remove Local Toast Helper
22+
**Files:** `js/speechToText.js`, `css/speech.css`
23+
**What:** Deleted the local `showToast()` function (16 lines) which created `.speech-toast` DOM elements. Replaced 3 call sites (mic denied, network error, start failure) with `M.showToast()`. Removed `.speech-toast`, `.speech-toast-show`, `.speech-toast-error`, `.speech-toast-info`, and dark-theme variants from `speech.css`.
24+
**Impact:** Speech error notifications now match the app-wide toast style instead of appearing as a separate, differently-styled notification.
25+
26+
---
27+
28+
## Files Changed (4 total)
29+
30+
| File | Lines Changed | Type |
31+
|------|:---:|------|
32+
| `js/file-converters.js` | +1 −8 | Remove ad-hoc toast, use M.showToast |
33+
| `js/speechToText.js` | +3 −19 | Remove local showToast, use M.showToast |
34+
| `css/modals.css` | −30 | Remove .conversion-toast CSS + keyframes |
35+
| `css/speech.css` | −45 | Remove .speech-toast CSS + variants |

css/modals.css

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -584,36 +584,7 @@
584584
}
585585
}
586586

587-
/* Conversion success toast */
588-
.conversion-toast {
589-
position: fixed;
590-
bottom: 24px;
591-
left: 50%;
592-
transform: translateX(-50%) translateY(20px);
593-
background: #238636;
594-
color: #fff;
595-
padding: 10px 20px;
596-
border-radius: 8px;
597-
font-size: 0.9rem;
598-
z-index: 10001;
599-
opacity: 0;
600-
animation: toastIn 0.3s ease forwards, toastOut 0.3s ease 2.5s forwards;
601-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
602-
}
603-
604-
@keyframes toastIn {
605-
to {
606-
opacity: 1;
607-
transform: translateX(-50%) translateY(0);
608-
}
609-
}
610587

611-
@keyframes toastOut {
612-
to {
613-
opacity: 0;
614-
transform: translateX(-50%) translateY(20px);
615-
}
616-
}
617588

618589
/* ========================================
619590
LLM MEMORY CONVERTER MODAL

css/speech.css

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -194,51 +194,7 @@
194194
color: #a5b4fc;
195195
}
196196

197-
/* --- Toast Notifications --- */
198-
.speech-toast {
199-
position: fixed;
200-
bottom: 24px;
201-
left: 50%;
202-
transform: translateX(-50%) translateY(20px);
203-
padding: 10px 20px;
204-
border-radius: 8px;
205-
font-size: 13px;
206-
font-weight: 500;
207-
z-index: 9999;
208-
opacity: 0;
209-
transition: all 0.3s ease;
210-
pointer-events: none;
211-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
212-
}
213-
214-
.speech-toast-show {
215-
opacity: 1;
216-
transform: translateX(-50%) translateY(0);
217-
}
218197

219-
.speech-toast-error {
220-
background: #fef2f2;
221-
color: #dc2626;
222-
border: 1px solid #fecaca;
223-
}
224-
225-
.speech-toast-info {
226-
background: #eff6ff;
227-
color: #2563eb;
228-
border: 1px solid #bfdbfe;
229-
}
230-
231-
[data-theme="dark"] .speech-toast-error {
232-
background: #2a1515;
233-
color: #f87171;
234-
border-color: #4a2020;
235-
}
236-
237-
[data-theme="dark"] .speech-toast-info {
238-
background: #152040;
239-
color: #60a5fa;
240-
border-color: #1e3a5f;
241-
}
242198

243199
/* --- Voice Commands Cheat Sheet Popup --- */
244200
.speech-cheat-popup {
@@ -406,4 +362,4 @@
406362
width: auto;
407363
max-height: 60vh;
408364
}
409-
}
365+
}

js/file-converters.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,6 @@
2929
conversionOverlay.style.display = 'none';
3030
}
3131

32-
function showConversionToast(message) {
33-
var toast = document.createElement('div');
34-
toast.className = 'conversion-toast';
35-
toast.textContent = message;
36-
document.body.appendChild(toast);
37-
setTimeout(function () { toast.remove(); }, 3000);
38-
}
39-
4032
function getFileExtension(filename) {
4133
return (filename.split('.').pop() || '').toLowerCase();
4234
}
@@ -74,7 +66,7 @@
7466
M.markdownEditor.value = markdown;
7567
M.renderMarkdown();
7668
M.dropzone.style.display = 'none';
77-
showConversionToast('\u2713 Converted ' + file.name + ' to Markdown');
69+
M.showToast('\u2713 Converted ' + file.name + ' to Markdown', 'success');
7870
} catch (err) {
7971
console.error('File conversion failed:', err);
8072
M.showToast('Failed to convert ' + file.name + ': ' + err.message, 'error');

js/speechToText.js

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,12 @@
322322
recognition.onerror = (event) => {
323323
console.warn('🎤 Speech recognition error:', event.error);
324324
if (event.error === 'not-allowed' || event.error === 'service-not-allowed') {
325-
showToast('Microphone access denied. Please allow microphone permissions.', 'error');
325+
M.showToast('Microphone access denied. Please allow microphone permissions.', 'error');
326326
stopListening();
327327
} else if (event.error === 'no-speech') {
328328
// Ignore — just means silence
329329
} else if (event.error === 'network') {
330-
showToast('Speech recognition requires an internet connection (Chrome).', 'error');
330+
M.showToast('Speech recognition requires an internet connection (Chrome).', 'error');
331331
stopListening();
332332
} else if (event.error === 'aborted') {
333333
// Normal stop, ignore
@@ -382,7 +382,7 @@
382382
// Recreate and retry
383383
createRecognition();
384384
try { recognition.start(); } catch (e2) {
385-
showToast('Could not start voice dictation.', 'error');
385+
M.showToast('Could not start voice dictation.', 'error');
386386
}
387387
}
388388
}
@@ -441,23 +441,6 @@
441441
}
442442
}
443443

444-
// ── Toast Helper ──────────────────────────────
445-
function showToast(message, type) {
446-
// Use existing toast if available, otherwise create a simple one
447-
const existing = document.querySelector('.speech-toast');
448-
if (existing) existing.remove();
449-
450-
const toast = document.createElement('div');
451-
toast.className = `speech-toast speech-toast-${type || 'info'}`;
452-
toast.textContent = message;
453-
document.body.appendChild(toast);
454-
requestAnimationFrame(() => toast.classList.add('speech-toast-show'));
455-
setTimeout(() => {
456-
toast.classList.remove('speech-toast-show');
457-
setTimeout(() => toast.remove(), 300);
458-
}, 4000);
459-
}
460-
461444
// ── Event Listeners ───────────────────────────
462445
if (micBtn) micBtn.addEventListener('click', toggleListening);
463446
if (mobileMicBtn) mobileMicBtn.addEventListener('click', () => {

0 commit comments

Comments
 (0)