feat(ext/popup): ⚙ picker → device/vault settings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user