commit e5b824c0b089b16034bef76ea0cc472ca3ba46c9
parent 5c5daf6d0273c616287986ab86d11c9de6087bfb
Author: Daniel D’Aquino <daniel@daquino.me>
Date: Sat, 30 Nov 2024 15:44:00 +0900
Merge pull request #35 from danieldaquino/notedeck#418
Notedeck#418 Notedeck landing page and install instructions
Diffstat:
31 files changed, 3168 insertions(+), 225 deletions(-)
diff --git a/content/compiled-locales/en.json b/content/compiled-locales/en.json
@@ -8,7 +8,7 @@
"benefits.benefit1.description": [
{
"type": 0,
- "value": "Built on open internet protocols, there is no platform that can ban or censor you. You are in control of your data & speech."
+ "value": "Built on open internet protocols, there is no platform that can ban or censor you. You are in control of your data & speech"
}
],
"benefits.benefit1.name": [
@@ -32,7 +32,7 @@
"benefits.benefit3.description": [
{
"type": 0,
- "value": "Traditional social media keeps you addicted in order to sell you ads, which is intoxicating society. Damus doesn't. The result? A healthier community."
+ "value": "Traditional social media keeps you addicted in order to sell you ads, which is intoxicating society. Damus doesn't. The result? A healthier community"
}
],
"benefits.benefit3.name": [
@@ -44,7 +44,7 @@
"benefits.benefit4.description": [
{
"type": 0,
- "value": "Tip your friends, earn tips, support creators, and stack sats with Bitcoin & Lightning, the Free and open internet money."
+ "value": "Tip your friends, earn tips, support creators, and stack sats with Bitcoin & Lightning, the Free and open internet money"
}
],
"benefits.benefit4.name": [
@@ -229,6 +229,114 @@
"value": "Tokyo, Japan"
}
],
+ "faq.buy.purple": [
+ {
+ "type": 0,
+ "value": "How can I buy purple if I am not a purple member?"
+ }
+ ],
+ "faq.buy.purple.a": [
+ {
+ "type": 0,
+ "value": "[https://damus.io/purple](https://damus.io/purple)"
+ }
+ ],
+ "faq.day.one": [
+ {
+ "type": 0,
+ "value": "What works on day one?"
+ }
+ ],
+ "faq.day.one.a": [
+ {
+ "type": 0,
+ "value": "- Login with public key (npub)\n- Login with private key (nsec)\n- Multiple accounts\n- Add profile columns\n- Add notifications columns\n- Add hashtag columns\n- Create post\n- Reply\n- Quote note\n- Log out`"
+ }
+ ],
+ "faq.need.purple": [
+ {
+ "type": 0,
+ "value": "Do I need purple to get notedeck on day one?"
+ }
+ ],
+ "faq.need.purple.a": [
+ {
+ "type": 0,
+ "value": "Yes!"
+ }
+ ],
+ "faq.not.work.day.one": [
+ {
+ "type": 0,
+ "value": "Important stuff that doesn't work on day one"
+ }
+ ],
+ "faq.not.work.day.one.a": [
+ {
+ "type": 0,
+ "value": "- Auto suggest tagging\n- Zaps\n- DMs\n- Reposts\n- Likes/reactions\n- Embedded video\n- Upload media\n- Modifying profile\n- Modifying relay list\n- See followers/following count\n- Search, and many more"
+ }
+ ],
+ "faq.purple.effect": [
+ {
+ "type": 0,
+ "value": "How does my purple membership affect notedeck?"
+ }
+ ],
+ "faq.purple.effect.a": [
+ {
+ "type": 0,
+ "value": "You get access to notedeck before anyone else!"
+ }
+ ],
+ "faq.start": [
+ {
+ "type": 0,
+ "value": "How do I get started (private key, public key)?"
+ }
+ ],
+ "faq.start.a": [
+ {
+ "type": 0,
+ "value": "**From iOS Damus:** `Go to side menu` -> `Tap on profile` -> Tap on the *Copy button* adjacent to npub field\n\n**Any public key:** Go to [nostr.band](https://nostr.band) in your browser, and search for a nostr user of interest (e.g. Terry Yiu) to find their npub (public key). Tap on npub to copy it.\n\n\n\n**Private key:** From iOS Damus -> go to side menu -> settings -> keys -> copy \"secret account login key\" and use this to log in to notedeck."
+ }
+ ],
+ "faq.support.signer.extension": [
+ {
+ "type": 0,
+ "value": "Does notedeck support signer extension?"
+ }
+ ],
+ "faq.support.signer.extension.a": [
+ {
+ "type": 0,
+ "value": "No. Notedeck is a desktop app where key storage is local so a signer extension isn't required. Trust in third party apps is eliminated by using the desktop app Notedeck."
+ }
+ ],
+ "faq.supported.os": [
+ {
+ "type": 0,
+ "value": "Supported operating systems"
+ }
+ ],
+ "faq.supported.os.a": [
+ {
+ "type": 0,
+ "value": "**On day one:**\n\n- Linux\n- Ubuntu\n- Fedora\n- MacOS\n- Windows\n\n**In the future:**\n- Android\n- iOS\n- iPadOS"
+ }
+ ],
+ "faq.trouble.feedback": [
+ {
+ "type": 0,
+ "value": "Having trouble, or have feedback?"
+ }
+ ],
+ "faq.trouble.feedback.a": [
+ {
+ "type": 0,
+ "value": "Tag damus on nostr, or send an email to [support@damus.io](mailto:support@damus.io)."
+ }
+ ],
"final_cta.headline": [
{
"type": 0,
@@ -293,12 +401,180 @@
"value": "shortName"
}
],
+ "notedeck.benefits.benefit1.description": [
+ {
+ "type": 0,
+ "value": "Access your account directly with your private key for enhanced security and privacy"
+ }
+ ],
+ "notedeck.benefits.benefit1.name": [
+ {
+ "type": 0,
+ "value": "Login with Private Key (nsec)"
+ }
+ ],
+ "notedeck.benefits.benefit2.description": [
+ {
+ "type": 0,
+ "value": "Login with any public key for a read-only view of any nostr account"
+ }
+ ],
+ "notedeck.benefits.benefit2.name": [
+ {
+ "type": 0,
+ "value": "Login with Public Key (npub)"
+ }
+ ],
+ "notedeck.benefits.benefit3.description": [
+ {
+ "type": 0,
+ "value": "Easily manage multiple accounts. Great for quickly switching between work and personal accounts"
+ }
+ ],
+ "notedeck.benefits.benefit3.name": [
+ {
+ "type": 0,
+ "value": "Multiple Accounts"
+ }
+ ],
+ "notedeck.benefits.benefit4.description": [
+ {
+ "type": 0,
+ "value": "Add columns to view profiles, hashtags, and notifications at a glance"
+ }
+ ],
+ "notedeck.benefits.benefit4.name": [
+ {
+ "type": 0,
+ "value": "Profile, Notification, and Hashtag columns"
+ }
+ ],
+ "notedeck.benefits.benefit5.description": [
+ {
+ "type": 0,
+ "value": "Create posts, reply to others, and quote notes to engage with the nostr community"
+ }
+ ],
+ "notedeck.benefits.benefit5.name": [
+ {
+ "type": 0,
+ "value": "Create and Interact with Posts"
+ }
+ ],
+ "notedeck.benefits.benefit6.description": [
+ {
+ "type": 0,
+ "value": "Easily log out of your account whenever needed for privacy"
+ }
+ ],
+ "notedeck.benefits.benefit6.name": [
+ {
+ "type": 0,
+ "value": "Log Out Securely"
+ }
+ ],
+ "notedeck.benefits.description": [
+ {
+ "type": 0,
+ "value": "These are currently the features you will receive immediately when you subscribe to Damus Purple"
+ }
+ ],
+ "notedeck.benefits.headline": [
+ {
+ "type": 0,
+ "value": "What’s included in Notedeck Alpha with your Damus Purple subscription?"
+ }
+ ],
+ "notedeck.feature.availability.description": [
+ {
+ "type": 0,
+ "value": "Nostriches can also switch between multiple accounts quickly and easily, drastically improving personal and business use cases"
+ }
+ ],
+ "notedeck.feature.availability.name": [
+ {
+ "type": 0,
+ "value": "Switch Between Accounts"
+ }
+ ],
+ "notedeck.feature.customization.description": [
+ {
+ "type": 0,
+ "value": "Add profile, hashtag, and notification columns of any nostr public or private key. Which means you can see the nostr landscape through other peoples' eyes"
+ }
+ ],
+ "notedeck.feature.customization.name": [
+ {
+ "type": 0,
+ "value": "Powerful Views"
+ }
+ ],
+ "notedeck.feature.speed.description": [
+ {
+ "type": 0,
+ "value": "The fastest nostr client. Built from the ground up with an ultra-fast database made exclusively for nostr, leveraging several state-of-the-art performance techniques not available on web clients"
+ }
+ ],
+ "notedeck.feature.speed.name": [
+ {
+ "type": 0,
+ "value": "Speed"
+ }
+ ],
+ "notedeck.feedback.headline": [
+ {
+ "type": 0,
+ "value": "Share your feedback with us"
+ }
+ ],
+ "notedeck.feedback.share-feedback": [
+ {
+ "type": 0,
+ "value": "Send Feedback"
+ }
+ ],
+ "notedeck.feedback.subheadline": [
+ {
+ "type": 0,
+ "value": "We encourage you to send feedback to our Damus npub over nostr or Github to we can take it in consideration in our Roadmap"
+ }
+ ],
+ "notedeck.feedback.view_github": [
+ {
+ "type": 0,
+ "value": "View our GitHub"
+ }
+ ],
+ "notedeck.hero.announcement": [
+ {
+ "type": 0,
+ "value": "Notedeck Alpha now available on Purple"
+ }
+ ],
"notedeck.hero.description": [
{
"type": 0,
"value": "Damus. Everywhere. The fastest native, customizable nostr experience, for all platforms."
}
],
+ "notedeck.hero.headline": [
+ {
+ "type": 0,
+ "value": "Damus. Everywhere. The fastest native, customizable nostr experience, for all platforms."
+ }
+ ],
+ "notedeck.hero.install": [
+ {
+ "type": 0,
+ "value": "Install now"
+ }
+ ],
+ "notedeck.hero.learn-more": [
+ {
+ "type": 0,
+ "value": "Learn more"
+ }
+ ],
"notedeck.hero.menu.signup-for-the-waitlist": [
{
"type": 0,
@@ -317,6 +593,144 @@
"value": "Notedeck is a powerful, performant Nostr client for Linux, Windows, and macOS"
}
],
+ "notedeck.not-included.description": [
+ {
+ "type": 0,
+ "value": "We are working very hard to get these features out as soon as possible. If you want to collaborate please check out our [GitHub](https://github.com/damus-io/notedeck/issues)"
+ }
+ ],
+ "notedeck.not-included.feature1.badge": [
+ {
+ "type": 0,
+ "value": "Coming 2025 Q1"
+ }
+ ],
+ "notedeck.not-included.feature1.description": [
+ {
+ "type": 0,
+ "value": "Tip creators and friends on nostr, or get tips for posting content"
+ }
+ ],
+ "notedeck.not-included.feature1.name": [
+ {
+ "type": 0,
+ "value": "Zaps"
+ }
+ ],
+ "notedeck.not-included.feature2.badge": [
+ {
+ "type": 0,
+ "value": "Coming 2025 Q1"
+ }
+ ],
+ "notedeck.not-included.feature2.description": [
+ {
+ "type": 0,
+ "value": "Send direct messages to friends"
+ }
+ ],
+ "notedeck.not-included.feature2.name": [
+ {
+ "type": 0,
+ "value": "DMs"
+ }
+ ],
+ "notedeck.not-included.feature3.badge": [
+ {
+ "type": 0,
+ "value": "Coming 2025 Q1"
+ }
+ ],
+ "notedeck.not-included.feature3.description": [
+ {
+ "type": 0,
+ "value": "View videos directly from your feed"
+ }
+ ],
+ "notedeck.not-included.feature3.name": [
+ {
+ "type": 0,
+ "value": "Embedded video"
+ }
+ ],
+ "notedeck.not-included.feature4.badge": [
+ {
+ "type": 0,
+ "value": "Coming 2025 Q1"
+ }
+ ],
+ "notedeck.not-included.feature4.description": [
+ {
+ "type": 0,
+ "value": "React to posts from your friends"
+ }
+ ],
+ "notedeck.not-included.feature4.name": [
+ {
+ "type": 0,
+ "value": "Likes/reactions/reposts"
+ }
+ ],
+ "notedeck.not-included.feature5.badge": [
+ {
+ "type": 0,
+ "value": "Coming 2025 Q1"
+ }
+ ],
+ "notedeck.not-included.feature5.description": [
+ {
+ "type": 0,
+ "value": "Easily search for content on your feed or around the nostr network, and get suggestions when tagging users"
+ }
+ ],
+ "notedeck.not-included.feature5.name": [
+ {
+ "type": 0,
+ "value": "Search"
+ }
+ ],
+ "notedeck.not-included.feature6.badge": [
+ {
+ "type": 0,
+ "value": "Coming 2025 Q1"
+ }
+ ],
+ "notedeck.not-included.feature6.description": [
+ {
+ "type": 0,
+ "value": "Attach images or videos to your posts"
+ }
+ ],
+ "notedeck.not-included.feature6.name": [
+ {
+ "type": 0,
+ "value": "Upload media"
+ }
+ ],
+ "notedeck.not-included.headline": [
+ {
+ "type": 0,
+ "value": "What's NOT included so far, but we are building right now"
+ }
+ ],
+ "notedeck.recap.description": [
+ {
+ "type": 0,
+ "value": "Damus Notedeck is a lightning fast native app that allows you to explore the nostr social network in a completely new way, with several power features. **Try it today** with our [Purple](/purple) subscription"
+ }
+ ],
+ "notedeck.recap.headline": [
+ {
+ "type": 0,
+ "value": "Let’s recap"
+ }
+ ],
+ "notedeck.recap.try_it_today": [
+ {
+ "type": 0,
+ "value": "Install now"
+ }
+ ],
"notedeck.waitlist.headline": [
{
"type": 0,
@@ -329,6 +743,30 @@
"value": "Sign up for our waitlist and be the first to know when notedeck is ready"
}
],
+ "notedeck.what-is.description1": [
+ {
+ "type": 0,
+ "value": "**Nostr is the future of social media** and Notedeck is our latest innovation towards the **future of nostr**.\n\n"
+ }
+ ],
+ "notedeck.what-is.description2": [
+ {
+ "type": 0,
+ "value": "A **lightning fast native app** that allows you to explore the nostr social network in a **completely new way**! Notedeck includes several power features to both personal and business use cases, including:"
+ }
+ ],
+ "notedeck.what-is.description3": [
+ {
+ "type": 0,
+ "value": "This isn't a web app and it's not just \"another\" social client. This is nostr **like you've never seen it before!**\n\nNotedeck works on **Linux**, **macOS**, and **Windows** on day one. An Android version will be added in 2025, along with iOS and iPadOS eventually.\n\nFuture nostr apps **will run on Notedeck in 2025.**\n\nAn early Notedeck Alpha version is available **today** to our paid [Purple](/purple) subscribers."
+ }
+ ],
+ "notedeck.what-is.headline": [
+ {
+ "type": 0,
+ "value": "Introducing Notedeck"
+ }
+ ],
"purple-checkout.account-will-renew": [
{
"type": 0,
@@ -389,6 +827,12 @@
"value": "Via Nostr"
}
],
+ "purple.account.notedeck-install-link": [
+ {
+ "type": 0,
+ "value": "Try Notedeck"
+ }
+ ],
"purple.account.sign-out": [
{
"type": 0,
diff --git a/content/locales/en.json b/content/locales/en.json
@@ -3,7 +3,7 @@
"string": "Banned in China"
},
"benefits.benefit1.description": {
- "string": "Built on open internet protocols, there is no platform that can ban or censor you. You are in control of your data & speech."
+ "string": "Built on open internet protocols, there is no platform that can ban or censor you. You are in control of your data & speech"
},
"benefits.benefit1.name": {
"string": "You are in control"
@@ -15,13 +15,13 @@
"string": "No registration required"
},
"benefits.benefit3.description": {
- "string": "Traditional social media keeps you addicted in order to sell you ads, which is intoxicating society. Damus doesn't. The result? A healthier community."
+ "string": "Traditional social media keeps you addicted in order to sell you ads, which is intoxicating society. Damus doesn't. The result? A healthier community"
},
"benefits.benefit3.name": {
"string": "No addictive algorithms"
},
"benefits.benefit4.description": {
- "string": "Tip your friends, earn tips, support creators, and stack sats with Bitcoin & Lightning, the Free and open internet money."
+ "string": "Tip your friends, earn tips, support creators, and stack sats with Bitcoin & Lightning, the Free and open internet money"
},
"benefits.benefit4.name": {
"string": "Free speech meets Free money"
@@ -110,6 +110,60 @@
"events.location.tokyo_japan": {
"string": "Tokyo, Japan"
},
+ "faq.buy.purple": {
+ "string": "How can I buy purple if I am not a purple member?"
+ },
+ "faq.buy.purple.a": {
+ "string": "[https://damus.io/purple](https://damus.io/purple)"
+ },
+ "faq.day.one": {
+ "string": "What works on day one?"
+ },
+ "faq.day.one.a": {
+ "string": "- Login with public key (npub)\n- Login with private key (nsec)\n- Multiple accounts\n- Add profile columns\n- Add notifications columns\n- Add hashtag columns\n- Create post\n- Reply\n- Quote note\n- Log out`"
+ },
+ "faq.need.purple": {
+ "string": "Do I need purple to get notedeck on day one?"
+ },
+ "faq.need.purple.a": {
+ "string": "Yes!"
+ },
+ "faq.not.work.day.one": {
+ "string": "Important stuff that doesn't work on day one"
+ },
+ "faq.not.work.day.one.a": {
+ "string": "- Auto suggest tagging\n- Zaps\n- DMs\n- Reposts\n- Likes/reactions\n- Embedded video\n- Upload media\n- Modifying profile\n- Modifying relay list\n- See followers/following count\n- Search, and many more"
+ },
+ "faq.purple.effect": {
+ "string": "How does my purple membership affect notedeck?"
+ },
+ "faq.purple.effect.a": {
+ "string": "You get access to notedeck before anyone else!"
+ },
+ "faq.start": {
+ "string": "How do I get started (private key, public key)?"
+ },
+ "faq.start.a": {
+ "string": "**From iOS Damus:** `Go to side menu` -> `Tap on profile` -> Tap on the *Copy button* adjacent to npub field\n\n**Any public key:** Go to [nostr.band](https://nostr.band) in your browser, and search for a nostr user of interest (e.g. Terry Yiu) to find their npub (public key). Tap on npub to copy it.\n\n\n\n**Private key:** From iOS Damus -> go to side menu -> settings -> keys -> copy \"secret account login key\" and use this to log in to notedeck."
+ },
+ "faq.support.signer.extension": {
+ "string": "Does notedeck support signer extension?"
+ },
+ "faq.support.signer.extension.a": {
+ "string": "No. Notedeck is a desktop app where key storage is local so a signer extension isn't required. Trust in third party apps is eliminated by using the desktop app Notedeck."
+ },
+ "faq.supported.os": {
+ "string": "Supported operating systems"
+ },
+ "faq.supported.os.a": {
+ "string": "**On day one:**\n\n- Linux\n- Ubuntu\n- Fedora\n- MacOS\n- Windows\n\n**In the future:**\n- Android\n- iOS\n- iPadOS"
+ },
+ "faq.trouble.feedback": {
+ "string": "Having trouble, or have feedback?"
+ },
+ "faq.trouble.feedback.a": {
+ "string": "Tag damus on nostr, or send an email to [support@damus.io](mailto:support@damus.io)."
+ },
"final_cta.headline": {
"string": "Download Damus today!"
},
@@ -140,9 +194,93 @@
"meet_the_team.view_profile": {
"string": "Follow {shortName}"
},
+ "notedeck.benefits.benefit1.description": {
+ "string": "Access your account directly with your private key for enhanced security and privacy"
+ },
+ "notedeck.benefits.benefit1.name": {
+ "string": "Login with Private Key (nsec)"
+ },
+ "notedeck.benefits.benefit2.description": {
+ "string": "Login with any public key for a read-only view of any nostr account"
+ },
+ "notedeck.benefits.benefit2.name": {
+ "string": "Login with Public Key (npub)"
+ },
+ "notedeck.benefits.benefit3.description": {
+ "string": "Easily manage multiple accounts. Great for quickly switching between work and personal accounts"
+ },
+ "notedeck.benefits.benefit3.name": {
+ "string": "Multiple Accounts"
+ },
+ "notedeck.benefits.benefit4.description": {
+ "string": "Add columns to view profiles, hashtags, and notifications at a glance"
+ },
+ "notedeck.benefits.benefit4.name": {
+ "string": "Profile, Notification, and Hashtag columns"
+ },
+ "notedeck.benefits.benefit5.description": {
+ "string": "Create posts, reply to others, and quote notes to engage with the nostr community"
+ },
+ "notedeck.benefits.benefit5.name": {
+ "string": "Create and Interact with Posts"
+ },
+ "notedeck.benefits.benefit6.description": {
+ "string": "Easily log out of your account whenever needed for privacy"
+ },
+ "notedeck.benefits.benefit6.name": {
+ "string": "Log Out Securely"
+ },
+ "notedeck.benefits.description": {
+ "string": "These are currently the features you will receive immediately when you subscribe to Damus Purple"
+ },
+ "notedeck.benefits.headline": {
+ "string": "What’s included in Notedeck Alpha with your Damus Purple subscription?"
+ },
+ "notedeck.feature.availability.description": {
+ "string": "Nostriches can also switch between multiple accounts quickly and easily, drastically improving personal and business use cases"
+ },
+ "notedeck.feature.availability.name": {
+ "string": "Switch Between Accounts"
+ },
+ "notedeck.feature.customization.description": {
+ "string": "Add profile, hashtag, and notification columns of any nostr public or private key. Which means you can see the nostr landscape through other peoples' eyes"
+ },
+ "notedeck.feature.customization.name": {
+ "string": "Powerful Views"
+ },
+ "notedeck.feature.speed.description": {
+ "string": "The fastest nostr client. Built from the ground up with an ultra-fast database made exclusively for nostr, leveraging several state-of-the-art performance techniques not available on web clients"
+ },
+ "notedeck.feature.speed.name": {
+ "string": "Speed"
+ },
+ "notedeck.feedback.headline": {
+ "string": "Share your feedback with us"
+ },
+ "notedeck.feedback.share-feedback": {
+ "string": "Send Feedback"
+ },
+ "notedeck.feedback.subheadline": {
+ "string": "We encourage you to send feedback to our Damus npub over nostr or Github to we can take it in consideration in our Roadmap"
+ },
+ "notedeck.feedback.view_github": {
+ "string": "View our GitHub"
+ },
+ "notedeck.hero.announcement": {
+ "string": "Notedeck Alpha now available on Purple"
+ },
"notedeck.hero.description": {
"string": "Damus. Everywhere. The fastest native, customizable nostr experience, for all platforms."
},
+ "notedeck.hero.headline": {
+ "string": "Damus. Everywhere. The fastest native, customizable nostr experience, for all platforms."
+ },
+ "notedeck.hero.install": {
+ "string": "Install now"
+ },
+ "notedeck.hero.learn-more": {
+ "string": "Learn more"
+ },
"notedeck.hero.menu.signup-for-the-waitlist": {
"string": "Sign up"
},
@@ -152,12 +290,93 @@
"notedeck.meta_description": {
"string": "Notedeck is a powerful, performant Nostr client for Linux, Windows, and macOS"
},
+ "notedeck.not-included.description": {
+ "string": "We are working very hard to get these features out as soon as possible. If you want to collaborate please check out our [GitHub](https://github.com/damus-io/notedeck/issues)"
+ },
+ "notedeck.not-included.feature1.badge": {
+ "string": "Coming 2025 Q1"
+ },
+ "notedeck.not-included.feature1.description": {
+ "string": "Tip creators and friends on nostr, or get tips for posting content"
+ },
+ "notedeck.not-included.feature1.name": {
+ "string": "Zaps"
+ },
+ "notedeck.not-included.feature2.badge": {
+ "string": "Coming 2025 Q1"
+ },
+ "notedeck.not-included.feature2.description": {
+ "string": "Send direct messages to friends"
+ },
+ "notedeck.not-included.feature2.name": {
+ "string": "DMs"
+ },
+ "notedeck.not-included.feature3.badge": {
+ "string": "Coming 2025 Q1"
+ },
+ "notedeck.not-included.feature3.description": {
+ "string": "View videos directly from your feed"
+ },
+ "notedeck.not-included.feature3.name": {
+ "string": "Embedded video"
+ },
+ "notedeck.not-included.feature4.badge": {
+ "string": "Coming 2025 Q1"
+ },
+ "notedeck.not-included.feature4.description": {
+ "string": "React to posts from your friends"
+ },
+ "notedeck.not-included.feature4.name": {
+ "string": "Likes/reactions/reposts"
+ },
+ "notedeck.not-included.feature5.badge": {
+ "string": "Coming 2025 Q1"
+ },
+ "notedeck.not-included.feature5.description": {
+ "string": "Easily search for content on your feed or around the nostr network, and get suggestions when tagging users"
+ },
+ "notedeck.not-included.feature5.name": {
+ "string": "Search"
+ },
+ "notedeck.not-included.feature6.badge": {
+ "string": "Coming 2025 Q1"
+ },
+ "notedeck.not-included.feature6.description": {
+ "string": "Attach images or videos to your posts"
+ },
+ "notedeck.not-included.feature6.name": {
+ "string": "Upload media"
+ },
+ "notedeck.not-included.headline": {
+ "string": "What's NOT included so far, but we are building right now"
+ },
+ "notedeck.recap.description": {
+ "string": "Damus Notedeck is a lightning fast native app that allows you to explore the nostr social network in a completely new way, with several power features. **Try it today** with our [Purple](/purple) subscription"
+ },
+ "notedeck.recap.headline": {
+ "string": "Let’s recap"
+ },
+ "notedeck.recap.try_it_today": {
+ "string": "Install now"
+ },
"notedeck.waitlist.headline": {
"string": "Coming soon!"
},
"notedeck.waitlist.subheadline": {
"string": "Sign up for our waitlist and be the first to know when notedeck is ready"
},
+ "notedeck.what-is.description1": {
+ "string": "**Nostr is the future of social media** and Notedeck is our latest innovation towards the **future of nostr**.\n\n"
+ },
+ "notedeck.what-is.description2": {
+ "string": "A **lightning fast native app** that allows you to explore the nostr social network in a **completely new way**! Notedeck includes several power features to both personal and business use cases, including:"
+ },
+ "notedeck.what-is.description3": {
+ "string": "This isn't a web app and it's not just \"another\" social client. This is nostr **like you've never seen it before!**\n\nNotedeck works on **Linux**, **macOS**, and **Windows** on day one. An Android version will be added in 2025, along with iOS and iPadOS eventually.\n\nFuture nostr apps **will run on Notedeck in 2025.**\n\nAn early Notedeck Alpha version is available **today** to our paid [Purple](/purple) subscribers."
+ },
+ "notedeck.what-is.headline": {
+ "string": "Introducing Notedeck"
+ },
"purple-checkout.account-will-renew": {
"string": "Paying will renew or extend your membership."
},
@@ -188,6 +407,9 @@
"purple.account.nostr-support": {
"string": "Via Nostr"
},
+ "purple.account.notedeck-install-link": {
+ "string": "Try Notedeck"
+ },
"purple.account.sign-out": {
"string": "Sign out"
},
diff --git a/package-lock.json b/package-lock.json
@@ -26,6 +26,7 @@
"react": "^18",
"react-dom": "^18",
"react-intl": "^6.4.7",
+ "react-markdown": "^9.0.1",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^2.9.2"
@@ -1602,6 +1603,39 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.3.tgz",
@@ -1617,6 +1651,21 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "0.7.34",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
+ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
+ "license": "MIT"
+ },
"node_modules/@types/node": {
"version": "20.8.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.6.tgz",
@@ -1655,6 +1704,12 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz",
"integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ=="
},
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
"node_modules/@typescript-eslint/parser": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.5.tgz",
@@ -1757,6 +1812,12 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "license": "ISC"
+ },
"node_modules/acorn": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
@@ -2078,6 +2139,16 @@
"dequal": "^2.0.3"
}
},
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -2245,6 +2316,16 @@
}
]
},
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2261,6 +2342,46 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -2340,6 +2461,16 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -2393,7 +2524,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -2406,6 +2536,19 @@
}
}
},
+ "node_modules/decode-named-character-reference": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
+ "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -2447,7 +2590,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -2457,6 +2599,19 @@
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
},
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -3061,6 +3216,16 @@
"node": ">=4.0"
}
},
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -3070,6 +3235,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3517,6 +3688,46 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz",
+ "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-object": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -3525,6 +3736,16 @@
"react-is": "^16.7.0"
}
},
+ "node_modules/html-url-attributes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -3592,6 +3813,12 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "node_modules/inline-style-parser": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
+ "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
+ "license": "MIT"
+ },
"node_modules/input-otp": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.2.3.tgz",
@@ -3634,6 +3861,30 @@
"loose-envify": "^1.0.0"
}
},
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
@@ -3740,6 +3991,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3786,6 +4047,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-map": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
@@ -3839,6 +4110,18 @@
"node": ">=8"
}
},
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-regex": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
@@ -4133,6 +4416,16 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -4164,88 +4457,682 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
}
},
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "engines": {
- "node": ">= 8"
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
+ "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
"dependencies": {
- "braces": "^3.0.2",
- "picomatch": "^2.3.1"
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
},
- "engines": {
- "node": ">=8.6"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz",
+ "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==",
+ "license": "MIT",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
},
- "engines": {
- "node": "*"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
"funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/mitata": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/mitata/-/mitata-0.1.6.tgz",
- "integrity": "sha512-VKQ0r3jriTOU9E2Z+mwbZrUmbg4Li4QyFfi7kfHKl6reZhGzL0AYlu3wE0VPXzIwA5xnFzmEQoBwCcNT8stUkA=="
- },
- "node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "node_modules/mz": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
- "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
+ "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+ "license": "MIT",
"dependencies": {
- "any-promise": "^1.0.0",
- "object-assign": "^4.0.1",
- "thenify-all": "^1.0.0"
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/nanoid": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "bin": {
- "nanoid": "bin/nanoid.cjs"
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
},
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromark": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz",
+ "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz",
+ "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz",
+ "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz",
+ "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mitata": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/mitata/-/mitata-0.1.6.tgz",
+ "integrity": "sha512-VKQ0r3jriTOU9E2Z+mwbZrUmbg4Li4QyFfi7kfHKl6reZhGzL0AYlu3wE0VPXzIwA5xnFzmEQoBwCcNT8stUkA=="
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
"node_modules/next": {
@@ -4536,6 +5423,32 @@
"node": ">=6"
}
},
+ "node_modules/parse-entities": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
+ "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4752,6 +5665,16 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/property-information": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
+ "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
@@ -4842,6 +5765,32 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/react-markdown": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz",
+ "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "html-url-attributes": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18",
+ "react": ">=18"
+ }
+ },
"node_modules/react-remove-scroll": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
@@ -4970,6 +5919,39 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz",
+ "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -5179,6 +6161,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -5252,6 +6244,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -5285,6 +6291,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/style-to-object": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
+ "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.4"
+ }
+ },
"node_modules/styled-jsx": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
@@ -5468,6 +6483,26 @@
"node": ">=8.0"
}
},
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@@ -5625,6 +6660,93 @@
"integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
"dev": true
},
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
@@ -5723,6 +6845,34 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@@ -5876,6 +7026,16 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
}
}
}
diff --git a/package.json b/package.json
@@ -33,6 +33,7 @@
"react": "^18",
"react-dom": "^18",
"react-intl": "^6.4.7",
+ "react-markdown": "^9.0.1",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^2.9.2"
diff --git a/public/logo_icon_2.png b/public/logo_icon_2.png
Binary files differ.
diff --git a/public/notedeck/notedeck-hero-2.png b/public/notedeck/notedeck-hero-2.png
Binary files differ.
diff --git a/public/notedeck/npub_example.png b/public/notedeck/npub_example.png
Binary files differ.
diff --git a/public/notedeck/share-feedback.png b/public/notedeck/share-feedback.png
Binary files differ.
diff --git a/shell.nix b/shell.nix
@@ -0,0 +1,5 @@
+{ pkgs ? import <nixpkgs> {} }:
+ pkgs.mkShell {
+ # nativeBuildInputs is usually what you want -- tools you need to run
+ nativeBuildInputs = with pkgs.buildPackages; [ nodejs_20 ];
+}
diff --git a/src/components/NotedeckInstallLayout.tsx b/src/components/NotedeckInstallLayout.tsx
@@ -0,0 +1,46 @@
+import { motion, useScroll, useTransform } from "framer-motion";
+import { RoundedContainerWithGradientBorder } from "./ui/RoundedContainerWithGradientBorder";
+import { PurpleIcon } from "./icons/PurpleIcon";
+import { MeshGradient5 } from "./effects/MeshGradient.5";
+import Image from "next/image";
+import Link from "next/link";
+import { Button } from "./ui/Button";
+import { ArrowLeft, LifeBuoy } from "lucide-react";
+import { useIntl } from "react-intl";
+import { cn } from "@/lib/utils";
+import { Onest } from "next/font/google";
+import StarField from "./effects/StarField";
+import { MeshGradient1 } from "@/components/effects/MeshGradient.1";
+import { useRef } from "react";
+import { TopMenu } from "./sections/TopMenu";
+import { MeshGradient2 } from "./effects/MeshGradient.2";
+import { MeshGradient3 } from "./effects/MeshGradient.3";
+
+const onest = Onest({ subsets: ['latin'] })
+
+
+export function NotedeckInstallLayout({ children }: { children: React.ReactNode }) {
+ const intl = useIntl()
+ const ref = useRef(null)
+ const { scrollYProgress } = useScroll({
+ target: ref,
+ offset: ["start start", "end start"]
+ })
+ const mobileScreenshotOffsetX = useTransform(scrollYProgress, [0, 1], [0, -1200]);
+ const bgOpacity = useTransform(scrollYProgress, [0.3, 1], [1.0, 0.0]);
+
+ return (
+ <div
+ className="bg-black overflow-hidden relative min-h-screen"
+ >
+ <TopMenu/>
+ <motion.div className="absolute z-0 w-full h-full pointer-events-none" style={{ opacity: bgOpacity }}>
+ <MeshGradient3 className="-translate-x-1/3" />
+ </motion.div>
+
+ <div className="container z-10 mx-auto max-w-2xl px-6 pt-6 h-full flex flex-col justify-start mt-36">
+ {children}
+ </div>
+ </div>
+ )
+}
diff --git a/src/components/pages/home.tsx b/src/components/pages/home.tsx
@@ -13,6 +13,7 @@ import { Contribute } from '@/components/sections/Contribute';
import { FinalCTA } from '@/components/sections/FinalCTA';
import { Benefits } from '@/components/sections/Benefits';
import { PurpleBanner } from '../sections/PurpleBanner';
+import { NotedeckHero } from '../sections/Notedeck/NotedeckHero';
const inter = Inter({ subsets: ['latin'] })
@@ -28,6 +29,7 @@ export function Home() {
<main style={{ scrollBehavior: "smooth" }}>
<Hero/>
<Benefits/>
+ <NotedeckHero introducing={true} />
<PurpleBanner/>
<BannedInChina/>
<div id="events">
diff --git a/src/components/pages/notedeck.tsx b/src/components/pages/notedeck.tsx
@@ -1,21 +0,0 @@
-import Head from "next/head";
-import { useIntl } from 'react-intl'
-import { Footer } from '@/components/sections/Footer';
-import { NotedeckHero } from '../sections/Notedeck/NotedeckHero';
-import { NotedeckWaitlistForm } from "../sections/Notedeck/WaitlistForm";
-
-export function Notedeck() {
- const intl = useIntl()
-
- return (<>
- <Head>
- <title>Notedeck</title>
- <meta name="description" content={intl.formatMessage({ id: "notedeck.meta_description", defaultMessage: "Notedeck is a powerful, performant Nostr client for Linux, Windows, and macOS" })} />
- </Head>
- <main style={{ scrollBehavior: "smooth" }}>
- <NotedeckHero/>
- <NotedeckWaitlistForm />
- <Footer />
- </main>
- </>)
-}
diff --git a/src/components/pages/notedeck/index.tsx b/src/components/pages/notedeck/index.tsx
@@ -0,0 +1,48 @@
+import Head from "next/head";
+import { useIntl } from 'react-intl'
+import { Footer } from '@/components/sections/Footer';
+import { NotedeckHero } from '@/components/sections/Notedeck/NotedeckHero';
+import { NotedeckWaitlistForm } from "@/components/sections/Notedeck/WaitlistForm";
+import { Benefits } from "@/components/sections/Notedeck/Benefits";
+import { NotIncluded } from "@/components/sections/Notedeck/NotIncluded";
+import { ShareFeedback } from "@/components/sections/Notedeck/ShareFeedback";
+import { NotedeckFAQ } from "@/components/sections/Notedeck/NotedeckFAQ";
+import { WhatIsNotedeck } from "@/components/sections/Notedeck/WhatIsNotedeck";
+import { Recap } from "@/components/sections/Notedeck/Recap";
+import { motion } from "framer-motion";
+import { TopMenu } from "@/components/sections/TopMenu";
+import { InstallNowButton } from "@/components/sections/Notedeck/NotedeckHero";
+
+export function Notedeck() {
+ const intl = useIntl()
+
+ return (<>
+ <Head>
+ <title>Notedeck</title>
+ <meta name="description" content={intl.formatMessage({ id: "notedeck.meta_description", defaultMessage: "Notedeck is a powerful, performant Nostr client for Linux, Windows, and macOS" })} />
+ </Head>
+ <main style={{ scrollBehavior: "smooth" }}>
+ <motion.div
+ className="w-full"
+ style={{ opacity: 0 }}
+ animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
+ >
+ <TopMenu
+ className="w-full z-10"
+ customCTA={
+ <InstallNowButton className="hidden lg:block"/>
+ }
+ hideLogoOnTop={true}
+ />
+ </motion.div>
+ <NotedeckHero/>
+ <WhatIsNotedeck />
+ <Benefits />
+ <NotIncluded />
+ <NotedeckFAQ />
+ <ShareFeedback />
+ <Recap />
+ <Footer />
+ </main>
+ </>)
+}
diff --git a/src/components/pages/notedeck/install.tsx b/src/components/pages/notedeck/install.tsx
@@ -0,0 +1,19 @@
+import Head from "next/head";
+import { useIntl } from 'react-intl'
+import { Footer } from '@/components/sections/Footer';
+import { InstallInstructions } from "@/components/sections/Notedeck/InstallInstructions";
+
+export function NotedeckInstallPage() {
+ const intl = useIntl()
+
+ return (<>
+ <Head>
+ <title>Install Notedeck</title>
+ <meta name="description" content={intl.formatMessage({ id: "notedeck.meta_description", defaultMessage: "Notedeck is a powerful, performant Nostr client for Linux, Windows, and macOS" })} />
+ </Head>
+ <main style={{ scrollBehavior: "smooth" }}>
+ <InstallInstructions/>
+ <Footer />
+ </main>
+ </>)
+}
diff --git a/src/components/sections/Benefits.tsx b/src/components/sections/Benefits.tsx
@@ -17,7 +17,7 @@ export function Benefits({ className }: { className?: string }) {
{
icon: <Joystick className="h-12 w-12 text-white opacity-80"/>,
headline: intl.formatMessage({ id: "benefits.benefit1.name", defaultMessage: "You are in control" }),
- description: intl.formatMessage({ id: "benefits.benefit1.description", defaultMessage: "Built on open internet protocols, there is no platform that can ban or censor you. You are in control of your data & speech." }),
+ description: intl.formatMessage({ id: "benefits.benefit1.description", defaultMessage: "Built on open internet protocols, there is no platform that can ban or censor you. You are in control of your data & speech" }),
},
{
icon: <KeyRound className="text-white h-12 w-12 opacity-80"/>,
@@ -27,12 +27,12 @@ export function Benefits({ className }: { className?: string }) {
{
icon: <Scale className="h-12 w-12 text-white opacity-80"/>,
headline: intl.formatMessage({ id: "benefits.benefit3.name", defaultMessage: "No addictive algorithms" }),
- description: intl.formatMessage({ id: "benefits.benefit3.description", defaultMessage: "Traditional social media keeps you addicted in order to sell you ads, which is intoxicating society. Damus doesn't. The result? A healthier community." }),
+ description: intl.formatMessage({ id: "benefits.benefit3.description", defaultMessage: "Traditional social media keeps you addicted in order to sell you ads, which is intoxicating society. Damus doesn't. The result? A healthier community" }),
},
{
icon: <Wallet className="h-12 w-12 text-white opacity-80"/>,
headline: intl.formatMessage({ id: "benefits.benefit4.name", defaultMessage: "Free speech meets Free money" }),
- description: intl.formatMessage({ id: "benefits.benefit4.description", defaultMessage: "Tip your friends, earn tips, support creators, and stack sats with Bitcoin & Lightning, the Free and open internet money." }),
+ description: intl.formatMessage({ id: "benefits.benefit4.description", defaultMessage: "Tip your friends, earn tips, support creators, and stack sats with Bitcoin & Lightning, the Free and open internet money" }),
}
]
@@ -73,4 +73,4 @@ export function Benefits({ className }: { className?: string }) {
</div>
</div>
</>)
-}-
\ No newline at end of file
+}
diff --git a/src/components/sections/Hero.tsx b/src/components/sections/Hero.tsx
@@ -17,8 +17,8 @@ export function Hero() {
<div className="absolute z-10 pointer-events-none">
<MeshGradient1 className="-translate-x-1/3"/>
</div>
+ <TopMenu className="w-full"/>
<div className="container mx-auto px-6 pb-32 pt-12">
- <TopMenu className="w-full"/>
<div className="flex flex-col lg:flex-row items-center justify-center mt-32 lg:mt-16">
<div className="w-full z-20 mb-12">
<Link href="/purple">
@@ -33,13 +33,13 @@ export function Hero() {
<ChevronRight className="ml-2"/>
</motion.div>
</Link>
- <motion.h1
+ <motion.h1
className="my-6 text-5xl sm:text-5xl md:text-7xl text-transparent bg-clip-text pb-6 font-semibold whitespace-pre-line max-w-2xl"
- style={{
+ style={{
backgroundImage: "linear-gradient(to right, #ffffff -100%, #ffffff -40%, #2D175B 100%)",
opacity: 0
}}
- animate={{
+ animate={{
backgroundImage: "linear-gradient(to right, #ffffff 0%, #ffffff 40%, #2D175B 100%)",
transition: { duration: 3 },
opacity: 1
@@ -97,7 +97,7 @@ export function Hero() {
style={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 1, duration: 1 } }}
>
- <Image
+ <Image
src="/Hero.webp"
className="object-contain"
sizes="(max-width: 800px) 100vw, 800px"
diff --git a/src/components/sections/Notedeck/Benefits.tsx b/src/components/sections/Notedeck/Benefits.tsx
@@ -0,0 +1,55 @@
+import { Onest } from 'next/font/google'
+import { FormattedMessage, useIntl } from "react-intl";
+import { motion } from "framer-motion";
+import { RoundedContainerWithGradientBorder } from "@/components/ui/RoundedContainerWithGradientBorder";
+import { cn } from "@/lib/utils";
+import { Lock, User, Layers, Bell, FileText, LogOut } from "lucide-react";
+import { MeshGradient3 } from "@/components/effects/MeshGradient.3";
+import { ItemSection } from './ItemSection';
+
+const onest = Onest({ subsets: ['latin'] })
+
+export function Benefits({ className }: { className?: string }) {
+ const intl = useIntl()
+
+ const benefits = [
+ {
+ icon: <Lock className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.benefits.benefit1.name", defaultMessage: "Login with Private Key (nsec)" }),
+ description: intl.formatMessage({ id: "notedeck.benefits.benefit1.description", defaultMessage: "Access your account directly with your private key for enhanced security and privacy" }),
+ },
+ {
+ icon: <User className="text-white h-12 w-12 opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.benefits.benefit2.name", defaultMessage: "Login with Public Key (npub)" }),
+ description: intl.formatMessage({ id: "notedeck.benefits.benefit2.description", defaultMessage: "Login with any public key for a read-only view of any nostr account" }),
+ },
+ {
+ icon: <Layers className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.benefits.benefit3.name", defaultMessage: "Multiple Accounts" }),
+ description: intl.formatMessage({ id: "notedeck.benefits.benefit3.description", defaultMessage: "Easily manage multiple accounts. Great for quickly switching between work and personal accounts" }),
+ },
+ {
+ icon: <Bell className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.benefits.benefit4.name", defaultMessage: "Profile, Notification, and Hashtag columns" }),
+ description: intl.formatMessage({ id: "notedeck.benefits.benefit4.description", defaultMessage: "Add columns to view profiles, hashtags, and notifications at a glance" }),
+ },
+ {
+ icon: <FileText className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.benefits.benefit5.name", defaultMessage: "Create and Interact with Posts" }),
+ description: intl.formatMessage({ id: "notedeck.benefits.benefit5.description", defaultMessage: "Create posts, reply to others, and quote notes to engage with the nostr community" }),
+ },
+ {
+ icon: <LogOut className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.benefits.benefit6.name", defaultMessage: "Log Out Securely" }),
+ description: intl.formatMessage({ id: "notedeck.benefits.benefit6.description", defaultMessage: "Easily log out of your account whenever needed for privacy" }),
+ },
+ ]
+
+ return (<>
+ <ItemSection
+ heading={intl.formatMessage({ id: "notedeck.benefits.headline", defaultMessage: "What’s included in Notedeck Alpha with your Damus Purple subscription?" })}
+ subHeading={intl.formatMessage({ id: "notedeck.benefits.description", defaultMessage: "These are currently the features you will receive immediately when you subscribe to Damus Purple" })}
+ items={benefits}
+ />
+ </>)
+}
diff --git a/src/components/sections/Notedeck/InstallInstructions.tsx b/src/components/sections/Notedeck/InstallInstructions.tsx
@@ -0,0 +1,54 @@
+import { useIntl } from "react-intl";
+import { useEffect, useState } from "react";
+import { ErrorDialog } from "../../ErrorDialog";
+import { usePurpleLoginSession } from "@/hooks/usePurpleLoginSession";
+import { useLocalStorage } from "usehooks-ts";
+import { NotedeckInstallLayout } from "@/components/NotedeckInstallLayout";
+import { MarkdownView } from "@/components/ui/MarkdownView";
+
+export function InstallInstructions() {
+ const intl = useIntl()
+ const [error, setError] = useState<string | null>(null)
+ const { accountInfo: loggedInAccountInfo, logout } = usePurpleLoginSession(setError)
+ const [sessionToken, setSessionToken] = useLocalStorage('session_token', null);
+ const [installInstructions, setInstallInstructions] = useState<string | undefined | null>(undefined)
+
+ // MARK: - Functions
+
+ const fetchInstallInstructions = async () => {
+ const response = await fetch(process.env.NEXT_PUBLIC_PURPLE_API_BASE_URL + "/notedeck-install-instructions", {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ' + sessionToken
+ },
+ });
+
+ if (!response.ok) {
+ setError("Failed to get Notedeck install instructions from our servers. Please wait a few minutes and refresh the page. If the problem persists, please contact support.");
+ return;
+ }
+
+ const installInstructions = await response.json();
+ setInstallInstructions(installInstructions.value);
+ }
+
+ useEffect(() => {
+ if (loggedInAccountInfo) {
+ fetchInstallInstructions();
+ }
+ else if (loggedInAccountInfo === null) {
+ // Redirect to the login page
+ window.location.href = "/purple/login?redirect=" + encodeURIComponent("/notedeck/install")
+ }
+ }, [loggedInAccountInfo])
+
+ // MARK: - Render
+
+ return (<>
+ <ErrorDialog error={error} setError={setError} />
+ <NotedeckInstallLayout>
+ <MarkdownView className="text-purple-200">{installInstructions}</MarkdownView>
+ </NotedeckInstallLayout>
+ </>)
+}
diff --git a/src/components/sections/Notedeck/ItemSection.tsx b/src/components/sections/Notedeck/ItemSection.tsx
@@ -0,0 +1,83 @@
+import { Onest } from 'next/font/google'
+import { FormattedMessage, useIntl } from "react-intl";
+import { motion } from "framer-motion";
+import { RoundedContainerWithGradientBorder } from "@/components/ui/RoundedContainerWithGradientBorder";
+import { cn } from "@/lib/utils";
+import { Lock, User, Layers, Bell, FileText, LogOut } from "lucide-react";
+import { MeshGradient3 } from "@/components/effects/MeshGradient.3";
+import { MarkdownView } from '@/components/ui/MarkdownView';
+import React from 'react';
+
+const onest = Onest({ subsets: ['latin'] })
+
+export interface ItemSectionProps {
+ className?: string,
+ subHeadingClassName?: string,
+ heading: string,
+ subHeading: string,
+ items: Item[],
+ customChild?: React.ReactNode
+}
+
+export interface Item {
+ icon: React.ReactNode
+ headline: string
+ description: string
+ badgeClassName?: string
+ badgeText?: string
+}
+
+export function ItemSection({ className, subHeadingClassName, heading, subHeading, items, customChild }: ItemSectionProps) {
+ const intl = useIntl()
+
+ return (<>
+ <div className={cn("bg-black overflow-hidden relative", className)}>
+ <div className="container mx-auto px-6 pb-32 pt-20">
+ <div className="flex flex-col items-center justify-center mt-32 lg:mt-16">
+ <div className="relative mb-16 flex flex-col items-center">
+ <motion.h2
+ className={cn("mt-6 text-3xl sm:text-4xl md:text-5xl text-center text-transparent bg-clip-text pb-6 font-semibold whitespace-pre-line max-w-4xl tracking-wide leading-relaxed", onest.className)}
+ style={{
+ backgroundImage: "linear-gradient(to right, #ffffff -100%, #ffffff -40%, #2D175B 100%)",
+ opacity: 0,
+ }}
+ animate={{
+ backgroundImage: "linear-gradient(to right, #ffffff 0%, #ffffff 40%, #2D175B 100%)",
+ transition: { duration: 3 },
+ opacity: 1
+ }}
+ >
+ {heading}
+ </motion.h2>
+ <motion.div className={cn("text-white/60 text-xl text-center max-w-2xl mb-4 mt-6 break-keep", subHeadingClassName)}>
+ <MarkdownView>{subHeading}</MarkdownView>
+ </motion.div>
+ </div>
+ {(intl.locale != "ja" || process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
+ <div className="flex flex-wrap gap-x-8 gap-y-16 items-stretch justify-center">
+ {items.map((item, index) => (
+ <div key={index} className="w-72 flex flex-col items-start justify-start">
+ <div className="flex justify-between w-full items-start">
+ {item.icon}
+ {item.badgeText && (
+ <div className={cn("py-1 px-2 bg-orange-500/30 text-orange-200 rounded-lg", item.badgeClassName)}>
+ {item.badgeText}
+ </div>
+ )}
+ </div>
+ <h3 className="text-xl font-semibold text-transparent bg-clip-text bg-gradient-to-br text-white text-left text-normal mt-6">
+ {item.headline}
+ </h3>
+ <p className="text-white/80 text-left text-normal mt-4">
+ {item.description}
+ </p>
+ </div>
+ ))}
+ </div>
+ )}
+ </div>
+ {customChild}
+ </div>
+ </div>
+ </>)
+}
diff --git a/src/components/sections/Notedeck/NotIncluded.tsx b/src/components/sections/Notedeck/NotIncluded.tsx
@@ -0,0 +1,62 @@
+import { Onest } from 'next/font/google'
+import { FormattedMessage, useIntl } from "react-intl";
+import { motion } from "framer-motion";
+import { RoundedContainerWithGradientBorder } from "@/components/ui/RoundedContainerWithGradientBorder";
+import { cn } from "@/lib/utils";
+import { Film, Joystick, KeyRound, Mail, Scale, Search, ThumbsUp, Upload, Wallet, Zap } from "lucide-react";
+import { MeshGradient3 } from "@/components/effects/MeshGradient.3";
+import { MarkdownView } from '@/components/ui/MarkdownView';
+import { ItemSection } from './ItemSection';
+
+const onest = Onest({ subsets: ['latin'] })
+
+export function NotIncluded({ className }: { className?: string }) {
+ const intl = useIntl()
+
+ const itemsNotIncluded = [
+ {
+ icon: <Zap className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.not-included.feature1.name", defaultMessage: "Zaps" }),
+ description: intl.formatMessage({ id: "notedeck.not-included.feature1.description", defaultMessage: "Tip creators and friends on nostr, or get tips for posting content" }),
+ badgeText: intl.formatMessage({ id: "notedeck.not-included.feature1.badge", defaultMessage: "Coming 2025 Q1" })
+ },
+ {
+ icon: <Mail className="text-white h-12 w-12 opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.not-included.feature2.name", defaultMessage: "DMs" }),
+ description: intl.formatMessage({ id: "notedeck.not-included.feature2.description", defaultMessage: "Send direct messages to friends" }),
+ badgeText: intl.formatMessage({ id: "notedeck.not-included.feature2.badge", defaultMessage: "Coming 2025 Q1" })
+ },
+ {
+ icon: <Film className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.not-included.feature3.name", defaultMessage: "Embedded video" }),
+ description: intl.formatMessage({ id: "notedeck.not-included.feature3.description", defaultMessage: "View videos directly from your feed" }),
+ badgeText: intl.formatMessage({ id: "notedeck.not-included.feature3.badge", defaultMessage: "Coming 2025 Q1" })
+ },
+ {
+ icon: <ThumbsUp className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.not-included.feature4.name", defaultMessage: "Likes/reactions/reposts" }),
+ description: intl.formatMessage({ id: "notedeck.not-included.feature4.description", defaultMessage: "React to posts from your friends" }),
+ badgeText: intl.formatMessage({ id: "notedeck.not-included.feature4.badge", defaultMessage: "Coming 2025 Q1" })
+ },
+ {
+ icon: <Search className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.not-included.feature5.name", defaultMessage: "Search" }),
+ description: intl.formatMessage({ id: "notedeck.not-included.feature5.description", defaultMessage: "Easily search for content on your feed or around the nostr network, and get suggestions when tagging users" }),
+ badgeText: intl.formatMessage({ id: "notedeck.not-included.feature5.badge", defaultMessage: "Coming 2025 Q1" })
+ },
+ {
+ icon: <Upload className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.not-included.feature6.name", defaultMessage: "Upload media" }),
+ description: intl.formatMessage({ id: "notedeck.not-included.feature6.description", defaultMessage: "Attach images or videos to your posts" }),
+ badgeText: intl.formatMessage({ id: "notedeck.not-included.feature6.badge", defaultMessage: "Coming 2025 Q1" })
+ },
+ ]
+
+ return (<>
+ <ItemSection
+ heading={intl.formatMessage({ id: "notedeck.not-included.headline", defaultMessage: "What's NOT included so far, but we are building right now" })}
+ subHeading={intl.formatMessage({ id: "notedeck.not-included.description", defaultMessage: "We are working very hard to get these features out as soon as possible. If you want to collaborate please check out our [GitHub](https://github.com/damus-io/notedeck/issues)" })}
+ items={itemsNotIncluded}
+ />
+ </>)
+}
diff --git a/src/components/sections/Notedeck/NotedeckFAQ.tsx b/src/components/sections/Notedeck/NotedeckFAQ.tsx
@@ -0,0 +1,110 @@
+import { FormattedMessage, useIntl } from "react-intl";
+import { motion } from "framer-motion";
+import { RoundedContainerWithGradientBorder } from "@/components/ui/RoundedContainerWithGradientBorder";
+import { cn } from "@/lib/utils";
+import { Award, Badge, Heart, Joystick, KeyRound, Scale, Stars, Wallet } from "lucide-react";
+import { MeshGradient3 } from "@/components/effects/MeshGradient.3";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/components/ui/Accordion"
+import { Onest } from "next/font/google";
+import { MarkdownView } from "@/components/ui/MarkdownView";
+
+const onest = Onest({ subsets: ['latin'] })
+
+
+export function NotedeckFAQ({ className }: { className?: string }) {
+ const intl = useIntl()
+
+ const faq = [
+ {
+ question: intl.formatMessage({ id: "faq.start", defaultMessage: "How do I get started (private key, public key)?" }),
+ answer: intl.formatMessage({
+ id: "faq.start.a",
+ defaultMessage: `**From iOS Damus:** \`Go to side menu\` -> \`Tap on profile\` -> Tap on the *Copy button* adjacent to npub field\n\n**Any public key:** Go to [nostr.band](https://nostr.band) in your browser, and search for a nostr user of interest (e.g. Terry Yiu) to find their npub (public key). Tap on npub to copy it.\n\n\n\n**Private key:** From iOS Damus -> go to side menu -> settings -> keys -> copy "secret account login key" and use this to log in to notedeck.`
+ }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.purple.effect", defaultMessage: "How does my purple membership affect notedeck?" }),
+ answer: intl.formatMessage({ id: "faq.purple.effect.a", defaultMessage: "You get access to notedeck before anyone else!" }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.need.purple", defaultMessage: "Do I need purple to get notedeck on day one?" }),
+ answer: intl.formatMessage({ id: "faq.need.purple.a", defaultMessage: "Yes!" }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.day.one", defaultMessage: "What works on day one?" }),
+ answer: intl.formatMessage({
+ id: "faq.day.one.a",
+ defaultMessage: "- Login with public key (npub)\n- Login with private key (nsec)\n- Multiple accounts\n- Add profile columns\n- Add notifications columns\n- Add hashtag columns\n- Create post\n- Reply\n- Quote note\n- Log out`"
+ }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.not.work.day.one", defaultMessage: "Important stuff that doesn't work on day one" }),
+ answer: intl.formatMessage({
+ id: "faq.not.work.day.one.a",
+ defaultMessage: `- Auto suggest tagging\n- Zaps\n- DMs\n- Reposts\n- Likes/reactions\n- Embedded video\n- Upload media\n- Modifying profile\n- Modifying relay list\n- See followers/following count\n- Search, and many more`
+ }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.trouble.feedback", defaultMessage: "Having trouble, or have feedback?" }),
+ answer: intl.formatMessage({
+ id: "faq.trouble.feedback.a",
+ defaultMessage: "Tag damus on nostr, or send an email to [support@damus.io](mailto:support@damus.io)."
+ }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.buy.purple", defaultMessage: "How can I buy purple if I am not a purple member?" }),
+ answer: intl.formatMessage({ id: "faq.buy.purple.a", defaultMessage: "[https://damus.io/purple](https://damus.io/purple)" }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.supported.os", defaultMessage: "Supported operating systems" }),
+ answer: intl.formatMessage({ id: "faq.supported.os.a", defaultMessage: `**On day one:**\n\n- Linux\n- Ubuntu\n- Fedora\n- MacOS\n- Windows\n\n**In the future:**\n- Android\n- iOS\n- iPadOS` }),
+ },
+ {
+ question: intl.formatMessage({ id: "faq.support.signer.extension", defaultMessage: "Does notedeck support signer extension?" }),
+ answer: intl.formatMessage({ id: "faq.support.signer.extension.a", defaultMessage: "No. Notedeck is a desktop app where key storage is local so a signer extension isn't required. Trust in third party apps is eliminated by using the desktop app Notedeck." }),
+ },
+ ]
+
+ return (<>
+ <div className={cn("bg-black overflow-hidden relative", className)}>
+ <MeshGradient3 className="absolute top-0 left-0 pointer-events-none translate-y-3/4 overflow-visible scale-150" />
+ <div className="container mx-auto px-6 pb-32 pt-20">
+ <div className="flex flex-col items-center justify-center mt-16">
+ <div className="relative mb-12 flex flex-col items-center">
+ <motion.h2
+ className={cn("mt-6 text-3xl sm:text-4xl md:text-5xl text-center text-transparent bg-clip-text pb-6 font-semibold whitespace-pre-line max-w-4xl tracking-wide leading-relaxed", onest.className)}
+ style={{
+ backgroundImage: "linear-gradient(to right, #ffffff -100%, #ffffff -40%, #2D175B 100%)",
+ opacity: 0,
+ }}
+ animate={{
+ backgroundImage: "linear-gradient(to right, #ffffff 0%, #ffffff 40%, #2D175B 100%)",
+ transition: { duration: 3 },
+ opacity: 1
+ }}
+ >
+ {intl.formatMessage({ id: "purple.faq.headline", defaultMessage: "Frequent Questions" })}
+ </motion.h2>
+ </div>
+ </div>
+ <Accordion type="single" collapsible className="w-full text-white max-w-2xl mx-auto">
+ {faq.map((item, index) => (
+ <AccordionItem value={`item-${index}`} key={index}>
+ <AccordionTrigger>{item.question}</AccordionTrigger>
+ <AccordionContent className="whitespace-pre-line">
+ <MarkdownView>
+ {item.answer}
+ </MarkdownView>
+ </AccordionContent>
+ </AccordionItem>
+ ))}
+ </Accordion>
+ </div>
+ </div>
+ </>)
+}
diff --git a/src/components/sections/Notedeck/NotedeckHero.tsx b/src/components/sections/Notedeck/NotedeckHero.tsx
@@ -1,97 +1,154 @@
-import { ArrowUpRight, ChevronRight, Globe2, LucideZapOff, Sparkles, Star, Zap, ZapIcon, ZapOff } from "lucide-react";
-import { MeshGradient1 } from "../../effects/MeshGradient.1";
import { TopMenu } from "../TopMenu";
+import { Onest } from 'next/font/google'
import { Button } from "../../ui/Button";
import { FormattedMessage, useIntl } from "react-intl";
import Link from "next/link";
-import { motion } from "framer-motion";
+import { motion, useScroll, useTransform } from "framer-motion";
import Image from "next/image";
-import StarField from "@/components/effects/StarField";
-import { useScroll, useTransform } from "framer-motion";
import { NOTEDECK_WAITLIST_URL } from "@/lib/constants";
+import { cn } from "@/lib/utils";
+import { ArrowUpRight, ChevronRight, Sparkles } from "lucide-react";
+import { useRef } from "react";
-export function NotedeckHero({ className }: { className?: string }) {
+const onest = Onest({ subsets: ['latin'] })
+
+export function NotedeckHero({ className, introducing }: { className?: string, introducing?: boolean }) {
const intl = useIntl()
- const { scrollYProgress } = useScroll()
- const heroOpacity = useTransform(scrollYProgress, [0.55, 0.8], [1.0, 0.0]);
- const bgOpacity = useTransform(scrollYProgress, [0.3, 0.55], [1.0, 0.0]);
- const mobileScreenshotOffsetX = useTransform(scrollYProgress, [0.2, 1], [0, -800]);
+ const ref = useRef(null)
+ const { scrollYProgress } = useScroll({
+ target: ref,
+ offset: ["start start", "end start"]
+ })
+ const mobileScreenshotOffsetX = useTransform(scrollYProgress, [0, 1], [0, -1200]);
+ const bgOpacity = useTransform(scrollYProgress, [0.3, 1], [1.0, 0.0]);
return (<>
- <motion.div
- className={`bg-black overflow-hidden relative ${className}`}
+ <div
+ ref={ref}
+ className={cn("bg-black overflow-hidden flex-col relative min-h-screen", className)}
>
- <motion.div className="absolute z-0 w-full h-full pointer-events-none" style={{ opacity: bgOpacity }}>
- <StarField className="z-0 fixed top-0 left-0 object-cover object-center w-full h-full"/>
- <MeshGradient1 className="-translate-x-1/3" />
- </motion.div>
- <motion.div className="container z-10 mx-auto px-6 pt-6 h-full min-h-screen flex flex-col justify-center" style={{ opacity: heroOpacity }}>
- <motion.div
- className="w-full z-10 backdrop-blur-sm bg-black/20 rounded-xl p-4 shadow-xl border border-black/30"
- style={{ opacity: 0 }}
- animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
- >
- <TopMenu
- className="w-full z-10"
- customCTA={
- <Link href={NOTEDECK_WAITLIST_URL}>
- <Button variant="accent" className="w-full">
- {intl.formatMessage({ id: "notedeck.hero.menu.signup-for-the-waitlist", defaultMessage: "Sign up" })}
- </Button>
- </Link>
- }
- />
- </motion.div>
+ <motion.div className="container mt-28 z-10 mx-auto px-6 pt-6 flex flex-col justify-center">
<motion.div
- className="flex flex-col items-center justify-center h-full grow mt-32 z-10"
+ className="flex flex-col items-start justify-start h-full grow mt-8 z-10"
style={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 0.5, duration: 1 } }}
>
- <div className="flex gap-x-4 items-center mb-2 md:mb-2">
- <Image
- src="/notedeck/notedeck-logo.png"
- alt="notedeck logo"
- width={500}
- height={105}
+ {introducing && (
+ <motion.h1
+ className={cn("text-2xl uppercase text-white/80 mb-12", onest.className)}
+ >
+ Introducing
+ </motion.h1>
+ )}
+ <div className="flex gap-x-4 items-center mb-2 md:mb-6">
+ <Image
+ src="/logo_icon.png"
+ alt="damus logo"
+ width={80}
+ height={80}
+ className="rounded-lg md:rounded-xl lg:rounded-2xl w-12 md:w-16 lg:w-20 aspect-square"
/>
+ <motion.h1
+ className={cn("text-4xl sm:text-4xl md:text-7xl text-transparent bg-clip-text font-semibold whitespace-pre-line max-w-4xl tracking-wide leading-loose", onest.className)}
+ style={{
+ backgroundImage: "linear-gradient(to right, #ffffff -100%, #ffffff -80%, #2D175B 100%)",
+ opacity: 0,
+ }}
+ animate={{
+ backgroundImage: "linear-gradient(to right, #ffffff 0%, #ffffff 100%, #2D175B 180%)",
+ transition: { duration: 3 },
+ opacity: 1
+ }}
+ >
+ Notedeck
+ </motion.h1>
</div>
- <div className="text-purple-200/70 text-lg md:text-2xl text-center max-w-2xl mb-8 break-keep">
- {intl.formatMessage({ id: "notedeck.hero.description", defaultMessage: "Damus. Everywhere. The fastest native, customizable nostr experience, for all platforms." })}
- </div>
+ <motion.h2
+ className={cn("mt-2 text-2xl sm:text-3xl md:text-5xl text-transparent bg-clip-text pb-6 font-semibold whitespace-pre-line max-w-4xl tracking-wide leading-8 md:leading-snug lg:leading-normal", onest.className)}
+ style={{
+ backgroundImage: "linear-gradient(to right, #ffffff -100%, #ffffff -40%, #2D175B 100%)",
+ opacity: 0,
+ }}
+ animate={{
+ backgroundImage: "linear-gradient(to right, #ffffff 0%, #ffffff 40%, #2D175B 100%)",
+ transition: { duration: 3 },
+ opacity: 1
+ }}
+ >
+ <FormattedMessage defaultMessage="Damus. Everywhere. The fastest native, customizable nostr experience, for all platforms." id="notedeck.hero.headline"/>
+ </motion.h2>
+ <motion.div
+ className="inline-flex gap-2 items-center text-xs md:text-md rounded-full bg-purple-100/10 backdrop-blur-sm shadow-lg p-1 px-3 text-purple-100/80 border border-purple-100/30 active:scale-95 transition mt-2 mb-8 md:mb-10"
+ style={{ opacity: 0 }}
+ animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
+ >
+ <Sparkles className="h-4 mr-1 text-purple-50" aria-hidden="true" />
+ {intl.formatMessage({ id: "notedeck.hero.announcement", defaultMessage: "Notedeck Alpha now available on Purple" })}
+ </motion.div>
<motion.div
className="mt-6 md:mt-4 mb-8 flex flex-col md:flex-row items-center md:items-center gap-y-4 gap-x-6 w-full md:w-auto"
style={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
>
- <Link href={NOTEDECK_WAITLIST_URL} className="w-full md:w-auto">
- <Button variant="default" className="w-full">
- {intl.formatMessage({ id: "notedeck.hero.signup-for-the-waitlist", defaultMessage: "Sign up for the waitlist" })}
- </Button>
- </Link>
+ <InstallNowButton/>
+ <a href={"/notedeck#introducing"}>
+ <Button variant="link" className="w-full md:w-auto">
+ { intl.formatMessage({ id: "notedeck.hero.learn-more", defaultMessage: "Learn more" }) }
+
+ </Button>
+ </a>
</motion.div>
- <div className="hidden md:flex gap-x-4 items-center mb-12 md:mb-6 z-20 relative">
- <Image
- src="/notedeck/notedeck-hero.png"
- alt="notedeck screenshot"
- className=""
- width={1200}
- height={105}
- />
- </div>
- <div className="flex md:hidden mb-12 md:mb-6 z-20 relative overflow-hidden w-screen">
- <motion.img
- src="/notedeck/notedeck-hero.png"
- alt="notedeck screenshot"
- className="max-w-fit"
- style={{
- marginLeft: mobileScreenshotOffsetX
- }}
- width={1200}
- height={105}
- />
- </div>
</motion.div>
</motion.div>
- </motion.div>
+ <motion.div className="flex justify-end w-full relative">
+ {/* Gradient fade to black on the bottom */}
+ <div className="w-full h-96 bottom-0 absolute bg-gradient-to-b from-transparent to-black z-10" />
+ <motion.div
+ className="z-0 w-auto pointer-events-none mt-[-553px] overflow-x-hidden overflow-y-visible flex"
+ >
+ <motion.img
+ src="/notedeck/notedeck-hero-2.png"
+ className="block md:hidden"
+ style={{
+ width: "1728px",
+ height: "1106px",
+ maxWidth: "1728px",
+ marginLeft: mobileScreenshotOffsetX,
+ opacity: bgOpacity
+ }}
+ alt="Notedeck screenshot"
+ sizes="1728px"
+ width={1728}
+ height={1106}
+ />
+ <motion.img
+ src="/notedeck/notedeck-hero-2.png"
+ className="hidden md:block"
+ style={{
+ width: "1728px",
+ height: "1106px",
+ maxWidth: "1728px",
+ }}
+ alt="Notedeck screenshot"
+ sizes="1728px"
+ width={1728}
+ height={1106}
+ />
+ </motion.div>
+ </motion.div>
+ </div>
</>)
}
+
+export function InstallNowButton({ className }: { className?: string }) {
+ const intl = useIntl()
+
+ return (
+ <Link href={"/notedeck/install"} className={cn("w-full md:w-auto", className)}>
+ <Button variant="accent" className="w-full flex gap-x-2 text-lg font-semibold">
+ <Sparkles className="w-4 h-4"/>
+ {intl.formatMessage({ id: "notedeck.hero.install", defaultMessage: "Install now" })}
+ </Button>
+ </Link>
+ )
+}
diff --git a/src/components/sections/Notedeck/NotedeckWaitlistHero.tsx b/src/components/sections/Notedeck/NotedeckWaitlistHero.tsx
@@ -0,0 +1,97 @@
+import { ArrowUpRight, ChevronRight, Globe2, LucideZapOff, Sparkles, Star, Zap, ZapIcon, ZapOff } from "lucide-react";
+import { MeshGradient1 } from "../../effects/MeshGradient.1";
+import { TopMenu } from "../TopMenu";
+import { Button } from "../../ui/Button";
+import { FormattedMessage, useIntl } from "react-intl";
+import Link from "next/link";
+import { motion } from "framer-motion";
+import Image from "next/image";
+import StarField from "@/components/effects/StarField";
+import { useScroll, useTransform } from "framer-motion";
+import { NOTEDECK_WAITLIST_URL } from "@/lib/constants";
+
+export function NotedeckWaitlistHero({ className }: { className?: string }) {
+ const intl = useIntl()
+ const { scrollYProgress } = useScroll()
+ const heroOpacity = useTransform(scrollYProgress, [0.55, 0.8], [1.0, 0.0]);
+ const bgOpacity = useTransform(scrollYProgress, [0.3, 0.55], [1.0, 0.0]);
+ const mobileScreenshotOffsetX = useTransform(scrollYProgress, [0.2, 1], [0, -800]);
+
+ return (<>
+ <motion.div
+ className={`bg-black overflow-hidden relative ${className}`}
+ >
+ <motion.div className="absolute z-0 w-full h-full pointer-events-none" style={{ opacity: bgOpacity }}>
+ <StarField className="z-0 fixed top-0 left-0 object-cover object-center w-full h-full"/>
+ <MeshGradient1 className="-translate-x-1/3" />
+ </motion.div>
+ <motion.div className="container z-10 mx-auto px-6 pt-6 h-full min-h-screen flex flex-col justify-center" style={{ opacity: heroOpacity }}>
+ <motion.div
+ className="w-full z-10 backdrop-blur-sm bg-black/20 rounded-xl p-4 shadow-xl border border-black/30"
+ style={{ opacity: 0 }}
+ animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
+ >
+ <TopMenu
+ className="w-full z-10"
+ customCTA={
+ <Link href={NOTEDECK_WAITLIST_URL}>
+ <Button variant="accent" className="w-full">
+ {intl.formatMessage({ id: "notedeck.hero.menu.signup-for-the-waitlist", defaultMessage: "Sign up" })}
+ </Button>
+ </Link>
+ }
+ />
+ </motion.div>
+ <motion.div
+ className="flex flex-col items-center justify-center h-full grow mt-32 z-10"
+ style={{ opacity: 0 }}
+ animate={{ opacity: 1, transition: { delay: 0.5, duration: 1 } }}
+ >
+ <div className="flex gap-x-4 items-center mb-2 md:mb-2">
+ <Image
+ src="/notedeck/notedeck-logo.png"
+ alt="notedeck logo"
+ width={500}
+ height={105}
+ />
+ </div>
+ <div className="text-purple-200/70 text-lg md:text-2xl text-center max-w-2xl mb-8 break-keep">
+ {intl.formatMessage({ id: "notedeck.hero.description", defaultMessage: "Damus. Everywhere. The fastest native, customizable nostr experience, for all platforms." })}
+ </div>
+ <motion.div
+ className="mt-6 md:mt-4 mb-8 flex flex-col md:flex-row items-center md:items-center gap-y-4 gap-x-6 w-full md:w-auto"
+ style={{ opacity: 0 }}
+ animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
+ >
+ <Link href={NOTEDECK_WAITLIST_URL} className="w-full md:w-auto">
+ <Button variant="default" className="w-full">
+ {intl.formatMessage({ id: "notedeck.hero.signup-for-the-waitlist", defaultMessage: "Sign up for the waitlist" })}
+ </Button>
+ </Link>
+ </motion.div>
+ <div className="hidden md:flex gap-x-4 items-center mb-12 md:mb-6 z-20 relative">
+ <Image
+ src="/notedeck/notedeck-hero.png"
+ alt="notedeck screenshot"
+ className=""
+ width={1200}
+ height={105}
+ />
+ </div>
+ <div className="flex md:hidden mb-12 md:mb-6 z-20 relative overflow-hidden w-screen">
+ <motion.img
+ src="/notedeck/notedeck-hero.png"
+ alt="notedeck screenshot"
+ className="max-w-fit"
+ style={{
+ marginLeft: mobileScreenshotOffsetX
+ }}
+ width={1200}
+ height={105}
+ />
+ </div>
+ </motion.div>
+ </motion.div>
+ </motion.div>
+ </>)
+}
diff --git a/src/components/sections/Notedeck/Recap.tsx b/src/components/sections/Notedeck/Recap.tsx
@@ -0,0 +1,62 @@
+import { Onest } from 'next/font/google'
+import { FormattedMessage, useIntl } from "react-intl";
+import { motion } from "framer-motion";
+import { RoundedContainerWithGradientBorder } from "@/components/ui/RoundedContainerWithGradientBorder";
+import { cn } from "@/lib/utils";
+import { Lock, User, Layers, Bell, FileText, LogOut, Settings2, Gauge, Globe, Sparkles } from "lucide-react";
+import { MeshGradient3 } from "@/components/effects/MeshGradient.3";
+import { Item, ItemSection } from './ItemSection';
+import Link from 'next/link';
+import { Button } from '@/components/ui/Button';
+
+const onest = Onest({ subsets: ['latin'] })
+
+export function Recap({ className }: { className?: string }) {
+ const intl = useIntl()
+
+ const items: Item[] = [
+ {
+ icon: <Settings2 className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.feature.customization.name", defaultMessage: "Customization" }),
+ description: intl.formatMessage({ id: "notedeck.feature.customization.description", defaultMessage: "Custom feeds, ability to switch between your accounts, follow hashtags in separate columns, the list goes on. Nostr prioritizes user choice and Notedeck takes this to the next level" }),
+ },
+ {
+ icon: <Gauge className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.feature.speed.name", defaultMessage: "Speed" }),
+ description: intl.formatMessage({ id: "notedeck.feature.speed.description", defaultMessage: "The fastest nostr client. Built from the ground up with an ultra-fast database made exclusively for nostr, leveraging several state-of-the-art performance techniques not available on web clients" }),
+ },
+ {
+ icon: <Globe className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.feature.availability.name", defaultMessage: "Available Everywhere" }),
+ description: intl.formatMessage({ id: "notedeck.feature.availability.description", defaultMessage: "We're no longer tied to Apple restrictions... Notedeck will eventually be available on all platforms, for everyone and with the ability to zap without approval. The sky's the limit!" }),
+ },
+ ]
+
+ return (<>
+ <ItemSection
+ heading={intl.formatMessage({ id: "notedeck.recap.headline", defaultMessage: "Let’s recap" })}
+ subHeading={intl.formatMessage({ id: "notedeck.recap.description", defaultMessage: "Damus Notedeck is a lightning fast native app that allows you to explore the nostr social network in a completely new way, with several power features. **Try it today** with our [Purple](/purple) subscription" })}
+ items={items}
+ customChild={
+ <div className="flex flex-col md:flex-row gap-x-8 gap-y-4 mx-auto my-32 w-full justify-center">
+ {(intl.locale != "ja" ||
+ process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
+ <Link
+ href="/notedeck/install"
+ target="_blank"
+ >
+ <Button variant="accent" className="w-full md:w-auto text-xl">
+ <Sparkles className="mr-2" />
+ {intl.formatMessage({
+ id: "notedeck.recap.try_it_today",
+ defaultMessage: "Install now",
+ })}
+ </Button>
+ </Link>
+ )}
+ </div>
+ }
+ />
+
+ </>)
+}
diff --git a/src/components/sections/Notedeck/ShareFeedback.tsx b/src/components/sections/Notedeck/ShareFeedback.tsx
@@ -0,0 +1,93 @@
+import { Button } from "@/components/ui/Button";
+import { useIntl } from "react-intl";
+import Link from "next/link";
+import { motion } from "framer-motion";
+import { cn } from "@/lib/utils";
+import { ArrowUpRight, MessageCircleIcon, GitBranch, Megaphone, Github } from "lucide-react";
+import { MeshGradient4 } from "@/components/effects/MeshGradient.4";
+import { GithubIcon } from "@/components/icons/GithubIcon";
+import Image from "next/image";
+
+export function ShareFeedback({ className }: { className?: string }) {
+ const intl = useIntl();
+
+ return (
+ <>
+ <div className={cn("bg-black overflow-hidden relative", className)}>
+ <div className="container mx-auto px-6 pb-24 pt-24">
+ <div className="flex flex-col items-center justify-center mt-32 lg:mt-16">
+ <div className="relative mb-24 flex flex-col items-center justify-center">
+ <Image
+ src="/notedeck/share-feedback.png"
+ width={192}
+ height={110.5}
+ className="mb-8"
+ alt={"Share feedback icon"}
+ />
+ <motion.h2 className="mb-8 text-3xl md:text-5xl lg:text-6xl text-center text-transparent bg-clip-text bg-gradient-to-r from-white from-5% to-[#E0A4D3] to-100% font-semibold">
+ {intl.formatMessage({
+ id: "notedeck.feedback.headline",
+ defaultMessage: "Share your feedback with us",
+ })}
+ </motion.h2>
+ {(intl.locale != "ja" ||
+ process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
+ <motion.div className="text-xl text-center max-w-2xl mb-12 text-white">
+ {intl.formatMessage({
+ id: "notedeck.feedback.subheadline",
+ defaultMessage:
+ "We encourage you to send feedback to our Damus npub over nostr or Github to we can take it in consideration in our Roadmap",
+ })}
+ </motion.div>
+ )}
+ <div className="flex flex-col md:flex-row gap-x-8 gap-y-4">
+ {(intl.locale != "ja" ||
+ process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
+ <Link
+ href="mailto:support@damus.io"
+ target="_blank"
+ >
+ <Button variant="accent" className="w-full md:w-auto">
+ <Megaphone className="mr-2" />
+ {intl.formatMessage({
+ id: "notedeck.feedback.share-feedback",
+ defaultMessage: "Send Feedback",
+ })}
+ <ArrowUpRight className="ml-2" />
+ </Button>
+ </Link>
+ )}
+ <Link href="https://github.com/damus-io/notedeck/issues/" target="_blank">
+ <Button variant="default" className="w-full md:w-auto">
+ <Github className="mr-2" />
+ {intl.formatMessage({
+ id: "notedeck.feedback.view_github",
+ defaultMessage: "View our GitHub",
+ })}
+ <ArrowUpRight className="ml-2" />
+ </Button>
+ </Link>
+ {(intl.locale != "ja" ||
+ process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
+ <Link
+ href="https://njump.me/npub1zafcms4xya5ap9zr7xxr0jlrtrattwlesytn2s42030lzu0dwlzqpd26k5"
+ target="_blank"
+ >
+ <Button variant="default" className="w-full md:w-auto">
+ <MessageCircleIcon className="mr-2" />
+ {intl.formatMessage({
+ id: "contribute.talk_to_a_team_member",
+ defaultMessage: "Talk to a team member",
+ })}
+ <ArrowUpRight className="ml-2" />
+ </Button>
+ </Link>
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </>
+ );
+}
diff --git a/src/components/sections/Notedeck/WhatIsNotedeck.tsx b/src/components/sections/Notedeck/WhatIsNotedeck.tsx
@@ -0,0 +1,221 @@
+import { Onest } from 'next/font/google'
+import { FormattedMessage, useIntl } from "react-intl";
+import { motion } from "framer-motion";
+import { RoundedContainerWithGradientBorder } from "@/components/ui/RoundedContainerWithGradientBorder";
+import { cn } from "@/lib/utils";
+import { Columns, Eye, Film, Gauge, Globe, Joystick, KeyRound, Mail, Scale, Search, Settings2, ThumbsUp, Upload, Wallet, Zap, ArrowLeftRightIcon } from "lucide-react";
+import { MeshGradient3 } from "@/components/effects/MeshGradient.3";
+import { MarkdownView } from '@/components/ui/MarkdownView';
+import { Item, ItemSection } from './ItemSection';
+import Markdown from 'react-markdown';
+
+const onest = Onest({ subsets: ['latin'] })
+
+export function WhatIsNotedeck({ className }: { className?: string }) {
+ const intl = useIntl()
+
+ const list = {
+ visible: {
+ opacity: 1,
+ transition: {
+ when: "beforeChildren",
+ staggerChildren: 2,
+ },
+ },
+ hidden: {
+ opacity: 0,
+ transition: {
+ when: "afterChildren",
+ },
+ },
+ }
+
+ const item = {
+ visible: {
+ opacity: 1
+ },
+ hidden: {
+ opacity: 0
+ },
+ }
+
+ const items: Item[] = [
+ {
+ icon: <Eye className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.feature.customization.name", defaultMessage: "Powerful Views" }),
+ description: intl.formatMessage({ id: "notedeck.feature.customization.description", defaultMessage: "Add profile, hashtag, and notification columns of any nostr public or private key. Which means you can see the nostr landscape through other peoples' eyes" }),
+ },
+ {
+ icon: <Gauge className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.feature.speed.name", defaultMessage: "Speed" }),
+ description: intl.formatMessage({ id: "notedeck.feature.speed.description", defaultMessage: "The fastest nostr client. Built from the ground up with an ultra-fast database made exclusively for nostr, leveraging several state-of-the-art performance techniques not available on web clients" }),
+ },
+ {
+ icon: <ArrowLeftRightIcon className="h-12 w-12 text-white opacity-80"/>,
+ headline: intl.formatMessage({ id: "notedeck.feature.availability.name", defaultMessage: "Switch Between Accounts" }),
+ description: intl.formatMessage({ id: "notedeck.feature.availability.description", defaultMessage: "Nostriches can also switch between multiple accounts quickly and easily, drastically improving personal and business use cases" }),
+ },
+ ]
+
+ return (<>
+ <div id="introducing" className={cn("bg-black overflow-hidden relative", className)}>
+ <div className="container mx-auto px-6 pb-32 pt-20">
+ <div className="flex flex-col items-center justify-center mt-32 lg:mt-16">
+ <div className="relative mb-16 flex flex-col items-center">
+ <motion.h2
+ className={cn("mt-6 text-3xl sm:text-4xl md:text-5xl text-center text-transparent bg-clip-text pb-6 font-semibold whitespace-pre-line max-w-4xl tracking-wide leading-relaxed", onest.className)}
+ style={{
+ backgroundImage: "linear-gradient(to right, #ffffff -100%, #ffffff -40%, #2D175B 100%)",
+ opacity: 0,
+ }}
+ animate={{
+ backgroundImage: "linear-gradient(to right, #ffffff 0%, #ffffff 60%, #2D175B 150%)",
+ transition: { duration: 2 },
+ opacity: 1
+ }}
+ >
+ {intl.formatMessage({ id: "notedeck.what-is.headline", defaultMessage: "Introducing Notedeck" })}
+ </motion.h2>
+ <motion.div
+ initial="hidden"
+ whileInView="visible"
+ transition={{ duration: 1 }}
+ variants={list}
+ viewport={{ once: true }}
+ >
+ <motion.div
+ className={cn("text-white/60 text-4xl leading-relaxed text-left max-w-4xl my-32 break-keep")}
+ variants={item}
+ transition={{ duration: 1 }}
+ >
+ <SpecialMarkdownView>
+ {intl.formatMessage({
+ id: "notedeck.what-is.description1",
+ defaultMessage: "**Nostr is the future of social media** and Notedeck is our latest innovation towards the **future of nostr**.\n\n"
+ })}
+ </SpecialMarkdownView>
+ </motion.div>
+ <motion.div
+ className={cn("text-white/60 text-4xl leading-relaxed text-left max-w-4xl my-32 break-keep")}
+ variants={item}
+ transition={{ duration: 1 }}
+ >
+ <SpecialMarkdownView>
+ {intl.formatMessage({
+ id: "notedeck.what-is.description2",
+ defaultMessage: "A **lightning fast native app** that allows you to explore the nostr social network in a **completely new way**! Notedeck includes several power features to both personal and business use cases, including:"
+ })}
+ </SpecialMarkdownView>
+ </motion.div>
+ {(intl.locale != "ja" || process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
+ <motion.div
+ className="flex flex-wrap gap-x-8 gap-y-16 items-stretch justify-center mt-28 mb-56"
+ variants={item}
+ >
+ {items.map((item, index) => (
+ <ItemView key={index} item={item} index={index} />
+ ))}
+ </motion.div>
+ )}
+ <motion.div
+ className={cn("text-white/60 text-4xl leading-relaxed text-left max-w-4xl my-32 break-keep")}
+ variants={item}
+ transition={{ duration: 1 }}
+ >
+ <SpecialMarkdownView>
+ {intl.formatMessage({
+ id: "notedeck.what-is.description3",
+ defaultMessage: "This isn't a web app and it's not just \"another\" social client. This is nostr **like you've never seen it before!**\n\nNotedeck works on **Linux**, **macOS**, and **Windows** on day one. An Android version will be added in 2025, along with iOS and iPadOS eventually.\n\nFuture nostr apps **will run on Notedeck in 2025.**\n\nAn early Notedeck Alpha version is available **today** to our paid [Purple](/purple) subscribers."
+ })}
+ </SpecialMarkdownView>
+ </motion.div>
+ </motion.div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </>)
+}
+
+interface ItemViewProps {
+ item: Item,
+ index: number
+}
+
+function ItemView({ item, index }: ItemViewProps) {
+ return (
+ <div key={index} className="w-72 flex flex-col items-start justify-start">
+ <div className="flex justify-between w-full items-start">
+ {item.icon}
+ {item.badgeText && (
+ <div className={cn("py-1 px-2 bg-orange-500/30 text-orange-200 rounded-lg", item.badgeClassName)}>
+ {item.badgeText}
+ </div>
+ )}
+ </div>
+ <h3 className="text-xl font-semibold text-transparent bg-clip-text bg-gradient-to-br text-white text-left text-normal mt-6">
+ {item.headline}
+ </h3>
+ <p className="text-white/80 text-left text-normal mt-4">
+ {item.description}
+ </p>
+ </div>
+ )
+}
+
+function SpecialMarkdownView({ children, className }: { children?: string | null | undefined, className?: string }) {
+
+ const container = {
+ hidden: { opacity: 0 },
+ show: {
+ opacity: 1,
+ transition: {
+ delayChildren: 1,
+ staggerChildren: 1
+ }
+ }
+ }
+
+ const item = {
+ hidden: { opacity: 0.6 },
+ show: { opacity: 1 }
+ }
+
+ return (
+ <motion.div
+ initial="hidden"
+ whileInView="show"
+ transition={{ duration: 0.5 }}
+ variants={container}
+ className={className}
+ >
+ <Markdown components={{
+ h1: ({node, ...props}) => <h1 className="opacity-90 font-bold text-6xl mb-6" {...props} />,
+ h2: ({node, ...props}) => <h2 className="opacity-90 font-bold text-4xl mb-4" {...props} />,
+ h3: ({node, ...props}) => <h3 className="opacity-90 font-bold text-2xl" {...props} />,
+ ul: ({node, ...props}) => <ul className="opacity-80 list-disc list-inside mb-5" {...props} />,
+ ol: ({node, ...props}) => <ol className="opacity-80 list-decimal list-inside mb-5" {...props} />,
+ li: ({node, ...props}) => <li className="opacity-80 m-1" {...props} />,
+ em: ({node, ...props}) => <i className="italic" {...props} />,
+ // @ts-ignore
+ strong: ({node, ...props}) => <motion.strong
+ variants={item}
+ className="font-bold text-white"
+ {...props}
+ />,
+ p: ({node, ...props}) => <p className="opacity-80 mb-4" {...props} />,
+ // @ts-ignore
+ a: ({node, ...props}) => <motion.a
+ variants={item}
+ className="text-white font-bold underline"
+ target="_blank"
+ {...props}
+ />,
+ pre: ({node, ...props}) => <pre className="my-2 bg-white/10 overflow-scroll p-3 rounded-lg font-mono" {...props} />,
+ code: ({node, ...props}) => {
+ return (<code className="my-2 bg-white/10 overflow-scroll p-1 font-mono rounded-sm" {...props} />)
+ },
+ }} className={className}>{children}</Markdown>
+ </motion.div>
+ )
+}
diff --git a/src/components/sections/PurpleAccount.tsx b/src/components/sections/PurpleAccount.tsx
@@ -33,7 +33,7 @@ export function PurpleAccount() {
setError("Failed to get profile info from the relay. Please wait a few minutes and refresh the page. If the problem persists, please contact support.")
}
}
-
+
// MARK: - Effects and hooks
// Load the profile when the pubkey changes
@@ -96,11 +96,21 @@ export function PurpleAccount() {
<AccountInfoRow label={intl.formatMessage({ id: "purple.account.account-creation", defaultMessage: "Account creation" })} value={(loggedInAccountInfo?.created_at && unixTimestampToDateString(loggedInAccountInfo?.created_at)) || "N/A"} />
<AccountInfoRow label={intl.formatMessage({ id: "purple.account.subscriber-number", defaultMessage: "Subscriber number" })} value={(loggedInAccountInfo?.subscriber_number && "#" + loggedInAccountInfo?.subscriber_number) || "N/A"} last={loggedInAccountInfo?.testflight_url ? false : true} />
{loggedInAccountInfo?.testflight_url && <Link href={loggedInAccountInfo?.testflight_url} target="_blank">
- <Button variant="link" className="w-full text-left my-2">
- <ArrowUpRight className="text-damuspink-600 mr-2" />
- {intl.formatMessage({ id: "purple.account.testflight-link", defaultMessage: "Join TestFlight" })}
- </Button>
+ <div className="py-2 border-b border-purple-200/20">
+ <Button variant="link" className="w-full text-left">
+ <ArrowUpRight className="text-damuspink-600 mr-2" />
+ {intl.formatMessage({ id: "purple.account.testflight-link", defaultMessage: "Join TestFlight" })}
+ </Button>
+ </div>
</Link>}
+ <div className="py-2">
+ <Link href="/notedeck/install" target="_blank">
+ <Button variant="link" className="w-full text-left">
+ <ArrowUpRight className="text-damuspink-600 mr-2" />
+ {intl.formatMessage({ id: "purple.account.notedeck-install-link", defaultMessage: "Try Notedeck" })}
+ </Button>
+ </Link>
+ </div>
</div>
<Button className="w-full md:w-auto opacity-70 hover:opacity-100 transition mt-4 text-sm" onClick={() => logout()} variant="link">
<LogOut className="text-damuspink-600 mr-2" />
diff --git a/src/components/sections/PurpleHero.tsx b/src/components/sections/PurpleHero.tsx
@@ -25,25 +25,25 @@ export function PurpleHero() {
<Image src="/stars-bg.webp" fill className="absolute top-0 left-0 object-cover lg:object-contain object-center w-full h-full" alt="" aria-hidden="true" />
<MeshGradient1 className="-translate-x-1/3" />
</div>
+ <TopMenu
+ className="w-full"
+ customCTA={<>
+ {loggedInAccountInfo ?
+ <Link href="/purple/account">
+ <Button variant="accent">
+ {intl.formatMessage({ id: "purple.hero.menu.go-to-my-account", defaultMessage: "My Account" })}
+ </Button>
+ </Link>
+ :
+ <Link href="/purple/login">
+ <Button variant="accent">
+ {intl.formatMessage({ id: "purple.hero.menu.login", defaultMessage: "Login" })}
+ </Button>
+ </Link>
+ }
+ </>}
+ />
<div className="container z-10 mx-auto px-6 pt-12 h-full min-h-screen flex flex-col justify-center">
- <TopMenu
- className="w-full"
- customCTA={<>
- {loggedInAccountInfo ?
- <Link href="/purple/account">
- <Button variant="accent">
- {intl.formatMessage({ id: "purple.hero.menu.go-to-my-account", defaultMessage: "My Account" })}
- </Button>
- </Link>
- :
- <Link href="/purple/login">
- <Button variant="accent">
- {intl.formatMessage({ id: "purple.hero.menu.login", defaultMessage: "Login" })}
- </Button>
- </Link>
- }
- </>}
- />
<div className="flex flex-col items-center justify-center h-full grow">
<Link href="/purple/checkout">
<motion.div
diff --git a/src/components/sections/TopMenu.tsx b/src/components/sections/TopMenu.tsx
@@ -2,11 +2,11 @@ import * as React from "react"
import Link from "next/link"
import { cn } from "@/lib/utils"
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
-import { Zap } from "lucide-react"
+import { Menu, X, Zap } from "lucide-react"
import { Button } from "../ui/Button";
import { DAMUS_APP_STORE_URL, DAMUS_MERCH_STORE_URL } from "@/lib/constants";
import { useIntl } from "react-intl";
-import { motion } from "framer-motion";
+import { AnimatePresence, motion, useScroll, useTransform } from "framer-motion";
let regularNavItems: { nameIntlId: string, href: string, target?: string }[] = [
{ nameIntlId: "topbar.purple", href: "/purple" },
@@ -22,10 +22,19 @@ const ENABLE_FULL_MENU = true
export interface TopMenuProps {
className?: string
customCTA?: React.ReactNode
+ hideLogoOnTop?: boolean
}
-export function TopMenu({ className, customCTA }: TopMenuProps) {
+export function TopMenu({ className, customCTA, hideLogoOnTop: hideLogoOnTop = false }: TopMenuProps) {
let navItemDefaultStyles = "hover:opacity-80 transition-opacity duration-200 ease-in-out"
+ const [showMobileMenu, setShowMobileMenu] = React.useState<boolean>(false)
+ const ref = React.useRef(null)
+ const { scrollYProgress } = useScroll({
+ target: ref,
+ offset: ["start start", "end start"]
+ })
+ const logoOpacity = useTransform(scrollYProgress, [0.1, 0.5], [hideLogoOnTop ? 0.0 : 1.0, 1.0]);
+ const menuBgColor = useTransform(scrollYProgress, [0.1, 0.5], ["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"]);
const intl = useIntl()
// This is needed to allow intl commands to extract the strings
@@ -38,42 +47,82 @@ export function TopMenu({ className, customCTA }: TopMenuProps) {
"topbar.contribute": intl.formatMessage({ id: "topbar.contribute", defaultMessage: "Contribute" }),
}
- return (
+ return (<>
+ <div ref={ref} className="absolute top-0 h-screen bg-transparent pointer-events-none w-4 left-0" />
<motion.div
- style={{ opacity: 0 }}
+ style={{
+ opacity: 0,
+ backgroundColor: showMobileMenu ? "rgba(0,0,0,0.5)" : menuBgColor,
+ borderColor: showMobileMenu ? "rgba(0,0,0,0.5)" : menuBgColor,
+ }}
animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
+ className="w-full border-b z-50 backdrop-blur-sm fixed"
>
- <NavigationMenu.Root className={cn("flex justify-between items-center", className)}>
- <div>
+ <NavigationMenu.Root className={cn("flex flex-col items-center z-50", className)}>
+ <div className="container justify-between items-center flex p-6">
+ <motion.div
+ style={{ opacity: logoOpacity }}
+ >
<NavigationMenu.Link className={cn(navItemDefaultStyles, "text-white")} href="/">
- <img src="/logo.png" className="h-12" alt={ intl.formatMessage({ id: "topbar.logo_alt_text", defaultMessage: "Damus logo" }) }/>
+ <img src="/logo_icon_2.png" className="h-12" alt={ intl.formatMessage({ id: "topbar.logo_alt_text", defaultMessage: "Damus logo" }) }/>
</NavigationMenu.Link>
+ </motion.div>
+ <div className="hidden lg:block">
+ <NavigationMenu.List className={cn("inline-flex space-x-6 items-center justify-self-center")}>
+ {ENABLE_FULL_MENU && (<>
+ {regularNavItems.map((item, index) => (
+ <NavigationMenu.Item key={index}>
+ <NavigationMenu.Link className={cn(navItemDefaultStyles, "text-white")} href={item.href} target={item.target}>
+ {topbarItemNameIntl[item.nameIntlId]}
+ </NavigationMenu.Link>
+ </NavigationMenu.Item>
+ ))}
+ <NavigationMenu.Item>
+ <NavigationMenu.Link className={cn("text-yellow-500 flex items-center")} href="lightning:lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0v3sk6atnjmrn5a" target="_blank">
+ <Zap className="h-4"/>
+ { intl.formatMessage({ id: "topbar.zap_us", defaultMessage: "Zap Us" }) }
+ </NavigationMenu.Link>
+ </NavigationMenu.Item>
+ </>)}
+ </NavigationMenu.List>
</div>
- <NavigationMenu.List className={cn("hidden lg:inline-flex space-x-6 items-center justify-self-center")}>
- {ENABLE_FULL_MENU && (<>
- {regularNavItems.map((item, index) => (
- <NavigationMenu.Item key={index}>
- <NavigationMenu.Link className={cn(navItemDefaultStyles, "text-white")} href={item.href} target={item.target}>
- {topbarItemNameIntl[item.nameIntlId]}
+ <div className="hidden lg:block">
+ {customCTA ? customCTA : <>
+ <Link href={DAMUS_APP_STORE_URL} target="_blank" className="hidden lg:block">
+ <Button variant="accent">
+ {intl.formatMessage({ id: "topbar.download", defaultMessage: "Download" })}
+ </Button>
+ </Link>
+ </>}
+ </div>
+ <button className="lg:hidden" onClick={() => setShowMobileMenu(!showMobileMenu)}>
+ {showMobileMenu ? <X className="h-8 text-white"/> : <Menu className="h-8 text-white"/>}
+ </button>
+ </div>
+ <AnimatePresence>
+ {showMobileMenu && (
+ <motion.div className="lg:hidden container flex justify-end mb-6" key="mobile-menu">
+ <NavigationMenu.List className={cn("flex flex-col gap-6 justify-start items-end text-3xl font-bold p-6")}>
+ {ENABLE_FULL_MENU && (<>
+ {regularNavItems.map((item, index) => (
+ <NavigationMenu.Item key={index}>
+ <NavigationMenu.Link className={cn(navItemDefaultStyles, "text-white")} href={item.href} target={item.target}>
+ {topbarItemNameIntl[item.nameIntlId]}
+ </NavigationMenu.Link>
+ </NavigationMenu.Item>
+ ))}
+ <NavigationMenu.Item>
+ <NavigationMenu.Link className={cn("text-yellow-500 flex items-center")} href="lightning:lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0v3sk6atnjmrn5a" target="_blank">
+ <Zap className="h-4"/>
+ { intl.formatMessage({ id: "topbar.zap_us", defaultMessage: "Zap Us" }) }
</NavigationMenu.Link>
</NavigationMenu.Item>
- ))}
- <NavigationMenu.Item>
- <NavigationMenu.Link className={cn("text-yellow-500 flex items-center")} href="lightning:lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0v3sk6atnjmrn5a" target="_blank">
- <Zap className="h-4"/>
- { intl.formatMessage({ id: "topbar.zap_us", defaultMessage: "Zap Us" }) }
- </NavigationMenu.Link>
- </NavigationMenu.Item>
- </>)}
- </NavigationMenu.List>
- {customCTA ? customCTA : <>
- <Link href={DAMUS_APP_STORE_URL} target="_blank">
- <Button variant="accent">
- {intl.formatMessage({ id: "topbar.download", defaultMessage: "Download" })}
- </Button>
- </Link>
- </>}
+ </>)}
+ </NavigationMenu.List>
+ </motion.div>
+ )}
+ </AnimatePresence>
</NavigationMenu.Root>
</motion.div>
- )
+ </>)
}
diff --git a/src/components/ui/MarkdownView.tsx b/src/components/ui/MarkdownView.tsx
@@ -0,0 +1,23 @@
+import React from 'react'
+import Markdown from 'react-markdown'
+
+export function MarkdownView({ children, className }: { children?: string | null | undefined, className?: string }) {
+ return (
+ <Markdown components={{
+ h1: ({node, ...props}) => <h1 className="opacity-90 font-bold text-6xl mb-6" {...props} />,
+ h2: ({node, ...props}) => <h2 className="opacity-90 font-bold text-4xl mb-4" {...props} />,
+ h3: ({node, ...props}) => <h3 className="opacity-90 font-bold text-2xl" {...props} />,
+ ul: ({node, ...props}) => <ul className="opacity-80 list-disc list-inside mb-5" {...props} />,
+ ol: ({node, ...props}) => <ol className="opacity-80 list-decimal list-inside mb-5" {...props} />,
+ li: ({node, ...props}) => <li className="opacity-80 m-1" {...props} />,
+ em: ({node, ...props}) => <i className="italic" {...props} />,
+ strong: ({node, ...props}) => <strong className="font-bold text-white" {...props} />,
+ p: ({node, ...props}) => <p className="opacity-80 mb-4" {...props} />,
+ a: ({node, ...props}) => <a className="text-damuspink-600 underline" {...props} />,
+ pre: ({node, ...props}) => <pre className="my-2 bg-white/10 overflow-scroll p-3 rounded-lg font-mono" {...props} />,
+ code: ({node, ...props}) => {
+ return (<code className="my-2 bg-white/10 overflow-scroll p-1 font-mono rounded-sm" {...props} />)
+ },
+ }} className={className}>{children}</Markdown>
+ )
+}
diff --git a/src/pages/notedeck/install.tsx b/src/pages/notedeck/install.tsx
@@ -0,0 +1,42 @@
+import { Inter } from 'next/font/google'
+import { IntlProvider, useIntl } from 'react-intl'
+import English from "@/../content/compiled-locales/en.json";
+import Japanese from "@/../content/compiled-locales/ja.json";
+import { useEffect } from 'react';
+import { useState } from 'react';
+import { Notedeck } from '@/components/pages/notedeck';
+import { NotedeckInstallPage } from '@/components/pages/notedeck/install';
+
+export default function Page() {
+ // Automatically detect the user's locale based on their browser settings
+ const [language, setLanguage] = useState("en");
+ const [messages, setMessages] = useState(English);
+
+ useEffect(() => {
+ setLanguage(navigator.language);
+ }, []);
+
+ useEffect(() => {
+ switch (language) {
+ case "en":
+ setMessages(English);
+ break;
+ case "ja":
+ // TODO: Add Japanese translations and then switch to "Japanese" below
+ setMessages(English);
+ break;
+ default:
+ setMessages(English);
+ break;
+ }
+ }, [language]);
+
+ return (<>
+ <IntlProvider
+ locale={language}
+ messages={messages}
+ onError={() => null}>
+ <NotedeckInstallPage />
+ </IntlProvider>
+ </>)
+}