diff --git a/packages/synapse-interface/components/ChangelogFeatures.tsx b/packages/synapse-interface/components/ChangelogFeatures.tsx
new file mode 100644
index 0000000000..6f0bc170a2
--- /dev/null
+++ b/packages/synapse-interface/components/ChangelogFeatures.tsx
@@ -0,0 +1,139 @@
+import React, { useState, useEffect, useRef } from 'react'
+import { SpeakerphoneIcon } from '@heroicons/react/outline'
+
+import { titleCase } from '@/utils/titleCase'
+import useCloseOnOutsideClick from '@/utils/hooks/useCloseOnOutsideClick'
+
+const CHANGELOG_RAW_URL =
+ 'https://raw.githubusercontent.com/synapsecns/sanguine/fe-release/packages/synapse-interface/CHANGELOG.md'
+
+const CHANGELOG_VISIBLE_URL =
+ 'https://github.com/synapsecns/sanguine/blob/fe-release/packages/synapse-interface/CHANGELOG.md'
+
+interface Feature {
+ version: string
+ description: string
+}
+
+export const ToggleChangelogButton = () => {
+ const ref = useRef(null)
+ const [isChangelogVisible, setIsChangelogVisible] = useState(false)
+ const [features, setFeatures] = useState([])
+ const [isNewVersion, setIsNewVersion] = useState(false)
+
+ useEffect(() => {
+ fetch(CHANGELOG_RAW_URL)
+ .then((response) => response.text())
+ .then((text) => {
+ const f = parseChangelog(text)
+ setFeatures(f)
+ const lastSeenVersion = localStorage.getItem('lastSeenVersion')
+ if (f.length > 0 && f[0].version !== lastSeenVersion) {
+ setIsNewVersion(true)
+ }
+ })
+ }, [])
+
+ const toggleChangelog = () => {
+ setIsChangelogVisible(!isChangelogVisible)
+ if (!isChangelogVisible && features.length > 0) {
+ localStorage.setItem('lastSeenVersion', features[0].version)
+ setIsNewVersion(false)
+ }
+ }
+
+ useCloseOnOutsideClick(ref, () => setIsChangelogVisible(false))
+
+ return (
+
+
+ {isChangelogVisible && (
+
+
+
+ )}
+
+ )
+}
+const ChangelogFeatures = ({ features }: { features: Feature[] }) => {
+ return (
+
+ )
+}
+
+function parseChangelog(changelogText) {
+ const features = []
+ const lines = changelogText.split('\n')
+
+ let currentVersion = null
+ let currentDescription = null
+
+ for (const line of lines) {
+ const versionMatch = line.match(/^# \[(\d+\.\d+\.\d+)\]/)
+ if (versionMatch) {
+ if (currentVersion && currentDescription) {
+ features.push({
+ version: currentVersion,
+ description: currentDescription.trim(),
+ })
+ }
+ currentVersion = versionMatch[1]
+ currentDescription = ''
+ } else if (line.startsWith('* **synapse-interface:**')) {
+ const description = line.replace('* **synapse-interface:**', '').trim()
+ const cleanedDescription = description.replace(
+ /\((\[#\d+\]|\[\w+\])\(https?:\/\/[^)]+\)\)/g,
+ ''
+ )
+ currentDescription += cleanedDescription.trim() + ' '
+ }
+ }
+
+ if (currentVersion && currentDescription) {
+ features.push({
+ version: currentVersion,
+ description: currentDescription.trim(),
+ })
+ }
+
+ return features
+}
diff --git a/packages/synapse-interface/components/Wallet.tsx b/packages/synapse-interface/components/Wallet.tsx
index bd6426881d..0d5e6596e7 100644
--- a/packages/synapse-interface/components/Wallet.tsx
+++ b/packages/synapse-interface/components/Wallet.tsx
@@ -6,6 +6,8 @@ import { CoinbaseWalletIcon } from '@icons/WalletIcons/CoinbaseWalletIcon'
import { WalletConnectIcon } from '@icons/WalletIcons/WalletConnectIcon'
import { IconProps, WalletId } from '@utils/types'
import Spinner from './icons/Spinner'
+import { SpeakerphoneIcon } from '@heroicons/react/outline'
+import { ToggleChangelogButton } from './ChangelogFeatures'
const WALLETS = [
{
diff --git a/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx b/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx
index 7c033f624c..0f56b0356d 100644
--- a/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx
+++ b/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx
@@ -27,6 +27,7 @@ import { PageFooter } from './PageFooter'
import { joinClassNames } from '@/utils/joinClassNames'
import { MaintenanceBanners } from '@/components/Maintenance/Maintenance'
import { AnnouncementBanner } from '@/components/Maintenance/components/AnnouncementBanner'
+import { ToggleChangelogButton } from '@/components/ChangelogFeatures'
const wrapperClassName = joinClassNames({
textColor: 'text-zinc-800 dark:text-zinc-200',
@@ -88,6 +89,7 @@ export function LandingNav() {
+
{({ open }) => (
diff --git a/packages/synapse-interface/utils/titleCase.ts b/packages/synapse-interface/utils/titleCase.ts
new file mode 100644
index 0000000000..f32c97b74b
--- /dev/null
+++ b/packages/synapse-interface/utils/titleCase.ts
@@ -0,0 +1,3 @@
+export const titleCase = (string: string): string => {
+ return string[0].toUpperCase() + string.slice(1).toLowerCase()
+}