diff --git a/extension/src/popup/components/item-list.ts b/extension/src/popup/components/item-list.ts index f532401..9b7c885 100644 --- a/extension/src/popup/components/item-list.ts +++ b/extension/src/popup/components/item-list.ts @@ -93,7 +93,10 @@ export function renderItemList(app: HTMLElement): void { navigate('locked'); }); - document.getElementById('settings-btn')?.addEventListener('click', () => navigate('settings')); + document.getElementById('settings-btn')?.addEventListener('click', (e) => { + e.stopPropagation(); + showSettingsPicker(e.currentTarget as HTMLElement); + }); // Item row clicks. const rows = app.querySelectorAll('.entry-row'); @@ -307,3 +310,80 @@ function showNewTypePicker(anchor: HTMLElement): void { document.addEventListener('keydown', closeOnEsc); }, 0); } + +// ---------------------------------------------------------------------- +// Settings picker popover (device vs vault) +// ---------------------------------------------------------------------- + +const SETTINGS_OPTIONS: Array<{ view: 'settings' | 'settings-vault'; icon: string; label: string }> = [ + { view: 'settings', icon: '🖥', label: 'device settings' }, + { view: 'settings-vault', icon: '🔐', label: 'vault settings' }, +]; + +function showSettingsPicker(anchor: HTMLElement): void { + document.querySelectorAll('.settings-picker').forEach((el) => el.remove()); + + const picker = document.createElement('div'); + picker.className = 'settings-picker'; + Object.assign(picker.style, { + position: 'absolute', + background: '#161b22', + border: '1px solid #30363d', + borderRadius: '6px', + boxShadow: '0 4px 12px rgba(0,0,0,0.4)', + padding: '4px', + minWidth: '170px', + zIndex: '999999', + fontSize: '12px', + }); + + const rect = anchor.getBoundingClientRect(); + picker.style.top = `${rect.bottom + 4}px`; + picker.style.left = `${rect.left}px`; + + for (const opt of SETTINGS_OPTIONS) { + const row = document.createElement('div'); + Object.assign(row.style, { + padding: '6px 10px', cursor: 'pointer', color: '#c9d1d9', + borderRadius: '4px', display: 'flex', alignItems: 'center', gap: '8px', + }); + const iconSpan = document.createElement('span'); + iconSpan.textContent = opt.icon; + Object.assign(iconSpan.style, { fontSize: '14px', width: '16px', textAlign: 'center' }); + const labelSpan = document.createElement('span'); + labelSpan.textContent = opt.label; + row.appendChild(iconSpan); + row.appendChild(labelSpan); + row.addEventListener('mouseenter', () => { row.style.background = '#21262d'; }); + row.addEventListener('mouseleave', () => { row.style.background = 'transparent'; }); + row.addEventListener('click', (ev) => { + ev.stopPropagation(); + picker.remove(); + document.removeEventListener('click', closeOnOutside); + document.removeEventListener('keydown', closeOnEsc); + navigate(opt.view); + }); + picker.appendChild(row); + } + + document.body.appendChild(picker); + + const closeOnOutside = (ev: MouseEvent) => { + if (!picker.contains(ev.target as Node)) { + picker.remove(); + document.removeEventListener('click', closeOnOutside); + document.removeEventListener('keydown', closeOnEsc); + } + }; + const closeOnEsc = (ev: KeyboardEvent) => { + if (ev.key === 'Escape') { + picker.remove(); + document.removeEventListener('click', closeOnOutside); + document.removeEventListener('keydown', closeOnEsc); + } + }; + setTimeout(() => { + document.addEventListener('click', closeOnOutside); + document.addEventListener('keydown', closeOnEsc); + }, 0); +} diff --git a/extension/src/popup/popup.ts b/extension/src/popup/popup.ts index 123731f..2111685 100644 --- a/extension/src/popup/popup.ts +++ b/extension/src/popup/popup.ts @@ -10,6 +10,7 @@ import { renderItemList } from './components/item-list'; import { renderItemDetail } from './components/item-detail'; import { renderItemForm } from './components/item-form'; import { renderSettings } from './components/settings'; +import { renderVaultSettings } from './components/settings-vault'; // --- Escape HTML to prevent XSS --- export function escapeHtml(str: string): string { @@ -146,6 +147,9 @@ function render(): void { case 'settings': renderSettings(app); break; + case 'settings-vault': + renderVaultSettings(app); + break; } }