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
↓ peach-browser-1ab038.tar.gzatlas at this save
files changed10 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 −12manifest.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": {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",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": {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" &&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);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(() => {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);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;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 }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 }