diff --git a/extension/src/popup/components/item-list.ts b/extension/src/popup/components/item-list.ts index f46b7ba..ae15f9d 100644 --- a/extension/src/popup/components/item-list.ts +++ b/extension/src/popup/components/item-list.ts @@ -3,6 +3,7 @@ /// to the detail view. import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../../shared/state'; +import { showToast } from '../../shared/toast'; import { GLYPH_VAULT_TAB, GLYPH_DEVICES, GLYPH_LOCK, @@ -130,11 +131,14 @@ export function renderItemList(app: HTMLElement): void { if (listResp.ok) { const data = listResp.data as { items: Array<[ItemId, ManifestEntry]> }; setState({ entries: data.items, loading: false }); + showToast('Synced', 'success'); return; } setState({ loading: false, error: listResp.error }); + showToast(listResp.error ?? 'Sync failed', 'error'); } else { setState({ loading: false, error: resp.error }); + showToast(resp.error ?? 'Sync failed', 'error'); } }); diff --git a/extension/src/popup/styles.css b/extension/src/popup/styles.css index ad64c1c..ee28b08 100644 --- a/extension/src/popup/styles.css +++ b/extension/src/popup/styles.css @@ -1660,4 +1660,42 @@ textarea { .type-card__icon { font-size: 20px; margin-bottom: 4px; } .type-card__label { font-size: 12px; font-weight: 600; } + +/* Toast notifications */ +.relicario-toast-container { + position: fixed; + bottom: 16px; + left: 50%; + transform: translateX(-50%); + display: flex; + flex-direction: column; + gap: 6px; + pointer-events: none; + z-index: 9999; +} + +.vault-shell .relicario-toast-container { + left: auto; + right: 24px; + transform: none; +} + +.relicario-toast { + padding: 8px 16px; + border-radius: 6px; + font-size: 12px; + opacity: 0; + transform: translateY(8px); + transition: opacity 0.2s, transform 0.2s; + pointer-events: none; +} + +.relicario-toast--visible { + opacity: 1; + transform: translateY(0); +} + +.relicario-toast--success { background: #1f4a24; color: #aff0b5; border: 1px solid #238636; } +.relicario-toast--error { background: #4a1f1f; color: #f0afaf; border: 1px solid #ab2b20; } +.relicario-toast--info { background: #1f2d4a; color: #afc8f0; border: 1px solid #1f6feb; } .type-card__desc { font-size: 10px; color: var(--text-muted, #8b949e); margin-top: 2px; } diff --git a/extension/src/shared/toast.ts b/extension/src/shared/toast.ts new file mode 100644 index 0000000..943ea09 --- /dev/null +++ b/extension/src/shared/toast.ts @@ -0,0 +1,26 @@ +export function showToast( + message: string, + type: 'success' | 'error' | 'info' = 'info', + durationMs = 2500, +): void { + let container = document.querySelector('.relicario-toast-container'); + if (!container) { + container = document.createElement('div'); + container.className = 'relicario-toast-container'; + document.body.appendChild(container); + } + + const toast = document.createElement('div'); + toast.className = `relicario-toast relicario-toast--${type}`; + toast.textContent = message; + container.appendChild(toast); + + requestAnimationFrame(() => { + requestAnimationFrame(() => toast.classList.add('relicario-toast--visible')); + }); + + setTimeout(() => { + toast.classList.remove('relicario-toast--visible'); + toast.addEventListener('transitionend', () => toast.remove(), { once: true }); + }, durationMs); +} diff --git a/extension/src/vault/vault.css b/extension/src/vault/vault.css index c1d1639..270b73b 100644 --- a/extension/src/vault/vault.css +++ b/extension/src/vault/vault.css @@ -1719,3 +1719,41 @@ textarea { background: linear-gradient(to top, rgba(17, 22, 30, 0.7), transparent); pointer-events: none; } + +/* Toast notifications */ +.relicario-toast-container { + position: fixed; + bottom: 16px; + left: 50%; + transform: translateX(-50%); + display: flex; + flex-direction: column; + gap: 6px; + pointer-events: none; + z-index: 9999; +} + +.vault-shell .relicario-toast-container { + left: auto; + right: 24px; + transform: none; +} + +.relicario-toast { + padding: 8px 16px; + border-radius: 6px; + font-size: 12px; + opacity: 0; + transform: translateY(8px); + transition: opacity 0.2s, transform 0.2s; + pointer-events: none; +} + +.relicario-toast--visible { + opacity: 1; + transform: translateY(0); +} + +.relicario-toast--success { background: #1f4a24; color: #aff0b5; border: 1px solid #238636; } +.relicario-toast--error { background: #4a1f1f; color: #f0afaf; border: 1px solid #ab2b20; } +.relicario-toast--info { background: #1f2d4a; color: #afc8f0; border: 1px solid #1f6feb; }