/** Curated release history and post-update notice selection. */ declare const __PEACH_VERSION__: string; export type ReleaseImportance = "breaking" | "critical"; export interface ReleaseNote { heading: string; body: string; } export const PEACH_VERSION: string = typeof __PEACH_VERSION__ !== "undefined" ? __PEACH_VERSION__ : "0.0.0"; export interface PeachRelease { version: string; releasedAt: string; notes: ReleaseNote[]; importance?: ReleaseImportance; } export const RELEASES: PeachRelease[] = [ { version: "5.2.48", releasedAt: "2026-09-11", notes: [{ heading: "Complete locked-vault Sync resets", body: "Sync reset now unlocks the real vault before rotating its local identity when Settings is opened after the vault session expired.", }], }, { version: "5.2.46", releasedAt: "2026-09-11", importance: "breaking", notes: [{ heading: "Sync needs one deliberate repair", body: "This security update starts a new Sync generation. Update Peach on every other device, reset Sync here, then pair each device again. Your vault and recovery words are unchanged; old device trust is archived, not silently reused.", }], }, { version: "5.2.17", releasedAt: "2026-09-07", importance: "breaking", notes: [{ heading: "Sync may need device repair", body: "If sync starts failing after this security update, revoke the affected devices and pair them again. A recent security change may require repairing device trust before sync can resume." }] }, { version: "5.2.6", releasedAt: "2026-09-06", notes: [{ heading: "Release history", body: "Settings now has an accurate, versioned changelog. Important notices combine every critical or breaking change since your previous installed version." }] }, { version: "5.2.5", releasedAt: "2026-09-06", notes: [{ heading: "Quieter update notices", body: "Routine updates no longer replay unchanged What's New information." }] }, { version: "5.2.4", releasedAt: "2026-09-06", notes: [{ heading: "Chrome passkey sign-in", body: "Older Peach passkeys work again on GitHub and other strict sites." }] }, { version: "5.2.3", releasedAt: "2026-09-06", notes: [{ heading: "Inline entry creation", body: "The + button keeps new-entry creation inside the unlocked page helper." }] }, { version: "5.2.2", releasedAt: "2026-09-06", notes: [{ heading: "Inline editing", body: "Entry editing stays in the page helper, and password-limit guidance is readable." }] }, { version: "5.1.3", releasedAt: "2026-09-05", importance: "breaking", notes: [ { heading: "Passphrases & long passwords", body: "Peach supports passphrases and passwords up to 256 characters." }, { heading: "Update Android before using Vault V4", body: "Vault V4 requires Peach Android v2.15.3 or newer. Update Android and export a backup there before moving to Vault V4." }, ], }, ]; export function hasReleaseNotes(): boolean { return RELEASES.length > 0; } const UPGRADE_FROM_KEY = "peach_release_notes_from"; const LEGACY_SEEN_KEY = "peach_whats_new_seen"; function versionParts(version: string): [number, number, number] | null { const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version); return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null; } export function compareVersions(left: string, right: string): number { const a = versionParts(left); const b = versionParts(right); if (!a || !b) return 0; for (let index = 0; index < 3; index += 1) if (a[index] !== b[index]) return a[index]! - b[index]!; return 0; } export function importantReleasesSince(version: string): PeachRelease[] { const currentVersion = PEACH_VERSION === "0.0.0" ? RELEASES[0]!.version : PEACH_VERSION; return selectImportantReleases(RELEASES, version, currentVersion); } export function selectImportantReleases( releases: PeachRelease[], fromVersion: string, throughVersion: string, ): PeachRelease[] { return releases.filter((release) => release.importance !== undefined && compareVersions(release.version, fromVersion) > 0 && compareVersions(release.version, throughVersion) <= 0 ); } export async function recordInstalledVersion(details: chrome.runtime.InstalledDetails): Promise { if (details.reason !== "update" || !details.previousVersion) return; const stored = await chrome.storage.local.get(UPGRADE_FROM_KEY); if (stored[UPGRADE_FROM_KEY] === undefined) await chrome.storage.local.set({ [UPGRADE_FROM_KEY]: details.previousVersion }); } export async function pendingImportantReleases(): Promise { try { const stored = await chrome.storage.local.get([UPGRADE_FROM_KEY, LEGACY_SEEN_KEY]); const from = stored[UPGRADE_FROM_KEY]; return typeof from === "string" && versionParts(from) ? importantReleasesSince(from) : []; } catch { return []; } } export async function shouldShowWhatsNew(): Promise { return (await pendingImportantReleases()).length > 0; } export async function markWhatsNewSeen(): Promise { try { await chrome.storage.local.remove([UPGRADE_FROM_KEY, LEGACY_SEEN_KEY]); } catch { // Best effort: a storage failure may make an important notice reappear. } }