3e34a4a87aa1

add browser + transports fields to PasskeyEntry, export detectBrowser, tag chrome creates

time
2026-04-28 18:25 UTC · 6 months ago
machine
koh2
parent
6b53ab782017
4 files src/background/passkey.ts+13 −0src/popup/styles/popup.css+232 −0src/popup/views/Settings.tsx+13 −0src/shared/types.ts+8 −0
src/background/passkey.ts+13 −0
@@ -36,6 +36,17 @@3636   return typeof chrome.webAuthenticationProxy?.attach === "function";3737 }3838 39+export function detectBrowser(): "chrome" | "firefox" | "unknown" {40+  if (typeof chrome !== "undefined" && chrome.webAuthenticationProxy) {41+    return "chrome";42+  }43+  try {44+    const url = chrome.runtime?.getURL?.("") ?? "";45+    if (url.startsWith("moz-extension://")) return "firefox";46+  } catch { /* extension context unavailable */ }47+  return "unknown";48+}49+3950 function getBrowserLabel(): string {4051   const ua = navigator.userAgent;4152   if (/Firefox\/(\d+)/.test(ua)) {@@ -305,6 +316,8 @@305316     aaguid: base64urlEncode(new Uint8Array(16)),306317     backupEligible: true,307318     backupState: false,319+    browser: "chrome",320+    transports: ["internal"],308321     createdAt: Date.now(),309322     modifiedAt: Date.now(),310323   };

view file

src/popup/styles/popup.css+232 −0
@@ -6049,3 +6049,235 @@60496049       transparent 60%60506050     );60516051 }6052+6053+/* ── Danger zone (Settings home → Panic flow) ──────────────────────────── */6054+.peach-danger-zone {6055+  border-top: 1px solid rgba(220, 38, 38, 0.3);6056+  margin: 24px 16px 0;6057+  padding-top: 20px;6058+}6059+6060+.peach-danger-zone-label {6061+  margin: 0 0 12px;6062+  font-family: "IBM Plex Mono", monospace;6063+  font-size: 10px;6064+  letter-spacing: 0.1em;6065+  color: rgba(220, 38, 38, 0.7);6066+}6067+6068+.peach-destroy-button {6069+  width: 100%;6070+  padding: 10px;6071+  background: transparent;6072+  border: 1px solid rgba(220, 38, 38, 0.4);6073+  border-radius: 8px;6074+  color: rgba(220, 38, 38, 0.7);6075+  font-size: 13px;6076+  cursor: pointer;6077+  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;6078+}6079+6080+.peach-destroy-button:hover {6081+  background: rgba(220, 38, 38, 0.08);6082+  border-color: rgba(220, 38, 38, 0.7);6083+  color: rgb(220, 38, 38);6084+}6085+6086+/* ── Panic flow (three-step destruction) ───────────────────────────────── */6087+.peach-panic {6088+  display: flex;6089+  flex-direction: column;6090+  height: 100%;6091+  padding: 28px 20px 20px;6092+  gap: 18px;6093+  overflow-y: auto;6094+}6095+6096+.peach-panic-step2 {6097+  background: rgba(220, 38, 38, 0.04);6098+}6099+6100+.peach-panic-step3 {6101+  background: #0f0505;6102+  color: #ffffff;6103+}6104+6105+.peach-panic-icon {6106+  font-size: 32px;6107+  color: rgb(220, 38, 38);6108+  line-height: 1;6109+}6110+6111+.peach-panic-title {6112+  margin: 0;6113+  font-size: 18px;6114+  font-weight: 500;6115+  color: var(--peach-t1);6116+}6117+6118+.peach-panic-title--severe {6119+  font-size: 20px;6120+  font-weight: 700;6121+  color: rgb(220, 38, 38);6122+}6123+6124+.peach-panic-body {6125+  margin: 0;6126+  font-size: 13px;6127+  line-height: 1.55;6128+  color: var(--peach-t2);6129+}6130+6131+.peach-panic-body--severe {6132+  color: rgba(220, 38, 38, 0.85);6133+}6134+6135+.peach-panic-body--final {6136+  font-size: 16px;6137+  font-weight: 700;6138+  color: rgb(255, 80, 80);6139+  line-height: 1.4;6140+}6141+6142+.peach-panic-body--final-intro {6143+  font-size: 16px;6144+  color: #ffffff;6145+  font-weight: 400;6146+}6147+6148+.peach-panic-divider {6149+  height: 1px;6150+  background: rgba(255, 255, 255, 0.08);6151+  margin: 4px 0;6152+}6153+6154+.peach-panic-confirm-input {6155+  width: 100%;6156+  padding: 10px 12px;6157+  background: rgba(255, 255, 255, 0.02);6158+  border: 1px solid rgba(220, 38, 38, 0.4);6159+  border-radius: 8px;6160+  color: var(--peach-t1);6161+  font-family: "IBM Plex Mono", monospace;6162+  font-size: 14px;6163+  letter-spacing: 0.12em;6164+  text-transform: uppercase;6165+}6166+6167+.peach-panic-confirm-input:focus {6168+  outline: none;6169+  border-color: rgb(220, 38, 38);6170+}6171+6172+.peach-panic-confirm-input--match {6173+  border-color: rgb(220, 38, 38);6174+  color: rgb(220, 38, 38);6175+}6176+6177+.peach-panic-confirm-label {6178+  font-size: 11px;6179+  color: var(--peach-t2);6180+  margin: 0 0 -8px;6181+  letter-spacing: 0.06em;6182+}6183+6184+.peach-panic-actions {6185+  display: flex;6186+  flex-direction: column;6187+  gap: 10px;6188+  margin-top: auto;6189+}6190+6191+.peach-panic-button {6192+  width: 100%;6193+  padding: 12px;6194+  border-radius: 8px;6195+  font-size: 13px;6196+  cursor: pointer;6197+  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;6198+}6199+6200+.peach-panic-button--primary {6201+  background: var(--peach-accent);6202+  border: 1px solid var(--peach-accent);6203+  color: #ffffff;6204+  font-weight: 600;6205+}6206+6207+.peach-panic-button--primary:hover {6208+  background: color-mix(in srgb, var(--peach-accent) 88%, white);6209+}6210+6211+.peach-panic-button--danger {6212+  background: rgb(220, 38, 38);6213+  border: 1px solid rgb(220, 38, 38);6214+  color: #ffffff;6215+  font-weight: 600;6216+}6217+6218+.peach-panic-button--danger:disabled {6219+  opacity: 0.35;6220+  cursor: not-allowed;6221+}6222+6223+.peach-panic-button--ghost {6224+  background: transparent;6225+  border: 1px solid rgba(255, 255, 255, 0.18);6226+  color: var(--peach-t2);6227+}6228+6229+.peach-panic-button--ghost:hover {6230+  border-color: rgba(255, 255, 255, 0.32);6231+  color: var(--peach-t1);6232+}6233+6234+.peach-panic-link {6235+  background: transparent;6236+  border: none;6237+  padding: 4px 0;6238+  color: rgba(220, 38, 38, 0.65);6239+  font-size: 12px;6240+  cursor: pointer;6241+  text-align: left;6242+  text-decoration: underline;6243+  text-underline-offset: 3px;6244+}6245+6246+.peach-panic-link:hover {6247+  color: rgb(220, 38, 38);6248+}6249+6250+.peach-panic-bar {6251+  background: rgb(220, 38, 38);6252+  padding: 16px 20px;6253+  margin: -28px -20px 0;6254+  text-align: center;6255+  font-size: 13px;6256+  font-weight: 700;6257+  letter-spacing: 0.12em;6258+  color: #ffffff;6259+}6260+6261+.peach-panic-final-button {6262+  width: 100%;6263+  padding: 14px;6264+  background: rgb(220, 38, 38);6265+  border: none;6266+  border-radius: 8px;6267+  color: #ffffff;6268+  font-size: 14px;6269+  font-weight: 700;6270+  letter-spacing: 0.08em;6271+  text-transform: uppercase;6272+  cursor: pointer;6273+  animation: peach-panic-pulse 2s ease-in-out infinite;6274+}6275+6276+@keyframes peach-panic-pulse {6277+  0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.4); }6278+  50%      { box-shadow: 0 0 0 8px rgba(220, 38, 38, 0); }6279+}6280+6281+[data-motion="reduce"] .peach-panic-final-button {6282+  animation: none;6283+}

view file

src/popup/views/Settings.tsx+13 −0
@@ -55,6 +55,7 @@5555   onOpenHealth?: () => void;5656   onOpenSyncPairing?: (mode: "initiate" | "accept") => void;5757   onOpenFileImport?: () => void;58+  onOpenPanic?: () => void;5859 }5960 6061 type SectionName =@@ -564,6 +565,18 @@564565 565566         <p class="peach-settings-nav-label">SUPPORT</p>566567         {renderCatRow({ section: "feedback", name: "Feedback", desc: "Report issues, suggest features", icon: iconBubble })}568+569+        <Show when={props.onOpenPanic}>570+          <div class="peach-danger-zone">571+            <p class="peach-danger-zone-label">⚠ DANGER ZONE</p>572+            <button573+              class="peach-destroy-button"574+              onClick={() => props.onOpenPanic?.()}575+            >576+              Destroy vault577+            </button>578+          </div>579+        </Show>567580 568581         <div class="peach-settings-footer">569582           <span>Peach {version}</span>

view file

src/shared/types.ts+8 −0
@@ -66,6 +66,11 @@6666   backupEligible?: boolean;6767   backupState?: boolean;6868   lastUsedAt?: number;69+  // Passkeys are not portable between browsers: a Chrome passkey holds its70+  // private key in Peach's vault, while a Firefox passkey delegates to the71+  // platform authenticator. Tag each entry so the UI can warn cross-browser.72+  browser?: "chrome" | "firefox";73+  transports?: string[];6974   // Card entry fields7075   cardholderName?: string;7176   cardNumber?: string;       // full PAN, stored encrypted in vault@@ -116,6 +121,9 @@116121   backupEligible: boolean;117122   backupState: boolean;118123   lastUsedAt?: number;124+  // Optional on read for pre-v2.10.0 entries; new entries always set it.125+  browser?: "chrome" | "firefox";126+  transports?: string[];119127 }120128 121129 export interface PendingPasskeyRequest {

view file