5133483cf65f
feat: settings — license key visible with one-tap copy (v2.12.2)
- time
- 2026-04-29 17:44 UTC · 6 months ago
- machine
- koh2
- parent
- 5e15c7c0eafe
↓ peach-browser-5133483c.tar.gzatlas at this save
files changed4 files
manifest.firefox.json+1 −1manifest.json+1 −1src/popup/styles/popup.css+45 −0src/popup/views/Settings.tsx+32 −1manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach",4- "version": "2.12.1",4+ "version": "2.12.2",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": "2.12.1",4+ "version": "2.12.2",55 "description": "Zero-knowledge password manager",66 "action": {77 "default_popup": "src/popup/index.html",src/popup/styles/popup.css+45 −0
@@ -6678,6 +6678,51 @@66786678 animation: peach-panic-pulse 2s ease-in-out infinite;66796679 }66806680 6681+.peach-settings-license {6682+ margin-top: 10px;6683+ padding-top: 10px;6684+ border-top: 1px solid var(--peach-t3);6685+}6686+6687+.peach-settings-label {6688+ font-size: 10px;6689+ letter-spacing: 0.08em;6690+ color: var(--peach-t3);6691+ display: block;6692+ margin-bottom: 6px;6693+}6694+6695+.peach-settings-license-row {6696+ display: flex;6697+ align-items: center;6698+ justify-content: space-between;6699+ gap: 8px;6700+}6701+6702+.peach-settings-license-key {6703+ font-family: 'IBM Plex Mono', monospace;6704+ font-size: 12px;6705+ color: var(--peach-t0);6706+ letter-spacing: 0.04em;6707+ word-break: break-all;6708+}6709+6710+.peach-settings-license-copy {6711+ font-size: 11px;6712+ color: var(--peach-accent);6713+ background: transparent;6714+ border: none;6715+ cursor: pointer;6716+ padding: 4px 8px;6717+ border-radius: 4px;6718+ flex-shrink: 0;6719+ transition: opacity 150ms ease;6720+}6721+6722+.peach-settings-license-copy:hover {6723+ opacity: 0.75;6724+}6725+66816726 @keyframes peach-panic-pulse {66826727 0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.4); }66836728 50% { box-shadow: 0 0 0 8px rgba(220, 38, 38, 0); }src/popup/views/Settings.tsx+32 −1
@@ -153,12 +153,13 @@153153 const [fbResult, setFbResult] = createSignal<"success" | "error" | null>(null);154154 155155 onMount(async () => {156- const [licenseRes, syncRes, passkeyRes, duressRes, settingsRes] = await Promise.all([156+ const [licenseRes, syncRes, passkeyRes, duressRes, settingsRes, keyRes] = await Promise.all([157157 sendMessage({ type: "GET_LICENSE" }),158158 sendMessage({ type: "GET_SYNC_STATE" }),159159 sendMessage({ type: "GET_PASSKEY_PROVIDER_STATE" }),160160 sendMessage({ type: "IS_DURESS_ENABLED" }),161161 sendMessage({ type: "GET_SETTINGS" }),162+ sendMessage({ type: "GET_LICENSE_KEY" }),162163 ]);163164 164165 if ((licenseRes as { ok: boolean }).ok) {@@ -197,6 +198,10 @@197198 setDeclinedCardCount(sData?.declinedCardFingerprints?.length ?? 0);198199 }199200 201+ if ((keyRes as { ok: boolean }).ok) {202+ setLicenseKey((keyRes as { ok: boolean; data?: string }).data ?? null);203+ }204+200205 const entriesRes = await sendMessage({ type: "GET_ENTRIES" });201206 if ((entriesRes as { ok: boolean }).ok) {202207 const entries = (entriesRes as { ok: boolean; data?: Array<{id: string; title: string; canary?: Canary}> }).data ?? [];@@ -239,6 +244,14 @@239244 setChangePwSaving(false);240245 }241246 247+ const [copied, setCopied] = createSignal(false);248+249+ async function copyLicenseKey(key: string) {250+ await navigator.clipboard.writeText(key);251+ setCopied(true);252+ setTimeout(() => setCopied(false), 2000);253+ }254+242255 async function handleActivateLicense() {243256 const key = licenseKeyInput().trim();244257 if (!key) return;@@ -251,6 +264,7 @@251264 const data = (res as { ok: boolean; data?: LicenseInfo }).data;252265 if (data?.valid) {253266 setLicenseInfo(data);267+ setLicenseKey(key);254268 setLicenseSuccess("License activated!");255269 setLicenseKeyInput("");256270 } else {@@ -1403,6 +1417,8 @@14031417 { feature: "smart_password_change", name: "Smart Password Change", desc: "Guided reset · pre-filled · vault updated" },14041418 ];14051419 1420+ const [licenseKey, setLicenseKey] = createSignal<string | null>(null);1421+14061422 const [showKeyEntry, setShowKeyEntry] = createSignal(false);14071423 const [deactivating, setDeactivating] = createSignal(false);14081424 @@ -1527,6 +1543,21 @@15271543 <h2 class="peach-pro-active-title">Peach Pro</h2>15281544 <p class="peach-pro-active-since">{formatActivatedSince(licenseInfo()?.issued)}</p>15291545 </div>1546+1547+ <Show when={licenseKey()}>1548+ <div class="peach-settings-license">1549+ <span class="peach-settings-label">LICENSE KEY</span>1550+ <div class="peach-settings-license-row">1551+ <span class="peach-settings-license-key">{licenseKey()!}</span>1552+ <button1553+ class="peach-settings-license-copy"1554+ onClick={() => copyLicenseKey(licenseKey()!)}1555+ >1556+ {copied() ? "✓" : "copy"}1557+ </button>1558+ </div>1559+ </div>1560+ </Show>15301561 15311562 <div class="peach-pro-active-features">15321563 <For each={PRO_FEATURES}>