9bf731ef9119

pro screen redesign, canary in security, trash polish, account nav label

time
2026-04-28 18:03 UTC · 6 months ago
machine
koh2
parent
fba638254319
3 files src/popup/styles/popup.css+162 −48src/popup/views/Settings.tsx+158 −85src/popup/views/TrashView.tsx+15 −29
src/popup/styles/popup.css+162 −48
@@ -4951,9 +4951,8 @@49514951   justify-content: space-between;49524952   gap: 8px;49534953   padding: 8px 0;4954-  border-bottom: 1px solid var(--peach-t3);4954+  border-bottom: 1px solid var(--peach-border);49554955 }4956-.peach-trash-entry-row:last-child { border-bottom: none; }49574956 .peach-trash-entry-info {49584957   flex: 1;49594958   min-width: 0;@@ -4962,8 +4961,8 @@49624961   gap: 2px;49634962 }49644963 .peach-trash-entry-title {4965-  font-size: 12px;4966-  color: var(--peach-t1);4964+  font-size: 13px;4965+  color: var(--peach-t0);49674966   overflow: hidden;49684967   text-overflow: ellipsis;49694968   white-space: nowrap;@@ -4972,7 +4971,7 @@49724971   font-family: var(--font-mono);49734972   font-size: 9px;49744973   color: var(--peach-t2);4975-  letter-spacing: 0.06em;4974+  letter-spacing: 0.04em;49764975 }49774976 .peach-trash-entry-actions {49784977   display: flex;@@ -4980,15 +4979,44 @@49804979   gap: 4px;49814980   flex-shrink: 0;49824981 }4982+.peach-trash-delete-btn {4983+  background: transparent;4984+  border: 0;4985+  color: var(--peach-t3);4986+  font-size: 16px;4987+  line-height: 1;4988+  cursor: pointer;4989+  padding: 2px 5px;4990+  transition: color 0.15s;4991+}4992+.peach-trash-delete-btn:hover { color: var(--peach-breach); }49834993 .peach-trash-list {4984-  flex: 1;4985-  min-height: 0;49864994   overflow-y: auto;49874995 }4988-.peach-trash-notice {4989-  margin: 0 20px 16px;4990-  margin-top: auto;4996+.peach-trash-footer {4997+  display: flex;4998+  align-items: center;4999+  gap: 8px;5000+  padding: 8px 0 0;5001+  border-top: 1px solid var(--peach-border);5002+  margin-top: 4px;49915003 }5004+.peach-trash-purge-all {5005+  background: transparent;5006+  border: 0;5007+  color: var(--peach-t3);5008+  font-size: 10px;5009+  cursor: pointer;5010+  padding: 4px 0;5011+  letter-spacing: 0.03em;5012+  transition: color 0.2s;5013+}5014+.peach-trash-purge-all:hover { color: var(--peach-breach); }5015+.peach-trash-purge-confirm-label {5016+  font-size: 10px;5017+  color: var(--peach-t1);5018+  flex: 1;5019+}49925020 49935021 /* ── Settings shortcuts grid ─────────────────────────────────────────── */49945022 .peach-shortcuts-grid {@@ -5186,9 +5214,11 @@51865214   align-items: center;51875215 }51885216 5217+/* ── Pro tier — upgrade page ─────────────────────────────────── */51895218 .peach-pro-upgrade {51905219   display: flex;51915220   flex-direction: column;5221+  align-items: stretch;51925222   gap: 14px;51935223   padding: 4px 4px 12px;51945224 }@@ -5197,18 +5227,28 @@51975227   display: flex;51985228   flex-direction: column;51995229   align-items: center;5200-  gap: 4px;5201-  padding: 8px 0 4px;5230+  gap: 6px;5231+  padding: 16px 0 8px;52025232 }52035233 5234+.peach-pro-upgrade-mark-wrap {5235+  display: flex;5236+  align-items: center;5237+  justify-content: center;5238+  margin-bottom: 2px;5239+}5240+52045241 .peach-pro-upgrade-mark {5205-  width: 32px;5206-  height: 32px;5242+  width: 48px;5243+  height: 48px;5244+  filter:5245+    drop-shadow(0 0 8px color-mix(in srgb, var(--peach-accent) 45%, transparent))5246+    drop-shadow(0 0 22px color-mix(in srgb, var(--peach-accent) 18%, transparent));52075247 }52085248 52095249 .peach-pro-upgrade-title {52105250   font-family: var(--font-display, var(--font-ui));5211-  font-size: 22px;5251+  font-size: 24px;52125252   margin: 0;52135253   color: var(--peach-t0);52145254   letter-spacing: 0.01em;@@ -5275,17 +5315,17 @@52755315   text-align: center;52765316   font-size: 12px;52775317   color: var(--peach-t2);5318+  letter-spacing: 0.04em;52785319 }52795320 52805321 .peach-pro-upgrade-cta {5281-  align-self: center;5282-  width: auto;5283-  min-width: 160px;5322+  width: 100%;52845323   text-align: center;52855324   text-decoration: none;5286-  display: inline-block;5287-  padding: 8px 20px;5325+  display: block;5326+  padding: 10px 20px;52885327   font-size: 13px;5328+  box-sizing: border-box;52895329 }52905330 52915331 .peach-pro-key-link {@@ -5302,16 +5342,36 @@53025342   color: var(--peach-accent);53035343 }53045344 5305-.peach-pro-key-entry {5345+/* ── Pro tier — key ceremony ─────────────────────────────────── */5346+.peach-pro-key-ceremony {53065347   display: flex;53075348   flex-direction: column;5308-  gap: 8px;5349+  align-items: center;5350+  gap: 12px;5351+  padding: 20px 4px 16px;53095352 }53105353 5354+.peach-pro-key-ceremony .peach-lock-rune-string {5355+  font-size: 16px;5356+  gap: 12px;5357+  flex: 0;5358+  justify-content: center;5359+}5360+5361+.peach-pro-key-ceremony-label {5362+  margin: 0;5363+  font-size: 11px;5364+  color: var(--peach-t2);5365+  font-style: italic;5366+  letter-spacing: 0.04em;5367+}5368+53115369 .peach-pro-key-input {53125370   font-family: var(--font-mono, monospace);53135371   letter-spacing: 0.06em;53145372   text-align: center;5373+  width: 100%;5374+  box-sizing: border-box;53155375 }53165376 53175377 .peach-pro-key-error {@@ -5319,68 +5379,122 @@53195379   font-size: 12px;53205380   text-align: center;53215381   color: var(--peach-breach, #e55);5322-  padding: 2px 0 2px;5382+  padding: 0;53235383 }53245384 5385+.peach-pro-key-activate {5386+  width: 100%;5387+  text-align: center;5388+  box-sizing: border-box;5389+  padding: 10px 20px;5390+  font-size: 13px;5391+}5392+5393+.peach-pro-key-cancel {5394+  background: transparent;5395+  border: 0;5396+  color: var(--peach-t2);5397+  font-size: 11px;5398+  cursor: pointer;5399+  padding: 2px 8px;5400+  transition: color 0.2s;5401+}5402+5403+.peach-pro-key-cancel:hover { color: var(--peach-accent); }5404+53255405 /* ── Pro tier — active state ─────────────────────────────────── */53265406 .peach-pro-active {53275407   display: flex;53285408   flex-direction: column;5329-  gap: 8px;5330-  padding: 14px 4px;5409+  align-items: center;5410+  gap: 0;5411+  padding: 8px 4px 16px;5412+  min-height: 280px;53315413 }53325414 5333-.peach-pro-active-head {5415+.peach-pro-active-hero {53345416   display: flex;5417+  flex-direction: column;53355418   align-items: center;5336-  gap: 8px;5419+  gap: 6px;5420+  padding: 20px 0 22px;53375421 }53385422 5339-.peach-pro-active-mark { font-size: 16px; line-height: 1; }5423+.peach-pro-active-mark-wrap {5424+  display: flex;5425+  align-items: center;5426+  justify-content: center;5427+  margin-bottom: 4px;5428+}53405429 5430+.peach-pro-active-mark {5431+  width: 60px;5432+  height: 60px;5433+  filter:5434+    drop-shadow(0 0 10px color-mix(in srgb, var(--peach-accent) 55%, transparent))5435+    drop-shadow(0 0 26px color-mix(in srgb, var(--peach-accent) 22%, transparent));5436+}5437+53415438 .peach-pro-active-title {5439+  font-family: var(--font-display, var(--font-ui));5440+  font-size: 26px;53425441   margin: 0;5343-  font-size: 15px;5344-  font-weight: 500;53455442   color: var(--peach-t0);5443+  letter-spacing: 0.01em;53465444 }53475445 53485446 .peach-pro-active-since {53495447   margin: 0;5350-  font-size: 12px;5448+  font-size: 11px;53515449   color: var(--peach-t2);5450+  font-style: italic;53525451 }53535452 5354-.peach-pro-active-divider {5355-  height: 1px;5356-  background: var(--peach-border);5357-  margin: 8px 0 4px;5453+.peach-pro-active-features {5454+  display: flex;5455+  flex-direction: column;5456+  gap: 8px;5457+  width: 100%;5458+  padding: 14px 8px 0;5459+  border-top: 1px solid var(--peach-border);5460+  flex: 1;53585461 }53595462 5360-.peach-pro-active-thanks {5361-  margin: 0;5463+.peach-pro-active-feature {5464+  display: flex;5465+  align-items: center;5466+  gap: 9px;5467+}5468+5469+.peach-pro-active-feature-mark {5470+  color: var(--peach-accent);5471+  font-size: 10px;5472+  opacity: 0.65;5473+  flex-shrink: 0;5474+}5475+5476+.peach-pro-active-feature-name {53625477   font-size: 12px;5363-  font-style: italic;5364-  color: var(--peach-t2);5478+  color: var(--peach-t1);53655479 }53665480 53675481 .peach-pro-deactivate {5368-  align-self: flex-end;5482+  align-self: center;53695483   background: transparent;53705484   border: 0;5371-  color: var(--peach-t2);5372-  font-size: 11px;5485+  color: var(--peach-t3);5486+  font-size: 10px;53735487   cursor: pointer;5374-  padding: 6px 0;5375-  margin-top: 4px;5488+  padding: 8px 0 0;5489+  margin-top: 16px;5490+  letter-spacing: 0.03em;5491+  transition: color 0.2s;53765492 }53775493 5378-.peach-pro-deactivate:hover {5379-  color: var(--peach-accent);5380-}5494+.peach-pro-deactivate:hover { color: var(--peach-t2); }53815495 53825496 .peach-pro-deactivate:disabled {5383-  opacity: 0.6;5497+  opacity: 0.5;53845498   cursor: default;53855499 }53865500 

view file

src/popup/views/Settings.tsx+158 −85
@@ -1,6 +1,8 @@11 import { createSignal, For, Match, onMount, Show, Switch } from "solid-js";22 import { sendMessage } from "../App";33 import { PeachMark } from "../components/PeachMark";4+import { LockRuneCanvas } from "../components/LockRuneCanvas";5+import type { Canary } from "../../shared/types";46 import { ACCENT_COLORS } from "../../shared/types";57 import type { AccentColorName, PasskeyProviderState, ProFeature } from "../../shared/types";68 import type { PairedDevice, TrustedNetwork, SyncStatus } from "../../shared/sync-types";@@ -140,6 +142,10 @@140142   const [duressError, setDuressError] = createSignal<string | null>(null);141143   const [duressSuccess, setDuressSuccess] = createSignal<string | null>(null);142144 145+  const [canaryEntries, setCanaryEntries] = createSignal<Array<{id: string; title: string; canary: Canary}>>([]);146+  const [canaryTestingId, setCanaryTestingId] = createSignal<string | null>(null);147+  const [canaryTestResult, setCanaryTestResult] = createSignal<{id: string; ok: boolean} | null>(null);148+143149   const [fbCategory, setFbCategory] = createSignal("Bug report");144150   const [fbMessage, setFbMessage] = createSignal("");145151   const [fbSubmitting, setFbSubmitting] = createSignal(false);@@ -190,6 +196,16 @@190196       setDeclinedCardCount(sData?.declinedCardFingerprints?.length ?? 0);191197     }192198 199+    const entriesRes = await sendMessage({ type: "GET_ENTRIES" });200+    if ((entriesRes as { ok: boolean }).ok) {201+      const entries = (entriesRes as { ok: boolean; data?: Array<{id: string; title: string; canary?: Canary}> }).data ?? [];202+      setCanaryEntries(203+        entries204+          .filter((e) => e.canary?.webhookUrl)205+          .map((e) => ({ id: e.id, title: e.title, canary: e.canary! }))206+      );207+    }208+193209     chrome.runtime.onMessage.addListener((msg) => {194210       if (msg.type === "SYNC_STATE_UPDATE") {195211         setPairedDevices(msg.pairedDevices ?? []);@@ -536,7 +552,7 @@536552       <div class="peach-settings-body">537553         <p class="peach-settings-nav-label">PROTECTION</p>538554         {renderCatRow({ section: "security", name: "Security", desc: "Locking, breach monitoring, passkeys", icon: iconShield })}539-        {renderCatRow({ section: "license", name: "Account", desc: "Plan & license key", icon: iconKey })}555+        {renderCatRow({ section: "license", name: "Peach Pro", desc: isPro() ? "Active · manage your license" : "Upgrade · $39 · yours forever", icon: iconKey })}540556 541557         <p class="peach-settings-nav-label">VAULT</p>542558         {renderCatRow({ section: "vault", name: "Vault", desc: "Entries, health, import & export", icon: iconLock })}@@ -774,6 +790,40 @@774790             </div>775791           </Show>776792 793+          <p class="peach-settings-sub-label">CANARY ENTRIES</p>794+          <div class="peach-settings-row peach-settings-row--top">795+            <div class="peach-settings-row-main">796+              <span>Canary entries</span>797+              <span class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--peach-t2)">798+                Silently pings a webhook when an entry is accessed. Configure canary on any entry in your vault.799+              </span>800+            </div>801+            <span class="peach-settings-value" style="flex-shrink:0">802+              {canaryEntries().length > 0 ? `${canaryEntries().length} active` : "none"}803+            </span>804+          </div>805+          <Show when={canaryEntries().length > 0}>806+            <For each={canaryEntries()}>807+              {(entry) => (808+                <div class="peach-settings-row peach-settings-row--meta">809+                  <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0">{entry.title}</span>810+                  <button811+                    class="peach-btn peach-btn--subtle peach-btn--sm"812+                    onClick={() => void handleTestCanary(entry)}813+                    disabled={canaryTestingId() === entry.id}814+                    style="flex-shrink:0"815+                  >816+                    {canaryTestingId() === entry.id817+                      ? "sending…"818+                      : canaryTestResult()?.id === entry.id819+                        ? (canaryTestResult()!.ok ? "sent ✓" : "failed ✗")820+                        : "test"}821+                  </button>822+                </div>823+              )}824+            </For>825+          </Show>826+777827           <p class="peach-settings-sub-label">PASSKEYS</p>778828           <div class="peach-settings-row peach-settings-row--top">779829             <div class="peach-settings-row-main">@@ -1342,118 +1392,141 @@13421392     setDeactivating(false);13431393   }13441394 1395+  async function handleTestCanary(entry: {id: string; title: string; canary: Canary}) {1396+    setCanaryTestingId(entry.id);1397+    setCanaryTestResult(null);1398+    const res = await sendMessage({1399+      type: "FIRE_CANARY_PING",1400+      payload: { webhookUrl: entry.canary.webhookUrl, entryTitle: entry.title, trigger: "test" },1401+    }) as { ok: boolean };1402+    setCanaryTestResult({ id: entry.id, ok: res.ok });1403+    setCanaryTestingId(null);1404+    setTimeout(() => setCanaryTestResult((r) => r?.id === entry.id ? null : r), 3000);1405+  }1406+13451407   function renderLicense() {1408+    async function onActivate() {1409+      await handleActivateLicense();1410+      if ((licenseInfo()?.valid ?? false) && licenseInfo()?.plan === "pro") {1411+        await props.onLicenseChange?.();1412+      }1413+    }1414+13461415     return (13471416       <div class="peach-settings-body">13481417         <Show13491418           when={isPro()}13501419           fallback={1351-            <div class="peach-pro-upgrade">1352-              <div class="peach-pro-upgrade-hero">1353-                <PeachMark class="peach-pro-upgrade-mark" />1354-                <h2 class="peach-pro-upgrade-title">Peach Pro</h2>1355-                <p class="peach-pro-upgrade-tagline">Everything on every device.</p>1356-              </div>1357-1358-              <div class="peach-pro-features">1359-                <For each={PRO_FEATURES}>1360-                  {(item) => (1361-                    <div1362-                      class="peach-pro-feature"1363-                      classList={{1364-                        "is-highlighted": props.upgradeHighlight === item.feature,1365-                      }}1366-                    >1367-                      <span class="peach-pro-feature-mark">✦</span>1368-                      <div class="peach-pro-feature-text">1369-                        <span class="peach-pro-feature-name">{item.name}</span>1370-                        <span class="peach-pro-feature-desc">{item.desc}</span>1371-                      </div>1420+            <Show1421+              when={showKeyEntry()}1422+              fallback={1423+                <div class="peach-pro-upgrade">1424+                  <div class="peach-pro-upgrade-hero">1425+                    <div class="peach-pro-upgrade-mark-wrap">1426+                      <PeachMark class="peach-pro-upgrade-mark" />13721427                     </div>1373-                  )}1374-                </For>1375-              </div>1428+                    <h2 class="peach-pro-upgrade-title">Peach Pro</h2>1429+                    <p class="peach-pro-upgrade-tagline">Everything on every device.</p>1430+                  </div>13761431 1377-              <p class="peach-pro-pricing">$39 · once · yours forever</p>1432+                  <div class="peach-pro-features">1433+                    <For each={PRO_FEATURES}>1434+                      {(item) => (1435+                        <div1436+                          class="peach-pro-feature"1437+                          classList={{ "is-highlighted": props.upgradeHighlight === item.feature }}1438+                        >1439+                          <span class="peach-pro-feature-mark">✦</span>1440+                          <div class="peach-pro-feature-text">1441+                            <span class="peach-pro-feature-name">{item.name}</span>1442+                            <span class="peach-pro-feature-desc">{item.desc}</span>1443+                          </div>1444+                        </div>1445+                      )}1446+                    </For>1447+                  </div>13781448 1379-              <a1380-                href="https://peachpasswords.com/#pricing"1381-                target="_blank"1382-                rel="noopener noreferrer"1383-                class="peach-btn peach-btn--accent peach-pro-upgrade-cta"1384-              >1385-                Get Peach Pro1386-              </a>1449+                  <p class="peach-pro-pricing">$39 · once · yours forever</p>13871450 1388-              <Show1389-                when={showKeyEntry()}1390-                fallback={1391-                  <button1392-                    class="peach-pro-key-link"1393-                    onClick={() => setShowKeyEntry(true)}1451+                  <a1452+                    href="https://peachpasswords.com/#pricing"1453+                    target="_blank"1454+                    rel="noopener noreferrer"1455+                    class="peach-btn peach-btn--accent peach-pro-upgrade-cta"13941456                   >1457+                    Get Peach Pro1458+                  </a>1459+1460+                  <button class="peach-pro-key-link" onClick={() => setShowKeyEntry(true)}>13951461                     Already have a key? Enter it →13961462                   </button>1397-                }1398-              >1399-                <div class="peach-pro-key-entry">1400-                  <input1401-                    class="peach-underline-input peach-pro-key-input"1402-                    type="text"1403-                    placeholder="PPK-XXXX-XXXX-XXXX-XXXX-XXXX"1404-                    value={licenseKeyInput()}1405-                    onInput={(e) =>1406-                      setLicenseKeyInput(e.currentTarget.value)1407-                    }1408-                    onKeyDown={(e) => {1409-                      if (e.key === "Enter") void onActivate();1410-                    }}1411-                    autocomplete="off"1412-                    spellcheck={false}1413-                  />1414-                  <Show when={licenseError()}>1415-                    <p class="peach-pro-key-error">That key doesn't look right.</p>1416-                  </Show>1417-                  <button1418-                    class="peach-btn peach-btn--accent peach-btn--sm"1419-                    onClick={() => void onActivate()}1420-                    disabled={activatingLicense() || !licenseKeyInput().trim()}1421-                  >1422-                    {activatingLicense() ? "activating…" : "Activate"}1423-                  </button>14241463                 </div>1425-              </Show>1426-            </div>1464+              }1465+            >1466+              <div class="peach-pro-key-ceremony">1467+                <LockRuneCanvas />1468+                <p class="peach-pro-key-ceremony-label">Enter your license key</p>1469+                <input1470+                  class="peach-underline-input peach-pro-key-input"1471+                  type="text"1472+                  placeholder="PPK-XXXX-XXXX-XXXX-XXXX-XXXX"1473+                  value={licenseKeyInput()}1474+                  onInput={(e) => setLicenseKeyInput(e.currentTarget.value)}1475+                  onKeyDown={(e) => { if (e.key === "Enter") void onActivate(); }}1476+                  autocomplete="off"1477+                  spellcheck={false}1478+                />1479+                <Show when={licenseError()}>1480+                  <p class="peach-pro-key-error">That key doesn't look right.</p>1481+                </Show>1482+                <button1483+                  class="peach-btn peach-btn--accent peach-pro-key-activate"1484+                  onClick={() => void onActivate()}1485+                  disabled={activatingLicense() || !licenseKeyInput().trim()}1486+                >1487+                  {activatingLicense() ? "activating…" : "Activate"}1488+                </button>1489+                <button1490+                  class="peach-pro-key-cancel"1491+                  onClick={() => { setShowKeyEntry(false); setLicenseError(null); setLicenseKeyInput(""); }}1492+                >1493+                  cancel1494+                </button>1495+              </div>1496+            </Show>14271497           }14281498         >14291499           <div class="peach-pro-active">1430-            <div class="peach-pro-active-head">1431-              <span class="peach-pro-active-mark" aria-hidden="true">🍑</span>1432-              <h3 class="peach-pro-active-title">Peach Pro</h3>1500+            <div class="peach-pro-active-hero">1501+              <div class="peach-pro-active-mark-wrap">1502+                <PeachMark class="peach-pro-active-mark" />1503+              </div>1504+              <h2 class="peach-pro-active-title">Peach Pro</h2>1505+              <p class="peach-pro-active-since">{formatActivatedSince(licenseInfo()?.issued)}</p>14331506             </div>1434-            <p class="peach-pro-active-since">1435-              {formatActivatedSince(licenseInfo()?.issued)}1436-            </p>1437-            <div class="peach-pro-active-divider"></div>1438-            <p class="peach-pro-active-thanks">Thank you.</p>1507+1508+            <div class="peach-pro-active-features">1509+              <For each={PRO_FEATURES}>1510+                {(item) => (1511+                  <div class="peach-pro-active-feature">1512+                    <span class="peach-pro-active-feature-mark">✦</span>1513+                    <span class="peach-pro-active-feature-name">{item.name}</span>1514+                  </div>1515+                )}1516+              </For>1517+            </div>1518+14391519             <button14401520               class="peach-pro-deactivate"14411521               onClick={() => void handleDeactivateLicense()}14421522               disabled={deactivating()}14431523             >1444-              {deactivating() ? "Deactivating…" : "Deactivate →"}1524+              {deactivating() ? "Deactivating…" : "Deactivate"}14451525             </button>14461526           </div>14471527         </Show>14481528       </div>14491529     );1450-1451-    async function onActivate() {1452-      await handleActivateLicense();1453-      if ((licenseInfo()?.valid ?? false) && licenseInfo()?.plan === "pro") {1454-        await props.onLicenseChange?.();1455-      }1456-    }14571530   }14581531 14591532   function renderFeedback() {

view file

src/popup/views/TrashView.tsx+15 −29
@@ -71,15 +71,16 @@7171         </Show>7272 7373         <Show when={!loading() && deleted().length === 0}>74-          <div class="peach-list-empty" style="padding:32px 20px;">75-            <span style="font-size:13px;color:var(--t1);">trash is empty</span>74+          <div class="peach-list-empty" style="padding:48px 20px;flex-direction:column;gap:6px">75+            <span style="font-size:13px;color:var(--peach-t1)">Trash is empty.</span>76+            <span style="font-size:11px;color:var(--peach-t2)">Deleted entries appear here before being removed permanently.</span>7677           </div>7778         </Show>7879 7980         <Show when={!loading() && deleted().length > 0}>8081           <div class="peach-settings-group peach-trash-list">81-            <p class="peach-settings-label">82-              {deleted().length} deleted {deleted().length === 1 ? "entry" : "entries"}82+            <p class="peach-settings-sub-label">83+              {deleted().length} {deleted().length === 1 ? "entry" : "entries"}8384             </p>8485 8586             <For each={deleted()}>@@ -88,14 +89,13 @@8889                   <div class="peach-trash-entry-info">8990                     <span class="peach-trash-entry-title">{entry.title}</span>9091                     <span class="peach-trash-entry-meta">91-                      deleted {formatDate(entry.deletedAt)}92+                      {formatDate(entry.deletedAt)}9293                       {entry.url ? ` · ${entry.url.replace(/^https?:\/\//, "").split("/")[0]}` : ""}9394                     </span>9495                   </div>9596                   <div class="peach-trash-entry-actions">9697                     <button9798                       class="peach-btn peach-btn--subtle peach-btn--sm"98-                      style="font-size:10px"9999                       onClick={() => void restore(entry.id)}100100                     >101101                       restore@@ -104,70 +104,56 @@104104                       when={confirmPurgeId() === entry.id}105105                       fallback={106106                         <button107-                          class="peach-btn peach-btn--ghost peach-btn--sm"108-                          style="font-size:10px;color:var(--peach-breach)"107+                          class="peach-trash-delete-btn"109108                           onClick={() => setConfirmPurgeId(entry.id)}109+                          aria-label="Delete permanently"110110                         >111-                          delete111+                          ×112112                         </button>113113                       }114114                     >115115                       <button116116                         class="peach-btn peach-btn--sm peach-btn--danger"117-                        style="font-size:10px"118117                         onClick={() => void purge(entry.id)}119118                       >120-                        confirm119+                        delete121120                       </button>122121                       <button123122                         class="peach-btn peach-btn--ghost peach-btn--sm"124-                        style="font-size:10px"125123                         onClick={() => setConfirmPurgeId(null)}126124                       >127-                        ×125+                        cancel128126                       </button>129127                     </Show>130128                   </div>131129                 </div>132130               )}133131             </For>134-          </div>135132 136-          <div class="peach-settings-group">137-            <div class="peach-settings-row peach-settings-row--meta">133+            <div class="peach-trash-footer">138134               <Show139135                 when={confirmPurgeAll()}140136                 fallback={141-                  <button142-                    class="peach-btn peach-btn--ghost peach-btn--sm"143-                    style="color:var(--peach-breach);font-size:11px"144-                    onClick={() => setConfirmPurgeAll(true)}145-                  >137+                  <button class="peach-trash-purge-all" onClick={() => setConfirmPurgeAll(true)}>146138                     permanently delete all147139                   </button>148140                 }149141               >150-                <span style="font-size:11px;color:var(--t1)">delete all permanently?</span>142+                <span class="peach-trash-purge-confirm-label">delete all permanently?</span>151143                 <button152144                   class="peach-btn peach-btn--sm peach-btn--danger"153-                  style="font-size:10px"154145                   onClick={() => void purgeAll()}155146                 >156-                  yes, delete all147+                  yes157148                 </button>158149                 <button159150                   class="peach-btn peach-btn--ghost peach-btn--sm"160-                  style="font-size:10px"161151                   onClick={() => setConfirmPurgeAll(false)}162152                 >163153                   cancel164154                 </button>165155               </Show>166156             </div>167-          </div>168-169-          <div class="peach-info-notice peach-trash-notice">170-            Deleted entries are auto-removed based on your trash retention setting.171157           </div>172158         </Show>173159       </div>

view file