diff --git a/extension/src/popup/components/setup-wizard.ts b/extension/src/popup/components/setup-wizard.ts index c571477..9d1f887 100644 --- a/extension/src/popup/components/setup-wizard.ts +++ b/extension/src/popup/components/setup-wizard.ts @@ -10,6 +10,17 @@ let wizardConfig: Partial = { let wizardImageBase64: string | null = null; export function renderSetupWizard(app: HTMLElement): void { + // Check if image is already in storage (e.g. pushed by init wizard). + if (!wizardImageBase64) { + chrome.storage.local.get(['imageBase64'], (result: Record) => { + if (result.imageBase64) { + wizardImageBase64 = result.imageBase64; + // Re-render now that we have the image. + renderSetupWizard(app); + } + }); + } + const state = getState(); // Progress bar. @@ -120,66 +131,46 @@ function attachStep0Listeners(): void { }); } -// --- Step 1: Reference image upload --- +// --- Step 1: Reference image --- +// +// Chrome closes the popup when a file picker steals focus, which makes +// in-popup file uploads unreliable. Instead we check if an image is +// already in chrome.storage.local (pushed by the init wizard or a +// previous setup). If not, we link the user to the full-page setup.html +// where the file picker works without focus issues. function renderStep1(): string { + if (wizardImageBase64) { + // Image already loaded (from storage or previous attempt). + return ` +
+

reference image

+

✓ reference image loaded

+
+ + +
+
+ `; + } + return `

reference image

- Upload the JPEG that contains your embedded secret. - This is the second factor for vault decryption. + No reference image found. Use the full-page setup wizard to + upload your reference image — file pickers don't work reliably + in the popup.

-
- - ${wizardImageBase64 - ? '

image loaded

' - : '

click to select JPEG

'} -
- +
`; } function attachStep1Listeners(): void { - const fileDrop = document.getElementById('file-drop')!; - const fileInput = document.getElementById('file-input') as HTMLInputElement; - - fileDrop.addEventListener('click', () => fileInput.click()); - - fileInput.addEventListener('change', () => { - const file = fileInput.files?.[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = () => { - try { - const result = reader.result as string; - // Remove the data:image/jpeg;base64, prefix. - const base64 = result.split(',')[1] ?? result; - wizardImageBase64 = base64; - // Update UI without a full re-render to avoid resetting file input state. - const dropEl = document.getElementById('file-drop'); - if (dropEl) { - dropEl.classList.add('has-file'); - const p = dropEl.querySelector('p'); - if (p) p.textContent = `image loaded (${(file.size / 1024).toFixed(0)} KB)`; - } - const nextBtn = document.getElementById('next-btn') as HTMLButtonElement; - if (nextBtn) nextBtn.disabled = false; - } catch (err) { - setState({ error: `Failed to read image: ${err}` }); - } - }; - reader.onerror = () => { - setState({ error: 'Failed to read image file' }); - }; - reader.readAsDataURL(file); - }); - document.getElementById('back-btn')?.addEventListener('click', () => { wizardStep = 0; setState({ error: null }); @@ -190,6 +181,12 @@ function attachStep1Listeners(): void { wizardStep = 2; setState({ error: null }); }); + + document.getElementById('open-setup-btn')?.addEventListener('click', () => { + // Open the full-page setup wizard in a new tab. + chrome.tabs.create({ url: chrome.runtime.getURL('setup.html') }); + window.close(); + }); } // --- Step 2: Test unlock ---