8fa1082f4aae

fix Mayan decode via createImageBitmap; reattach toolbar popup after successful import in detached window

time
2026-05-01 13:40 UTC · 5 months ago
machine
koh2
parent
5cf135768dc8
3 files manifest.firefox.json+1 −1manifest.json+1 −1src/popup/views/Lock.tsx+25 −9
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.20.0",4+  "version": "2.21.0",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.20.0",4+  "version": "2.21.0",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

src/popup/views/Lock.tsx+25 −9
@@ -424,12 +424,22 @@424424   const [showIntro, setShowIntro] = createSignal(false);425425   const [pendingUnlock, setPendingUnlock] = createSignal(false);426426 427+  const isDetachedWindow = new URLSearchParams(location.search).has("detached");428+429+  async function reattachPopup() {430+    if (!isDetachedWindow) return;431+    // Reopen the normal toolbar popup then discard the detached window.432+    try { await (chrome.action as { openPopup?: () => Promise<void> }).openPopup?.(); } catch {}433+    window.close();434+  }435+427436   async function handleRestoreSuccess() {428437     const seen = await storageGet("peach_shown_restore_intro").catch(() => null);429438     if (!seen) {430439       setShowIntro(true);431440     } else {432441       await props.onUnlock();442+      await reattachPopup();433443     }434444   }435445 @@ -437,6 +447,7 @@437447     await storageSet("peach_shown_restore_intro", "true").catch(() => {});438448     setShowIntro(false);439449     await props.onUnlock();450+    await reattachPopup();440451   }441452 442453   return (@@ -834,15 +845,20 @@834845     setEntryLine(null);835846     setError(null);836847     try {837-      const reader = new FileReader();838-      const base64 = await new Promise<string>((res, rej) => {839-        reader.onload = () => res(reader.result as string);840-        reader.onerror = () => rej(new Error("read error"));841-        reader.readAsDataURL(file);842-      });843-      const result = await handleMayanDecode(base64);844-      if (!result.ok) { setError(result.reason); return; }845-      setDecoded(result.payload);848+      const { decodeMayan } = await import("../../crypto/mayan");849+      const bitmap = await createImageBitmap(file);850+      const MAX_DIM = 1500;851+      const scale = Math.min(1, MAX_DIM / Math.max(bitmap.width, bitmap.height));852+      const canvas = document.createElement("canvas");853+      canvas.width = Math.round(bitmap.width * scale);854+      canvas.height = Math.round(bitmap.height * scale);855+      const ctx = canvas.getContext("2d")!;856+      ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);857+      bitmap.close();858+      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);859+      const payload = decodeMayan(imageData);860+      if (!payload) { setError("Could not read this Mayan code. Try a clearer image."); return; }861+      setDecoded(payload);846862     } catch {847863       setError("Could not read this Mayan code. Try a clearer image.");848864     } finally {

view file