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
↓ peach-browser-9bf731ef.tar.gzatlas at this save
files changed3 files
src/popup/styles/popup.css+162 −48src/popup/views/Settings.tsx+158 −85src/popup/views/TrashView.tsx+15 −29src/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 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() {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>