From ff19faff03674e1e072b9c223f5b5cb7c99e9c23 Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Sun, 12 Apr 2026 12:25:25 -0400 Subject: [PATCH] feat: add settings view with capture toggle and blacklist management Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/src/popup/components/settings.ts | 98 ++++++++++++++++++++++ extension/src/popup/components/unlock.ts | 2 +- extension/src/popup/popup.ts | 6 +- 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 extension/src/popup/components/settings.ts diff --git a/extension/src/popup/components/settings.ts b/extension/src/popup/components/settings.ts new file mode 100644 index 0000000..bb8cd85 --- /dev/null +++ b/extension/src/popup/components/settings.ts @@ -0,0 +1,98 @@ +/// Settings view — capture toggle, prompt style, and blacklist management. + +import { sendMessage, navigate, escapeHtml } from '../popup'; +import type { IdfotoSettings } from '../../shared/types'; + +export async function renderSettings(app: HTMLElement): Promise { + app.innerHTML = '
'; + + // Load settings and blacklist in parallel + const [settingsResp, blacklistResp] = await Promise.all([ + sendMessage({ type: 'get_settings' }), + sendMessage({ type: 'get_blacklist' }), + ]); + + const settings: IdfotoSettings = settingsResp.ok + ? (settingsResp.data as { settings: IdfotoSettings }).settings + : { captureEnabled: false, captureStyle: 'bar' }; + + const blacklist: string[] = blacklistResp.ok + ? (blacklistResp.data as { blacklist: string[] }).blacklist + : []; + + const blacklistHtml = blacklist.length > 0 + ? blacklist.map((h) => ` +
+ ${escapeHtml(h)} + +
+ `).join('') + : '

no blacklisted sites

'; + + app.innerHTML = ` +
+
+ + settings +
+ +
+ +
+ +
+
prompt style
+
+ + +
+
+ +
+
blacklisted sites
+
+ ${blacklistHtml} +
+
+
+ `; + + // Back button + document.getElementById('settings-back')?.addEventListener('click', () => { + navigate('locked'); + }); + + // Capture enabled toggle + document.getElementById('capture-enabled')?.addEventListener('change', async (e) => { + const checked = (e.target as HTMLInputElement).checked; + await sendMessage({ type: 'update_settings', settings: { captureEnabled: checked } }); + }); + + // Style buttons + document.getElementById('style-bar')?.addEventListener('click', async () => { + await sendMessage({ type: 'update_settings', settings: { captureStyle: 'bar' } }); + renderSettings(app); + }); + + document.getElementById('style-toast')?.addEventListener('click', async () => { + await sendMessage({ type: 'update_settings', settings: { captureStyle: 'toast' } }); + renderSettings(app); + }); + + // Blacklist remove buttons + document.querySelectorAll('.idfoto-remove-bl').forEach((btn) => { + btn.addEventListener('click', async () => { + const hostname = (btn as HTMLElement).dataset.hostname; + if (hostname) { + await sendMessage({ type: 'remove_blacklist', hostname }); + renderSettings(app); + } + }); + }); +} diff --git a/extension/src/popup/components/unlock.ts b/extension/src/popup/components/unlock.ts index bff615a..dcc7517 100644 --- a/extension/src/popup/components/unlock.ts +++ b/extension/src/popup/components/unlock.ts @@ -52,5 +52,5 @@ export function renderUnlock(app: HTMLElement): void { } const settingsBtn = document.getElementById('settings-btn'); - settingsBtn?.addEventListener('click', () => navigate('setup')); + settingsBtn?.addEventListener('click', () => navigate('settings')); } diff --git a/extension/src/popup/popup.ts b/extension/src/popup/popup.ts index 5a88aa8..036bca1 100644 --- a/extension/src/popup/popup.ts +++ b/extension/src/popup/popup.ts @@ -10,6 +10,7 @@ import { renderEntryList } from './components/entry-list'; import { renderEntryDetail } from './components/entry-detail'; import { renderEntryForm } from './components/entry-form'; import { renderSetupWizard } from './components/setup-wizard'; +import { renderSettings } from './components/settings'; // --- Escape HTML to prevent XSS --- export function escapeHtml(str: string): string { @@ -20,7 +21,7 @@ export function escapeHtml(str: string): string { // --- State --- -export type View = 'setup' | 'locked' | 'list' | 'detail' | 'add' | 'edit'; +export type View = 'setup' | 'locked' | 'list' | 'detail' | 'add' | 'edit' | 'settings'; export interface PopupState { view: View; @@ -96,6 +97,9 @@ function render(): void { case 'edit': renderEntryForm(app, 'edit'); break; + case 'settings': + renderSettings(app); + break; } }