1ab038

fix: inline popup new entry save — vault status check and response handling (v1.7.3)

time
2026-04-24 02:12 UTC · 6 months ago
machine
koh2
parent
b11107
10 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/messages.ts+3 −0src/content/inject.ts+27 −5src/popup/App.tsx+7 −1src/popup/components/GeneratorDrawer.tsx+1 −1src/popup/styles/popup.css+7 −1src/popup/styles/vars.css+4 −1src/shared/types.ts+14 −12
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "1.7.2",4+  "version": "1.7.3",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": "1.7.2",4+  "version": "1.7.3",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "1.7.2",3+  "version": "1.7.3",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/background/messages.ts+3 −0
@@ -92,6 +92,8 @@9292         return { ok: true };9393       case "IS_UNLOCKED":9494         return { ok: true, data: vault.isUnlocked() };95+      case "GET_VAULT_STATUS":96+        return { ok: true, unlocked: vault.isUnlocked() };9597       case "HAS_VAULT":9698         return { ok: true, data: await vault.hasVault() };9799       case "GET_ENTRIES":@@ -485,6 +487,7 @@485487   return (486488     type !== "HAS_VAULT" &&487489     type !== "IS_UNLOCKED" &&490+    type !== "GET_VAULT_STATUS" &&488491     type !== "LOCK" &&489492     type !== "UPDATE_SETTINGS" &&490493     type !== "GET_PENDING_PASSKEY" &&

view file

src/content/inject.ts+27 −5
@@ -694,10 +694,19 @@694694     if (idx >= 0) (all[idx] as HTMLElement).scrollIntoView({ block: "nearest" });695695   }696696 697-  function showCreateForm() {697+  async function showCreateForm() {698698     currentMode = "create";699699     container.innerHTML = "";700700 701+    // Guard: the service worker may lock the vault between when the selector702+    // opens and when the user clicks "New entry". Show the inline unlock form703+    // instead of silently failing on save.704+    const statusRes = await chrome.runtime.sendMessage({ type: "GET_VAULT_STATUS" }).catch(() => null) as { unlocked?: boolean } | null;705+    if (!statusRes?.unlocked) {706+      renderLocked();707+      return;708+    }709+701710     const wrap = document.createElement("div");702711     wrap.className = "create-form-wrap";703712 @@ -755,15 +764,23 @@755764     // Save756765     const saveBtn = document.createElement("button"); saveBtn.className = "save-btn"; saveBtn.type = "button";757766     saveBtn.textContent = "Save entry";767+    const errEl = document.createElement("div"); errEl.className = "locked-error"; errEl.style.display = "none";768+    function showFormError(msg: string) { errEl.textContent = msg; errEl.style.display = ""; }758769     saveBtn.addEventListener("click", async () => {759770       const title = tg.inp.value.trim();760771       const pw = pwInp.value.trim();761772       const username = xg.inp.value.trim();762-      if (!title || !pw) {763-        if (!title) { tg.inp.style.borderColor = "rgba(220,70,60,0.5)"; tg.inp.focus(); }764-        else { pwInp.style.borderColor = "rgba(220,70,60,0.5)"; pwInp.focus(); }773+      if (!title) {774+        tg.inp.style.borderColor = "rgba(220,70,60,0.5)"; tg.inp.focus();775+        showFormError("Entry needs a title");765776         return;766777       }778+      if (!pw) {779+        pwInp.style.borderColor = "rgba(220,70,60,0.5)"; pwInp.focus();780+        showFormError("Entry needs a password");781+        return;782+      }783+      errEl.style.display = "none";767784       saveBtn.disabled = true; saveBtn.textContent = "Saving…";768785       try {769786         const res = await chrome.runtime.sendMessage({@@ -777,11 +794,16 @@777794           }778795           showSuccess(title);779796         } else {797+          const errMsg = (res as { error?: string }).error === "vault locked"798+            ? "Vault locked — open Peach to save"799+            : "Could not save entry";800+          showFormError(errMsg);780801           saveBtn.disabled = false; saveBtn.textContent = "Save entry";781802         }782-      } catch { saveBtn.disabled = false; saveBtn.textContent = "Save entry"; }803+      } catch { showFormError("Could not reach Peach"); saveBtn.disabled = false; saveBtn.textContent = "Save entry"; }783804     });784805     body.appendChild(saveBtn);806+    body.appendChild(errEl);785807 786808     wrap.append(head, body);787809     container.appendChild(wrap);

view file

src/popup/App.tsx+7 −1
@@ -177,7 +177,13 @@177177 178178   function applyAccent(name: AccentColorName) {179179     const color = ACCENT_COLORS.find(c => c.name === name) ?? ACCENT_COLORS[0]!;180-    document.documentElement.style.setProperty("--peach-accent", color.hex);180+    const hex = color.hex;181+    document.documentElement.style.setProperty("--peach-accent", hex);182+    // Ambient surface variables — derived from the accent so surfaces shift183+    // subtly toward the accent hue rather than staying fixed warm-brown.184+    document.documentElement.style.setProperty("--peach-glow", hex + "15");185+    document.documentElement.style.setProperty("--peach-surface-tint", hex + "08");186+    document.documentElement.style.setProperty("--peach-border-accent", hex + "20");181187   }182188 183189   createEffect(() => {

view file

src/popup/components/GeneratorDrawer.tsx+1 −1
@@ -170,7 +170,7 @@170170                 type="range"171171                 class="peach-pwgen-slider peach-pwgen-compact-slider"172172                 min="8"173-                max="64"173+                max="128"174174                 value={length()}175175                 onInput={(e) => {176176                   const n = parseInt((e.target as HTMLInputElement).value, 10);

view file

src/popup/styles/popup.css+7 −1
@@ -780,7 +780,7 @@780780 }781781 .peach-btn--sm { min-height: 32px; padding: 6px 9px; font-size: 11px; }782782 .peach-btn--md { min-height: 38px; }783-.peach-btn--accent { background: var(--peach-accent); color: rgba(0,0,0,0.75); }783+.peach-btn--accent { background: var(--peach-accent); color: rgba(0,0,0,0.75); box-shadow: 0 2px 16px var(--peach-glow); }784784 .peach-btn--accent:hover:not(:disabled) { background: var(--peach-accent); filter: brightness(1.12); }785785 .peach-btn--ghost { border-color: var(--peach-t3); color: var(--peach-t1); }786786 .peach-btn--subtle { color: var(--peach-t2); border-color: var(--peach-t3); }@@ -1291,6 +1291,11 @@12911291   box-shadow: inset 2px 0 0 var(--peach-accent);12921292   background: rgba(240,232,216,0.03);12931293 }1294+.peach-entry-row:focus-visible {1295+  outline: none;1296+  box-shadow: 0 0 0 1px var(--peach-border-accent);1297+  background: rgba(240,232,216,0.03);1298+}12941299 .peach-entry-row-icon {12951300   width: 26px; height: 26px;12961301   border-radius: 7px;@@ -3127,6 +3132,7 @@31273132   background:31283133     linear-gradient(180deg, rgba(240, 232, 216, 0.03) 0%, rgba(240, 232, 216, 0.015) 100%);31293134   box-shadow:3135+    inset 0 0 0 1px var(--peach-border-accent),31303136     inset 0 1px 0 rgba(240, 232, 216, 0.04),31313137     0 12px 32px rgba(0, 0, 0, 0.14);31323138   overflow: hidden;

view file

src/popup/styles/vars.css+4 −1
@@ -113,6 +113,9 @@113113   --peach-t3: var(--t3);114114   --peach-accent: var(--ac);115115   --peach-accent-dim: color-mix(in srgb, var(--peach-accent) 25%, transparent);116+  --peach-glow: #E8956D15;        /* accent bloom behind buttons/illustrations — set by applyAccent() */117+  --peach-surface-tint: #E8956D08; /* accent tint on card surfaces — set by applyAccent() */118+  --peach-border-accent: #E8956D20; /* accent-tinted border for active states — set by applyAccent() */116119   --peach-breach: var(--breach);117120   --peach-health-ok: var(--health);118121   --peach-totp-tag: var(--totp-tag);@@ -150,6 +153,6 @@150153   --shell-top-solid: rgba(210, 196, 178, 0.96);151154   --shell-top-fade: rgba(198, 180, 160, 0.96);152155   --shell-accent-a: color-mix(in srgb, var(--peach-accent) 12%, transparent);153-  --shell-accent-b: rgba(123, 144, 96, 0.10);156+  --shell-accent-b: color-mix(in srgb, var(--peach-accent) 6%, transparent);154157   --copy-shadow: 0 2px 8px rgba(80, 56, 32, 0.22), 0 14px 48px rgba(80, 56, 32, 0.20), 0 0 0 1px rgba(28,25,21,0.06);155158 }

view file

src/shared/types.ts+14 −12
@@ -189,6 +189,7 @@189189   | "RECORD_MANUAL_SUMMON"190190   | "DECLINE_CARD_SAVE"191191   | "IS_CARD_DECLINED"192+  | "GET_VAULT_STATUS"192193   // Content-script-bound message types — sent BY the background TO content193194   // scripts. They never reach messages.ts's switch. Kept in this union so194195   // existing call sites type-check; do not add cases for them in the@@ -199,18 +200,18 @@199200   | "VAULT_UNLOCKED"; // background → content/popup: vault just unlocked200201 201202 export const ACCENT_COLORS = [202-  { name: "peach",    hex: "#E8956D" },203-  { name: "rose",     hex: "#E8909A" },204-  { name: "plum",     hex: "#C49AC8" },205-  { name: "lavender", hex: "#9FA8D4" },206-  { name: "sky",      hex: "#85BAD4" },207-  { name: "mint",     hex: "#7EC8A4" },208-  { name: "sage",     hex: "#9AB87A" },209-  { name: "butter",   hex: "#D4B96A" },210-  { name: "sand",     hex: "#C8A882" },211-  { name: "clay",     hex: "#C4826A" },212-  { name: "slate",    hex: "#8A9EAA" },213-  { name: "stone",    hex: "#A89E92" },203+  { name: "peach",    hex: "#E8956D" },  // brand — unchanged204+  { name: "rose",     hex: "#E08888" },  // slightly warmer pink205+  { name: "plum",     hex: "#B896C4" },  // slightly warmer purple206+  { name: "lavender", hex: "#9698C8" },  // warmer blue-purple (~10° toward purple)207+  { name: "sky",      hex: "#7AAEC4" },  // desaturated and warmed (~15% sat reduction)208+  { name: "mint",     hex: "#72B898" },  // slightly warmer green209+  { name: "sage",     hex: "#8EAA6E" },210+  { name: "butter",   hex: "#CCB060" },211+  { name: "sand",     hex: "#C4A278" },212+  { name: "clay",     hex: "#C07862" },213+  { name: "slate",    hex: "#8496A4" },214+  { name: "stone",    hex: "#A09488" },214215 ] as const;215216 216217 export type AccentColorName = typeof ACCENT_COLORS[number]["name"];@@ -235,4 +236,5 @@235236   ok: boolean;236237   data?: unknown;237238   error?: string;239+  unlocked?: boolean;238240 }

view file