785cca9b28d7
detach popup to chrome.windows for PDF import — survive file dialog focus loss, replace emoji icons with SVGs, lift guidance text contrast (v3.6.70)
- time
- 2026-05-21 01:32 UTC · 5 months ago
- machine
- koh2
- parent
- 0a34bc3ebed3
↓ peach-browser-785cca9b.tar.gzatlas at this save
files changed5 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/styles/codex.css+21 −6src/popup/views/CodexRestoreView.tsx+70 −5manifest.firefox.json+1 −1
@@ -2,7 +2,7 @@22 "_comment_audio_capture": "Firefox does not honor the Chrome 'audioCapture' optional_permission — getUserMedia in WebExtensions is gated solely by the runtime permission prompt. Microphone access for chirp pairing works from the popup under the existing '<all_urls>' host permission; no manifest entry is required or accepted here.",33 "manifest_version": 3,44 "name": "Peach",5- "version": "3.6.69",5+ "version": "3.6.70",66 "description": "Zero-knowledge password manager",77 "browser_specific_settings": {88 "gecko": {manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach",4- "version": "3.6.69",4+ "version": "3.6.70",55 "description": "Zero-knowledge password manager",66 "action": {77 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "3.6.69",3+ "version": "3.6.70",44 "description": "Zero-knowledge password manager",55 "type": "module",66 "scripts": {src/popup/styles/codex.css+21 −6
@@ -393,11 +393,27 @@393393 }394394 395395 .peach-codex-restore-option-icon {396- font-size: 22px;397396 flex-shrink: 0;398- line-height: 1;397+ width: 28px;398+ height: 28px;399+ display: inline-flex;400+ align-items: center;401+ justify-content: center;402+ color: var(--peach-accent);403+ border-radius: 8px;404+ background: color-mix(in srgb, var(--peach-accent) 10%, transparent);405+ border: 1px solid color-mix(in srgb, var(--peach-accent) 22%, transparent);399406 }400407 408+.peach-codex-restore-option-icon svg {409+ display: block;410+}411+412+.peach-codex-restore-option:hover .peach-codex-restore-option-icon {413+ background: color-mix(in srgb, var(--peach-accent) 16%, transparent);414+ border-color: color-mix(in srgb, var(--peach-accent) 36%, transparent);415+}416+401417 .peach-codex-restore-option-content {402418 display: flex;403419 flex-direction: column;@@ -416,11 +432,10 @@416432 }417433 418434 .peach-codex-restore-guidance {419- font-size: 0.6875rem;420- color: var(--peach-t3);435+ font-size: 0.75rem;436+ color: var(--peach-t2);421437 text-align: center;422- margin: 4px 0 0;423- font-family: var(--font-mono, ui-monospace, monospace);438+ margin: 8px 0 0;424439 line-height: 1.5;425440 }426441 src/popup/views/CodexRestoreView.tsx+70 −5
@@ -163,6 +163,39 @@163163 }164164 }165165 166+ // The Chrome action popup dismisses the moment the OS file picker steals167+ // focus, which stranded users mid-import. From the popup we instead open168+ // the restore view in a standalone window (chrome.windows.create) and169+ // let that window own the file dialog. Within the detached window, or in170+ // any non-Chromium context that lacks chrome.windows, we trigger the171+ // input directly.172+ async function openPdfPicker() {173+ if (autoPickPdf) {174+ pdfFileInput?.click();175+ return;176+ }177+ const canDetach =178+ typeof chrome !== "undefined" &&179+ typeof chrome.windows?.create === "function" &&180+ typeof chrome.runtime?.getURL === "function";181+ if (!canDetach) {182+ pdfFileInput?.click();183+ return;184+ }185+ try {186+ await chrome.windows.create({187+ url: chrome.runtime.getURL("popup.html?codex=pdf"),188+ type: "popup",189+ width: 460,190+ height: 720,191+ });192+ window.close();193+ } catch (err) {194+ console.warn("[codex] failed to detach popup for PDF import:", err);195+ pdfFileInput?.click();196+ }197+ }198+166199 async function handlePDFSelected(e: Event) {167200 const input = e.currentTarget as HTMLInputElement;168201 const file = input.files?.[0];@@ -217,9 +250,25 @@217250 }218251 }219252 253+ // Detect whether we are running inside a chrome.windows.create() window254+ // that was opened specifically to handle the PDF picker. The action255+ // popup dismisses on focus loss, so the file dialog tears it down. When256+ // routed via ?codex=pdf we know we are in a survivable window and can257+ // safely auto-trigger the picker on mount.258+ const autoPickPdf = (() => {259+ try {260+ return new URLSearchParams(window.location.search).get("codex") === "pdf";261+ } catch {262+ return false;263+ }264+ })();265+220266 onMount(() => {221- // Camera does not start automatically — the user picks a path on the222- // choose screen first.267+ if (autoPickPdf) {268+ // Tiny delay so the click happens after the SolidJS mount settles269+ // and the hidden <input> is in the DOM.270+ setTimeout(() => pdfFileInput?.click(), 50);271+ }223272 });224273 225274 onCleanup(() => {@@ -342,7 +391,14 @@342391 class="peach-codex-restore-option"343392 onClick={() => void startCameraScan()}344393 >345- <div class="peach-codex-restore-option-icon">📷</div>394+ <div class="peach-codex-restore-option-icon">395+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none"396+ stroke="currentColor" stroke-width="1.5"397+ stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">398+ <path d="M3 8a2 2 0 0 1 2-2h2l1.5-2h7L17 6h2a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8z" />399+ <circle cx="12" cy="13" r="3.5" />400+ </svg>401+ </div>346402 <div class="peach-codex-restore-option-content">347403 <span class="peach-codex-restore-option-title">348404 scan printed pages@@ -355,9 +411,18 @@355411 356412 <button357413 class="peach-codex-restore-option"358- onClick={() => pdfFileInput?.click()}414+ onClick={() => void openPdfPicker()}359415 >360- <div class="peach-codex-restore-option-icon">📄</div>416+ <div class="peach-codex-restore-option-icon">417+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none"418+ stroke="currentColor" stroke-width="1.5"419+ stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">420+ <path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" />421+ <path d="M14 3v5h5" />422+ <path d="M9 14h6" />423+ <path d="M9 17h4" />424+ </svg>425+ </div>361426 <div class="peach-codex-restore-option-content">362427 <span class="peach-codex-restore-option-title">363428 import PDF file