96284c293604
list all six Pro features in the nudge from a catalog shared with Settings (v5.3.2)
- time
- 2026-10-06 13:41 UTC · 2 weeks ago
- machine
- Junes-MacBook-Pro.local
- agent
- claude-opus-5-5
- session
- peach pro nudge screen
- parent
- 5157b6df8f7d
↓ peach-browser-96284c29.tar.gzatlas at this save
files changed8 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/pro-features.ts+15 −0src/popup/styles/popup.css+1 −13src/popup/views/ProNudge.test.tsx+9 −0src/popup/views/ProNudge.tsx+9 −18src/popup/views/Settings.tsx+1 −20manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "5.3.1",4+ "version": "5.3.2",55 "description": "Zero-knowledge password manager",66 "browser_specific_settings": {77 "gecko": {manifest.json+1 −1
@@ -2,7 +2,7 @@22 "manifest_version": 3,33 "minimum_chrome_version": "116",44 "name": "Peach Password Manager",5- "version": "5.3.1",5+ "version": "5.3.2",66 "description": "Zero-knowledge password manager",77 "action": {88 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "5.3.1",3+ "version": "5.3.2",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/popup/pro-features.ts+15 −0
@@ -0,0 +1,15 @@1+import type { ProFeature } from "../shared/types";2+3+type ProFeatureListing = { feature: ProFeature; name: string; desc: string };4+5+export const PRO_FEATURES: ProFeatureListing[] = [6+ { feature: "sync", name: "Peach Sync", desc: "Encrypted · peer-to-peer" },7+ { feature: "codex", name: "Peach Codex", desc: "Restore from paper" },8+ { feature: "passkeys", name: "Passkeys", desc: "Zero-knowledge WebAuthn" },9+ { feature: "advanced_health", name: "Advanced Vault Health", desc: "Reuse · age · exposure" },10+];11+12+export const PRO_SECURITY_FEATURES: ProFeatureListing[] = [13+ { feature: "canary_entry", name: "Canary entries", desc: "Detects vault compromise" },14+ { feature: "duress_vault", name: "Duress vault", desc: "Decoy under coercion" },15+];src/popup/styles/popup.css+1 −13
@@ -8072,23 +8072,11 @@80728072 margin-bottom: 2px;80738073 }80748074 8075-.peach-pro-nudge-list {8075+.peach-pro-nudge-grid {80768076 margin: 8px 0 0;80778077 padding: 0;80788078 list-style: none;8079- border-top: 1px solid var(--peach-border);80808079 }8081-8082-.peach-pro-nudge-item {8083- display: grid;8084- grid-template-columns: 24px 1fr;8085- column-gap: 8px;8086- row-gap: 2px;8087- padding: 7px 0;8088- border-bottom: 1px solid var(--peach-border);8089-}8090-8091-.peach-pro-nudge-item .peach-pro-grid-num { grid-row: span 2; padding-top: 2px; }80928080 80938081 .peach-pro-nudge-actions {80948082 display: flex;src/popup/views/ProNudge.test.tsx+9 −0
@@ -3,6 +3,7 @@33 import { afterEach, describe, expect, it, vi } from "vitest";44 55 import { flushView, renderView } from "./view-test-utils";6+import { PRO_FEATURES, PRO_SECURITY_FEATURES } from "../pro-features";67 78 const snoozeMock = vi.fn(async () => {});89 const silenceMock = vi.fn(async () => {});@@ -63,6 +64,14 @@6364 await flushView();6465 6566 expect(onDismiss).toHaveBeenCalledOnce();67+ });68+69+ it("lists every Pro feature from the shared catalog", () => {70+ const view = renderView(() => <ProNudgeView onSeePro={() => {}} onDismiss={() => {}} />);71+ dispose = view.dispose;72+73+ const names = [...view.container.querySelectorAll(".peach-pro-grid-name")].map((n) => n.textContent);74+ expect(names).toEqual([...PRO_FEATURES, ...PRO_SECURITY_FEATURES].map((f) => f.name));6675 });6776 6877 it("opens Pro without recording a dismissal", () => {src/popup/views/ProNudge.tsx+9 −18
@@ -1,14 +1,8 @@11 import { For } from "solid-js";22 import { PeachMark } from "../components/PeachMark";33 import { silenceProNudge, snoozeProNudge } from "../../shared/pro-nudge";4+import { PRO_FEATURES, PRO_SECURITY_FEATURES } from "../pro-features";45 5-const PRO_HIGHLIGHTS = [6- ["Peach Sync", "Encrypted, peer-to-peer, across every device"],7- ["Peach Codex", "Your whole vault, restorable from paper"],8- ["Passkeys", "Zero-knowledge WebAuthn, built in"],9- ["Duress vault", "A convincing decoy under coercion"],10-] as const;11-126 export function ProNudgeView(props: { onSeePro: () => void; onDismiss: () => void }) {137 async function dismiss(record: () => Promise<void>) {148 await record().catch(() => {@@ -25,22 +19,19 @@2519 <h1 id="pro-nudge-title" class="peach-pro-headline">2620 Peach stays free.<br />Pro keeps it <em>growing.</em>2721 </h1>28- <p class="peach-pro-subtitle">29- No subscription, no account. One purchase supports an independent30- password manager and unlocks the extras, forever.31- </p>22+ <p class="peach-pro-subtitle">No subscription. No account. Yours forever.</p>3223 33- <ol class="peach-pro-nudge-list">34- <For each={PRO_HIGHLIGHTS}>35- {([name, description], index) => (36- <li class="peach-pro-nudge-item">24+ <ul class="peach-pro-grid peach-pro-nudge-grid">25+ <For each={[...PRO_FEATURES, ...PRO_SECURITY_FEATURES]}>26+ {(item, index) => (27+ <li class="peach-pro-grid-item">3728 <span class="peach-pro-grid-num">{String(index() + 1).padStart(2, "0")}</span>38- <span class="peach-pro-grid-name">{name}</span>39- <span class="peach-pro-grid-desc">{description}</span>29+ <span class="peach-pro-grid-name">{item.name}</span>30+ <span class="peach-pro-grid-desc">{item.desc}</span>4031 </li>4132 )}4233 </For>43- </ol>34+ </ul>4435 4536 <div class="peach-pro-nudge-actions">4637 <button type="button" class="peach-pro-upgrade-cta" onClick={props.onSeePro}>src/popup/views/Settings.tsx+1 −20
@@ -33,6 +33,7 @@3333 type ShortcutAction,3434 } from "../../shared/shortcuts";3535 import { WEB3FORMS_KEY } from "../../shared/config";36+import { PRO_FEATURES, PRO_SECURITY_FEATURES } from "../pro-features";3637 3738 interface LicenseInfo {3839 valid: boolean;@@ -2268,26 +2269,6 @@22682269 );22692270 }22702271 }2271-2272- const PRO_FEATURES: Array<{2273- feature: ProFeature;2274- name: string;2275- desc: string;2276- }> = [2277- { feature: "sync", name: "Peach Sync", desc: "Encrypted · peer-to-peer" },2278- { feature: "codex", name: "Peach Codex", desc: "Restore from paper" },2279- { feature: "passkeys", name: "Passkeys", desc: "Zero-knowledge WebAuthn" },2280- { feature: "advanced_health", name: "Advanced Vault Health", desc: "Reuse · age · exposure" },2281- ];2282-2283- const PRO_SECURITY_FEATURES: Array<{2284- feature: ProFeature;2285- name: string;2286- desc: string;2287- }> = [2288- { feature: "canary_entry", name: "Canary entries", desc: "Detects vault compromise" },2289- { feature: "duress_vault", name: "Duress vault", desc: "Decoy under coercion" },2290- ];22912272 22922273 const [licenseKey, setLicenseKey] = createSignal<string | null>(null);22932274 const [showKey, setShowKey] = createSignal(false);