Merge branch 'new-website' into ep/new-site-generate
@@ -54,6 +54,7 @@ website/translations.json
|
||||
website/src/img/images/
|
||||
website/src/images/
|
||||
website/src/js/lottie.min.js
|
||||
website/src/js/ethers*
|
||||
website/src/privacy.md
|
||||
# Generated files
|
||||
website/package/generated*
|
||||
|
||||
@@ -425,6 +425,8 @@ Please do NOT report security vulnerabilities via GitHub issues.
|
||||
|
||||
This software is licensed under the GNU Affero General Public License version 3 (AGPLv3). See the [LICENSE](./LICENSE) file for details. The SimpleX and SimpleX Chat name, logo, and associated branding materials are not covered by this license and are subject to the terms outlined in the [TRADEMARK](./docs/TRADEMARK.md) file.
|
||||
|
||||
Graphic designs, artworks and layouts are not licensed for re-use. If you want to use them in your publications, please ask for permission. Texts can be used as direct quotes, referencing the source.
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/apple_store.svg" alt="iOS app" height="42">](https://apps.apple.com/us/app/simplex-chat/id1605771084)
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=chat.simplex.app)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# SimpleX Chat website
|
||||
|
||||
## License
|
||||
|
||||
SimpleX Chat website code is licensed under the GNU Affero General Public License version 3 (AGPLv3). See the [LICENSE](../LICENSE) file for details.
|
||||
|
||||
The SimpleX and SimpleX Chat name, logo, and associated branding materials are not covered by this license and are subject to the terms outlined in the [TRADEMARK](./docs/TRADEMARK.md) file.
|
||||
|
||||
Graphic designs, artworks and layouts are not licensed for re-use. If you want to use them in your publications, please ask for permission. Texts can be used as direct quotes, referencing the source.
|
||||
@@ -21,7 +21,7 @@
|
||||
"smp-protocol": "СМП Протокол",
|
||||
"chat-protocol": "Чат протокол",
|
||||
"donate": "Дарете",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Проект с отворен код",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Проект с отворен код",
|
||||
"simplex-chat-protocol": "SimpleX Чат протокол",
|
||||
"terminal-cli": "Системна конзола",
|
||||
"terms-and-privacy-policy": "Политика за поверителност",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"smp-protocol": "SMP protokol",
|
||||
"chat-protocol": "Chat protokol",
|
||||
"donate": "Darovat",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Projekt s otevřeným zdrojovým kódem",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Projekt s otevřeným zdrojovým kódem",
|
||||
"simplex-chat-protocol": "SimpleX Chat protokol",
|
||||
"terminal-cli": "Terminálové rozhraní příkazového řádku",
|
||||
"terms-and-privacy-policy": "Ochrana soukromí",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"smp-protocol": "SMP-Protokoll",
|
||||
"chat-bot-example": "Beispiel für einen Chatbot",
|
||||
"donate": "Spenden",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Open-Source-Projekt",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Open-Source-Projekt",
|
||||
"chat-protocol": "Chat-Protokoll",
|
||||
"simplex-chat-protocol": "SimpleX-Chat-Protokoll",
|
||||
"terminal-cli": "Terminal-Kommandozeilen-Schnittstelle",
|
||||
@@ -283,7 +283,7 @@
|
||||
"index-token-h2": "Communities, die Bestand haben",
|
||||
"index-token-p1": "Sie werden Ihre Lieblingsgruppen mit zukünftigen Community-Gutscheinen unterstützen.",
|
||||
"index-token-p2": "Server werden mit Gutscheinen bezahlt, damit Ihre Communities kostenlos und unabhängig bleiben können.",
|
||||
"index-token-cta": "Erfahren Sie mehr und <strong>holen Sie sich Ihren kostenlosen NFT</strong><br>für 20 % Rabatt auf SimpleX-Token!",
|
||||
"index-token-cta": "Erfahren Sie mehr und <strong>holen Sie sich Ihren kostenlosen NFT</strong><br>zum frühzeitigen ausprobieren.",
|
||||
"index-roadmap-h2": "SimpleX - Roadmap zum freien Internet",
|
||||
"index-roadmap-2025": "2025",
|
||||
"index-roadmap-2025-title": "Skalierung auf große Communities",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"reference": "Reference",
|
||||
"blog": "Blog",
|
||||
"features": "Features",
|
||||
"navbar-token": "Token",
|
||||
"why-simplex": "Why SimpleX",
|
||||
"simplex-privacy": "SimpleX privacy",
|
||||
"simplex-network": "SimpleX network",
|
||||
@@ -22,7 +23,7 @@
|
||||
"smp-protocol": "SMP protocol",
|
||||
"chat-protocol": "Chat protocol",
|
||||
"donate": "Donate",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Open-Source Project",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Open-Source Project",
|
||||
"simplex-chat-protocol": "SimpleX Chat protocol",
|
||||
"terminal-cli": "Terminal CLI",
|
||||
"about-and-contact-us": "About & Contact us",
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"simplex-explained-tab-3-p-2": "El usuario puede mejorar aún más la privacidad de sus metadatos, haciendo uso de la red Tor para acceder a los servidores, evitando así la correlación por dirección IP.",
|
||||
"smp-protocol": "Protocolo SMP",
|
||||
"donate": "Donación",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Proyecto de Código Abierto",
|
||||
"simplex-chat-protocol": "Protocolo de SimpleX Chat",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Proyecto de Código Abierto",
|
||||
"simplex-chat-protocol": "Protocolo SimpleX Chat",
|
||||
"terms-and-privacy-policy": "Política de Privacidad",
|
||||
"hero-header": "Privacidad redefinida",
|
||||
"hero-overlay-1-textlink": "¿Por qué los ID de usuario son perjudiciales para la privacidad?",
|
||||
@@ -267,37 +267,37 @@
|
||||
"index-security-assessment-title": "Auditorías de seguridad",
|
||||
"index-security-review-2022-title": "Auditoría de Seguridad 2022",
|
||||
"index-security-review-2024-title": "Auditoría de Seguridad 2024",
|
||||
"index-security-audits-label": "Auditorías en<br>Seguridad",
|
||||
"index-security-audits-label": "Auditorías<br>en Seguridad",
|
||||
"index-publications-privacy-guides-title": "Recomendaciones de mensajería de Privacy Guides",
|
||||
"index-publications-whonix-title": "Recomendación de mensajería de Whonix",
|
||||
"index-publications-heise-title": "Publicaciones Heise Online",
|
||||
"index-publications-kuketz-title": "Revisión por Mike Kuketz",
|
||||
"index-publications-optout-title": "Entrevista en podcast de OptOut",
|
||||
"worlds-most-secure-messaging": "Mensajería Más Segura Del Mundo",
|
||||
"index-messaging-p1": "Le mensajería SimpleX dispone de cifrado de extremo a extremo de vanguardia.",
|
||||
"index-messaging-p1": "La mensajería SimpleX posee un cifrado de extremo a extremo de vanguardia.",
|
||||
"index-messaging-p2": "Para tu seguridad y privacidad los servidores no pueden ver tus mensajes <span>ni con quién te comunicas</span>.",
|
||||
"index-messaging-cta": "Descubre más sobre la mensajería SimpleX",
|
||||
"index-nextweb-h2": "Tu Posees<br>La Próxima Web",
|
||||
"index-nextweb-h2": "La Web<br>Del Futuro<br>Te Pertenece",
|
||||
"index-nextweb-p1": "SimpleX se funda en la creencia de que <span>debes ser el propietario de tu identidad, contactos y comunidades</span>.",
|
||||
"index-nextweb-p2": "Una red abierta y descentralizada que te permite conectarte con personas y compartir ideas: libre y segura.",
|
||||
"index-token-h2": "Comunidades Duraderas",
|
||||
"index-token-p1": "Podrás apoyar a tus grupos favoritos con los futuros Vales Comunitarios.",
|
||||
"index-token-p2": "Los vales costearán los servidores para que tus comunidades sigan siendo libres e independientes.",
|
||||
"index-token-cta": "Descubre más y <strong>obtén tu NFT gratuito</strong><br>con 20% de descuento en SimpleX Token!",
|
||||
"index-roadmap-h2": "Ruta de SimpleX hacía el Internet Libre",
|
||||
"index-token-cta": "Descubre más y <strong>obtén tu NFT gratuito</strong><br>por participar en las pruebas.",
|
||||
"index-roadmap-h2": "Ruta SimpleX hacía el Internet Libre",
|
||||
"index-roadmap-2025": "2025",
|
||||
"index-roadmap-2025-title": "Escalar a Comunidades Grandes",
|
||||
"index-roadmap-2025-desc": "Huyendo de plataformas centralizadas",
|
||||
"index-roadmap-2025-desc": "Huir de plataformas centralizadas",
|
||||
"index-roadmap-2026": "2026",
|
||||
"index-roadmap-2026-title": "Comunidades y Servidores Sostenibles",
|
||||
"index-roadmap-2026-desc": "Lanzamiento de Vales Comunitarios",
|
||||
"index-roadmap-2027": "2027",
|
||||
"index-roadmap-2027-title": "Haz Crecer Tus Comunidades",
|
||||
"index-roadmap-2027-desc": "Herramientas para promocionarlas",
|
||||
"index-roadmap-2027-desc": "Herramientas para la promoción",
|
||||
"index-directory-h2": "Únete a las Comunidades SimpleX",
|
||||
"index-directory-p1": "Cientos de miles de personas confían ya en la mensajería SimpleX.",
|
||||
"index-directory-p1": "Miles de personas confían ya en la mensajería SimpleX.",
|
||||
"index-directory-p2": "¡Encuentra comunidades en el directorio SimpleX y crea la tuya!",
|
||||
"index-directory-cta": "Ver el Directorio SimpleX",
|
||||
"index-directory-cta": "Ir al Directorio SimpleX",
|
||||
"index-directory-users-group-title": "SimpleX users group",
|
||||
"how-secure-comparison-title": "Comparativa del cifrado de extremo a extremo en los distintos mensajeros",
|
||||
"how-secure-message-padding": "Relleno de mensajes",
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
"simplex-explained-tab-1-p-1": "Voit luoda yhteyshenkilöitä ja ryhmiä sekä käydä kaksisuuntaisia keskusteluja kuten missä tahansa muussa viestisovelluksessa.",
|
||||
"simplex-explained-tab-3-p-1": "Palvelimilla on erilliset anonyymit tunnistetiedot kullekin jonolle, eivätkä ne tiedä, mille käyttäjille ne kuuluvat.",
|
||||
"donate": "Lahjoita",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Avoin projekti",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Avoin projekti",
|
||||
"hero-p-1": "Muissa sovelluksissa on käyttäjätunnuksia: Signal, Matrix, Session, Briar, Jami, Cwtch, jne.<br> SimpleX ei käytä niitä, <strong>ei edes satunnaisia numeroita</strong>.<br> Tämä parantaa yksityisyyttäsi radikaalisti.",
|
||||
"simplex-private-1-title": "2 kerrosta päästä päähän salattua viestintää",
|
||||
"simplex-private-2-title": "Lisäkerros palvelimen salaukselle",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"smp-protocol": "Protocole SMP",
|
||||
"chat-protocol": "Protocole de chat",
|
||||
"donate": "Faire un don",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Projet Open-Source",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Projet Open-Source",
|
||||
"simplex-chat-protocol": "Protocole SimpleX Chat",
|
||||
"terminal-cli": "Terminal CLI",
|
||||
"terms-and-privacy-policy": "Politique de confidentialité",
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"smp-protocol": "פרוטוקול SMP",
|
||||
"chat-protocol": "פרוטוקול צ'אט",
|
||||
"donate": "תרומה",
|
||||
"copyright-label": "© 2020-2025 SimpleX | פרויקט קוד פתוח",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | פרויקט קוד פתוח",
|
||||
"hero-p-1": "לאפליקציות אחרות יש מזהי משתמש: Signal, Matrix, Session, Briar, Jami, Cwtch וכו'.<br> ל-SimpleX אין, <strong>אפילו לא מספרים אקראיים</strong>.<br> זה משפר באופן קיצוני את הפרטיות שלך.",
|
||||
"hero-overlay-2-title": "מדוע מזהי משתמש מזיקים לפרטיות?",
|
||||
"feature-6-title": "שיחות שמע ווידאו<br>מוצפנות מקצה לקצה",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"smp-protocol": "SMP-protokoll",
|
||||
"chat-protocol": "Csevegési protokoll",
|
||||
"donate": "Adományozás",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Nyílt forráskódú projekt",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Nyílt forráskódú projekt",
|
||||
"simplex-chat-protocol": "A SimpleX Chat protokoll",
|
||||
"terminal-cli": "Terminál CLI",
|
||||
"terms-and-privacy-policy": "Adatvédelmi irányelvek",
|
||||
@@ -283,7 +283,7 @@
|
||||
"index-token-h2": "Időtálló közösségek",
|
||||
"index-token-p1": "A jövőben közösségi utalványokkal támogathatja a kedvenc csoportjait.",
|
||||
"index-token-p2": "Az utalványokkal fizetni tudja a kiszolgálókat, hogy a közösségek szabadok és függetlenek maradhassanak.",
|
||||
"index-token-cta": "Tudjon meg többet, és <strong>szerezzen ingyenes NFT-t</strong><br>20% kedvezménnyel a SimpleX Tokenre!",
|
||||
"index-token-cta": "Tudjon meg többet, és <strong>szerezzen ingyenes NFT-t</strong><br>az előzetes tesztelésért.",
|
||||
"index-roadmap-h2": "A SimpleX ütemterve a szabad internethez",
|
||||
"index-roadmap-2025": "2025",
|
||||
"index-roadmap-2025-title": "Skálázódás nagy közösségekre",
|
||||
@@ -305,11 +305,11 @@
|
||||
"how-secure-forward-secrecy": "Kompromittálás előtti titkosságvédelem",
|
||||
"how-secure-break-in-recovery": "Kompromittálás utáni titkosságvédelem",
|
||||
"how-secure-two-factor-key-exchange": "Kétlépcsős kulcscsere",
|
||||
"how-secure-post-quantum-hybrid-crypto": "Hibrid kriptográfia a kvantum számítógépek megjelenése utánra",
|
||||
"how-secure-post-quantum-hybrid-crypto": "Kvantumbiztos, hibrid kriptográfia",
|
||||
"messengers-comparison-section-list-point-1": "A Briar 1024 bájtra, a Signal pedig 160 bájtra kerekítve tölti ki az üzenetek tartalmát",
|
||||
"messengers-comparison-section-list-point-2": "A letagadhatóság nem foglalja magába a kliens és a kiszolgáló közötti kapcsolatot.",
|
||||
"messengers-comparison-section-list-point-3": "Úgy tűnik, hogy a kriptográfiai aláírások használata rontja a letagadhatóságot, de ez tisztázásra szorul.",
|
||||
"messengers-comparison-section-list-point-4": "A többeszközös megvalósítás rontja a dupla racsni kompromittálás utáni biztonságát",
|
||||
"messengers-comparison-section-list-point-5": "A kétlépcsős kulcscsere nem követelmény a biztonsági kód ellenőrzéséhez.",
|
||||
"messengers-comparison-section-list-point-6": "A posztkvantum kulcscsere „ritka” — csak a racsnis lépések egy részét védi."
|
||||
"messengers-comparison-section-list-point-6": "A kvantumbiztos kulcscsere „ritka” — csak a racsnis lépések egy részét védi."
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"simplex-explained-tab-2-text": "2. Bagaimana cara kerjanya",
|
||||
"simplex-chat-protocol": "Protokol SimpleX Chat",
|
||||
"hero-overlay-2-title": "Mengapa ID pengguna buruk untuk privasi?",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Open-Source Project",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Open-Source Project",
|
||||
"simplex-explained-tab-3-text": "3. Apa yang dilihat server",
|
||||
"smp-protocol": "Protokol SMP",
|
||||
"please-use-link-in-mobile-app": "Mohon gunakan tautan di aplikasi seluler",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"simplex-explained-tab-3-p-1": "I server hanno credenziali anonime separate per ogni coda e non sanno a quali utenti appartengano.",
|
||||
"chat-protocol": "Protocollo di chat",
|
||||
"donate": "Dona",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Progetto Open-Source",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Progetto Open-Source",
|
||||
"simplex-chat-protocol": "Protocollo di SimpleX Chat",
|
||||
"terminal-cli": "Terminale CLI",
|
||||
"terms-and-privacy-policy": "Informativa sulla privacy",
|
||||
@@ -283,7 +283,7 @@
|
||||
"index-token-h2": "Comunità fatte per restare",
|
||||
"index-token-p1": "Sosterrai i tuoi gruppi preferiti con futuri buoni comunitari.",
|
||||
"index-token-p2": "I buoni pagheranno i server, per consentire alle tue comunità di rimanere libere e indipendenti.",
|
||||
"index-token-cta": "Scopri di più e <strong>ricevi un NFT gratuito</strong><br>per uno sconto del 20% su SimpleX Token!",
|
||||
"index-token-cta": "Scopri di più e <strong>ricevi un NFT gratuito</strong><br>per provarlo in anticipo.",
|
||||
"index-roadmap-h2": "Tabella di marcia per un internet libero",
|
||||
"index-roadmap-2025": "2025",
|
||||
"index-roadmap-2025-title": "Scalabilità per comunità numerose",
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"chat-protocol": "チャットプロトコル",
|
||||
"chat-bot-example": "チャットボットの例",
|
||||
"donate": "寄付",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Open-Source Project",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Open-Source Project",
|
||||
"hero-p-1": "他のアプリにはユーザー ID があります: Signal、Matrix、Session、Briar、Jami、Cwtch など。<br> SimpleX にはありません。<strong>乱数さえもありません</strong>。<br> これにより、プライバシーが大幅に向上します。",
|
||||
"copy-the-command-below-text": "以下のコマンドをコピーしてチャットで使用します:",
|
||||
"simplex-private-card-9-point-1": "各メッセージ キューは、異なる送信アドレスと受信アドレスを使用してメッセージを一方向に渡します。",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"chat-bot-example": "Chatbot voorbeeld",
|
||||
"smp-protocol": "SMP protocol",
|
||||
"donate": "Doneer",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Open-sourceproject",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Open-sourceproject",
|
||||
"simplex-chat-protocol": "SimpleX Chat protocol",
|
||||
"terminal-cli": "Terminal CLI",
|
||||
"terms-and-privacy-policy": "Privacybeleid",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"smp-protocol": "Protokół SMP",
|
||||
"chat-protocol": "Protokół czatu",
|
||||
"donate": "Darowizna",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Projekt Open-Source",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Projekt Open-Source",
|
||||
"simplex-chat-protocol": "Protokół SimpleX Chat",
|
||||
"terminal-cli": "Terminal CLI",
|
||||
"terms-and-privacy-policy": "Polityka prywatności",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"smp-protocol": "Protocolo SMP",
|
||||
"chat-protocol": "Protocolo de bate-papo",
|
||||
"donate": "Doar",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Projeto de Código Livre",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Projeto de Código Livre",
|
||||
"simplex-chat-protocol": "Protocolo Chat SimpleX",
|
||||
"terminal-cli": "CLI Terminal",
|
||||
"hero-header": "Privacidade redefinida",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"smp-protocol": "Protocolul SMP",
|
||||
"chat-protocol": "Protocol de chat",
|
||||
"donate": "Donează",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Proiect Open-Source",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Proiect Open-Source",
|
||||
"simplex-chat-protocol": "Protocolul SimpleX Chat",
|
||||
"terminal-cli": "Terminal CLI",
|
||||
"terms-and-privacy-policy": "Politică de confidențialitate",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"copy-the-command-below-text": "скопируйте приведенную ниже команду и используйте ее в чате:",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Проект с открытым исходным кодом",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Проект с открытым исходным кодом",
|
||||
"chat-bot-example": "Пример Чат бота",
|
||||
"simplex-private-card-9-point-1": "Каждая очередь сообщений передает сообщения в одном направлении с разными адресами отправки и получения.",
|
||||
"simplex-private-card-1-point-2": "NaCL cryptobox в каждой очереди для предотвращения корреляции трафика между очередями сообщений, в случае компрометированного TLS.",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"smp-protocol": "SMP Protokolü",
|
||||
"chat-protocol": "Sohbet Protokolü",
|
||||
"donate": "Bağış Yap",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Açık Kaynak Projesi",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Açık Kaynak Projesi",
|
||||
"simplex-chat-protocol": "SimpleX Sohbet Protokolü",
|
||||
"terminal-cli": "Terminal Komut Satırı Arayüzü",
|
||||
"terms-and-privacy-policy": "Gizlilik Politikası",
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"smp-protocol": "Протокол SMP",
|
||||
"chat-protocol": "Протокол чату",
|
||||
"donate": "Пожертвувати",
|
||||
"copyright-label": "© 2020-2025 SimpleX | Проект з відкритим кодом",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | Проект з відкритим кодом",
|
||||
"simplex-chat-protocol": "Протокол чату SimpleX",
|
||||
"terminal-cli": "Термінал CLI",
|
||||
"hero-header": "Приватність переосмислена",
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"simplex-chat-protocol": "SimpleX 聊天协议",
|
||||
"smp-protocol": "SMP协议",
|
||||
"chat-protocol": "聊天协议",
|
||||
"copyright-label": "© 2020-2025 SimpleX | 开源项目",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat | 开源项目",
|
||||
"terminal-cli": "命令行程式",
|
||||
"simplex-explained-tab-1-p-1": "您可以创建联系人和群组,并进行双向对话,就像是任何其他即时通讯软件一样。",
|
||||
"hero-p-1": "其他应用——如Signal、Matrix、Session、Briar、Jami、Cwtch 等——都需要用户 ID。<br>而SimpleX 不需要用户ID,连<strong>随机生成</strong>的也不需要。<br>这从根本上改善了您的隐私。",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"simplex-explained-tab-2-p-2": "伺服器僅單向傳遞消息,無法全面瞭解使用者的對話記錄或連接。",
|
||||
"simplex-explained-tab-2-p-1": "對於每個連接,您可以使用兩個單獨的消息佇列通過不同的伺服器發送和接收消息。",
|
||||
"chat-protocol": "聊天協定",
|
||||
"copyright-label": "© 2020-2025 SimpleX |開源專案",
|
||||
"copyright-label": "© 2020-2025 SimpleX Chat |開源專案",
|
||||
"donate": "捐助",
|
||||
"simplex-explained-tab-1-p-1": "你可以建立聯絡人和群組,並進行雙向對話,就像在任何其他即時通訊軟件中一樣。",
|
||||
"simplex-explained-tab-1-p-2": "它如何在沒有使用者個人檔案識別符的情況下使用單向佇列?",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"@11ty/eleventy-plugin-rss": "^1.2.0",
|
||||
"@simplex-chat/webrtc": "^0.1.1",
|
||||
"common-tags": "^1.8.2",
|
||||
"ethers": "^6.15.0",
|
||||
"fast-uri": "^2.1.0",
|
||||
"markdown-it-anchor": "^8.6.4",
|
||||
"markdown-it-replace-link": "^1.1.0",
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
|
||||
{% include "footer.html" %}
|
||||
|
||||
<script src="/js/animation.js"></script>
|
||||
<script src="/js/swiper-bundle.min.js"></script>
|
||||
<script src="/js/script.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -24,7 +24,191 @@
|
||||
<link id="prism-theme" rel="stylesheet" href="/css/prism-light.min.css"/>
|
||||
<link href="/css/style.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="/css/design3-nav.css">
|
||||
<script src="/js/ethers.umd.min.js"></script>
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
<style>
|
||||
.dark #nft-app-content {
|
||||
color: white;
|
||||
}
|
||||
#nft-app-content .nft-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#nft-app-content .metadata p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
#nft-app-content .account-info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#nft-app-content .switch-link {
|
||||
background: none;
|
||||
border: none;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
#nft-app-content .btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
#nft-app-content a {
|
||||
cursor: pointer;
|
||||
}
|
||||
#nft-app-content .btn-primary {
|
||||
background-color: rgb(0, 83, 208);
|
||||
color: white;
|
||||
}
|
||||
.dark #nft-app-content .btn-primary {
|
||||
background-color: rgb(112, 240, 249);
|
||||
color: rgb(13, 14, 18);
|
||||
}
|
||||
#nft-app-content #connect.btn-primary {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#nft-app-content .btn-large {
|
||||
padding: 10px 25px;
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#nft-app-content .spinner {
|
||||
border: 4px solid rgba(0, 0, 0, 0.1);
|
||||
border-left-color: #007bff;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: progress_spin 1s linear infinite;
|
||||
margin: 20px;
|
||||
}
|
||||
@keyframes progress_spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
#nft-app-content #nft-image-container {
|
||||
min-width: 420px;
|
||||
min-height: 420px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#nft-app-content .nft-image {
|
||||
max-width: 420px;
|
||||
display: block;
|
||||
}
|
||||
#nft-app-content .nft-id-overlay {
|
||||
position: absolute;
|
||||
top: 6.5%;
|
||||
right: 4%;
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
font-family: "GT-Walsheim", "Manrope", sans-serif;
|
||||
text-align: right;
|
||||
color: #023787;
|
||||
}
|
||||
#nft-app-content #next-nft-image-container {
|
||||
min-width: 200px;
|
||||
min-height: 200px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#nft-app-content .next-nft-image {
|
||||
max-width: 200px;
|
||||
display: block;
|
||||
}
|
||||
#nft-app-content .mint-it-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
font-family: "GT-Walsheim", "Manrope", sans-serif;
|
||||
color: white;
|
||||
}
|
||||
#nft-app-content .congrats {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
font-family: "GT-Walsheim", "Manrope", sans-serif;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#nft-app-content .owned-text,
|
||||
#nft-app-content .next-text {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#nft-app-content .metadata {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
max-width: 80%;
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
#nft-app-content #nft-image-container {
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#nft-app-content .nft-image {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@media (min-width: 960px) {
|
||||
#nft-app-content .nft-display {
|
||||
align-items: flex-start;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#nft-app-content .metadata {
|
||||
text-align: left;
|
||||
justify-content: left;
|
||||
max-width: 420px;
|
||||
}
|
||||
}
|
||||
#nft-app-content .error {
|
||||
color: red;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#nft-app-content .no-metamask {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#nft-app-content .no-metamask img {
|
||||
display: inline;
|
||||
width: 200px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#nft-app-content .trait-line {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.dark #nft-app-content .trait-line {
|
||||
color: lightgray;
|
||||
}
|
||||
#nft-app-content .more-link,
|
||||
#nft-app-content .collapse-link {
|
||||
margin-left: 5px;
|
||||
text-decoration: none;
|
||||
color: darkgray;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-[#F3F6F7] dark:bg-[#0C0B13]">
|
||||
@@ -34,8 +218,587 @@
|
||||
<div class="py-6 md:p-[60px]">{{ content | safe }}</div>
|
||||
</section>
|
||||
|
||||
<div id="mint-simplex-nft" class="overlay hidden fixed top-0 left-0 bottom-0 right-0 before:absolute before:w-full before:h-full bg-transparent before:bg-secondary-bg-light dark:before:bg-primary-bg-dark before:opacity-90 items-center justify-center p-3 md:p-10 z-[10000]">
|
||||
<div class="overlay-card w-full md:max-w-[1276px] bg-white dark:bg-card-bg-dark opacity-100 h-full md:max-h-[720px] z-[10001] rounded-md shadow-[0px_3px_12px_rgba(0,0,0,0.2)] p-6 py-10 sm:p-14 overflow-auto scale-100">
|
||||
<h1 id="nft-app-title" class="text-4xl font-bold text-active-blue mb-6 text-center gradient-text">Mint SimpleX NFT</h1>
|
||||
<div id="nft-app-content"></div>
|
||||
|
||||
<script>
|
||||
window.addEventListener("hashchange", showNftApp);
|
||||
window.addEventListener("load", showNftApp);
|
||||
|
||||
let appState;
|
||||
|
||||
function showNftApp() {
|
||||
if (window.location.hash === "#mint-simplex-nft") {
|
||||
if (!appState) initNftApp();
|
||||
renderNftApp();
|
||||
} else if (nextPollInterval) {
|
||||
clearInterval(nextPollInterval);
|
||||
}
|
||||
}
|
||||
|
||||
const selectedNetwork = 'arbitrum'; // Options: 'ethereum', 'arbitrum', 'hoodi'
|
||||
const networks = {
|
||||
ethereum: {
|
||||
chainId: '0x1',
|
||||
minterAddress: '',
|
||||
explorerHost: 'etherscan.io'
|
||||
},
|
||||
arbitrum: {
|
||||
chainId: '0xa4b1',
|
||||
minterAddress: '0x386E2B9f5d5c17049a1ADaf62928b3f3C834Ff28',
|
||||
explorerHost: 'arbiscan.io'
|
||||
},
|
||||
hoodi: {
|
||||
chainId: '0x88bb0',
|
||||
minterAddress: '0xdaF242166be93A7f9835C2792958F3b6c424F845',
|
||||
explorerHost: 'hoodi.etherscan.io'
|
||||
}
|
||||
};
|
||||
const currentConfig = networks[selectedNetwork];
|
||||
const minterAddress = currentConfig.minterAddress;
|
||||
|
||||
const nftABI = [
|
||||
'function name() view returns (string)',
|
||||
'function balanceOf(address owner) view returns (uint)',
|
||||
'function tokenOfOwnerByIndex(address owner, uint index) view returns (uint)',
|
||||
'function tokenURI(uint tokenId) view returns (string)',
|
||||
'function mintingLocked() view returns (bool)',
|
||||
'function nextTokenId() view returns (uint)',
|
||||
'function nextTokenURI() view returns (string)',
|
||||
'function totalSupply() view returns (uint)'
|
||||
];
|
||||
|
||||
const minterABI = [
|
||||
'function nft() view returns (address)',
|
||||
'function mintCount() view returns (uint)', // not used
|
||||
'function mintStartTime() view returns (uint)',
|
||||
'function mintEndTime() view returns (uint)',
|
||||
'function paused() view returns (bool)',
|
||||
'function mint()'
|
||||
];
|
||||
|
||||
const STATES = {
|
||||
NOT_CONNECTED: 'not_connected',
|
||||
LOADING: 'loading',
|
||||
OWNED: 'owned',
|
||||
NO_NFT: 'no_nft',
|
||||
MINTING: 'minting',
|
||||
MINTED: 'minted',
|
||||
ERROR: 'error'
|
||||
};
|
||||
|
||||
let provider;
|
||||
let signer;
|
||||
let nftContract;
|
||||
let minterContract;
|
||||
let ownedPollInterval;
|
||||
let nextPollInterval;
|
||||
let mintPollInterval;
|
||||
|
||||
function initNftApp() {
|
||||
appState = {
|
||||
state: STATES.NOT_CONNECTED,
|
||||
address: '',
|
||||
nftAddress: '',
|
||||
hasToken: false,
|
||||
tokenId: null,
|
||||
name: 'Mint SimpleX NFT',
|
||||
metadata: null,
|
||||
nextId: null,
|
||||
nextUri: null,
|
||||
nextMetadata: null,
|
||||
totalMinted: null,
|
||||
canMint: true,
|
||||
currentTime: null,
|
||||
mintStartTime: 0,
|
||||
mintEndTime: 0,
|
||||
paused: false,
|
||||
locked: false,
|
||||
openedTraits: new Set(),
|
||||
nextOpenedTraits: new Set(),
|
||||
error: null,
|
||||
lastRetry: null
|
||||
};
|
||||
|
||||
if (window.ethereum) {
|
||||
window.ethereum.on('accountsChanged', async accounts => {
|
||||
if (accounts.length > 0) {
|
||||
await loadAccount(accounts);
|
||||
} else {
|
||||
resetConnection();
|
||||
}
|
||||
renderNftApp();
|
||||
});
|
||||
|
||||
window.ethereum.on('chainChanged', () => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function resetConnection() {
|
||||
appState.error = null;
|
||||
appState.state = STATES.NOT_CONNECTED;
|
||||
appState.address = '';
|
||||
provider = null;
|
||||
signer = null;
|
||||
nftContract = null;
|
||||
minterContract = null;
|
||||
if (ownedPollInterval) clearInterval(ownedPollInterval);
|
||||
ownedPollInterval = null;
|
||||
if (nextPollInterval) clearInterval(nextPollInterval);
|
||||
nextPollInterval = null;
|
||||
}
|
||||
|
||||
async function connectWallet() {
|
||||
if (!window.ethereum) {
|
||||
appState.error = null;
|
||||
appState.state = STATES.NOT_CONNECTED;
|
||||
renderNftApp();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||
await loadAccount(accounts);
|
||||
} catch (error) {
|
||||
handleError(error, connectWallet);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAccount(accounts) {
|
||||
appState.address = accounts[0];
|
||||
provider = new ethers.BrowserProvider(window.ethereum);
|
||||
signer = await provider.getSigner();
|
||||
const network = await provider.getNetwork();
|
||||
|
||||
if (network.chainId.toString(16) !== currentConfig.chainId.slice(2).toLowerCase()) {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: currentConfig.chainId }]
|
||||
});
|
||||
}
|
||||
|
||||
minterContract = new ethers.Contract(minterAddress, minterABI, signer);
|
||||
|
||||
appState.error = null;
|
||||
appState.state = STATES.LOADING;
|
||||
renderNftApp();
|
||||
await loadData();
|
||||
}
|
||||
|
||||
async function switchAccount() {
|
||||
try {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_requestPermissions',
|
||||
params: [{ eth_accounts: {} }]
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, switchAccount);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData() { // TODO rename, make local
|
||||
try {
|
||||
appState.nftAddress = await minterContract.nft();
|
||||
nftContract = new ethers.Contract(appState.nftAddress, nftABI, signer);
|
||||
await checkCanMint();
|
||||
await checkBalance();
|
||||
} catch (error) {
|
||||
handleError(error, loadData);
|
||||
}
|
||||
}
|
||||
|
||||
async function checkCanMint() {
|
||||
appState.locked = await nftContract.mintingLocked();
|
||||
appState.mintStartTime = Number(await minterContract.mintStartTime());
|
||||
appState.mintEndTime = Number(await minterContract.mintEndTime());
|
||||
appState.paused = await minterContract.paused();
|
||||
appState.currentTime = Math.floor(Date.now() / 1000);
|
||||
appState.canMint = !appState.locked
|
||||
&& !appState.paused
|
||||
&& appState.currentTime >= appState.mintStartTime
|
||||
&& (appState.mintEndTime == 0 || appState.currentTime < appState.mintEndTime);
|
||||
}
|
||||
|
||||
async function checkBalance() {
|
||||
appState.name = await nftContract.name();
|
||||
appState.totalMinted = await nftContract.totalSupply();
|
||||
const balance = Number(await nftContract.balanceOf(appState.address));
|
||||
if (balance > 0) {
|
||||
appState.hasToken = true;
|
||||
appState.tokenId = Number(await nftContract.tokenOfOwnerByIndex(appState.address, 0));
|
||||
const uri = await nftContract.tokenURI(appState.tokenId);
|
||||
appState.metadata = await loadMetadata(uri);
|
||||
appState.error = null;
|
||||
appState.state = STATES.OWNED;
|
||||
startOwnedPolling();
|
||||
} else {
|
||||
appState.hasToken = false;
|
||||
await loadNext();
|
||||
appState.error = null;
|
||||
appState.state = STATES.NO_NFT;
|
||||
startNextPolling();
|
||||
}
|
||||
renderNftApp();
|
||||
}
|
||||
|
||||
async function loadNext() {
|
||||
try {
|
||||
appState.totalMinted = await nftContract.totalSupply();
|
||||
appState.nextId = Number(await nftContract.nextTokenId());
|
||||
const newNextUri = await nftContract.nextTokenURI();
|
||||
if (newNextUri !== appState.nextUri) {
|
||||
appState.nextUri = newNextUri;
|
||||
appState.nextMetadata = await loadMetadata(newNextUri);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load next:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceIPFS(uri) {
|
||||
return uri ? uri.replace(/^ipfs:\/\//, 'https://ipfs.io/ipfs/') : '';
|
||||
}
|
||||
|
||||
async function loadMetadata(uri) {
|
||||
let proxyUri = replaceIPFS(uri)
|
||||
const response = await fetch(proxyUri);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
function startOwnedPolling() {
|
||||
if (ownedPollInterval) clearInterval(ownedPollInterval);
|
||||
ownedPollInterval = setInterval(async () => {
|
||||
if (appState.state === STATES.OWNED || appState.state === STATES.MINTED) {
|
||||
const balance = Number(await nftContract.balanceOf(appState.address));
|
||||
const totalMinted = await nftContract.totalSupply();
|
||||
if (balance === 0) {
|
||||
appState.hasToken = false;
|
||||
appState.state = STATES.NO_NFT;
|
||||
appState.openedTraits.clear(); // Optional: Reset owned traits
|
||||
await checkCanMint();
|
||||
await loadNext();
|
||||
renderNftApp();
|
||||
startNextPolling();
|
||||
} else if (appState.totalMinted != totalMinted) {
|
||||
appState.totalMinted = totalMinted;
|
||||
renderNftApp();
|
||||
}
|
||||
} else {
|
||||
clearInterval(ownedPollInterval);
|
||||
ownedPollInterval = null;
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
function startNextPolling() {
|
||||
if (nextPollInterval) clearInterval(nextPollInterval);
|
||||
if (ownedPollInterval) clearInterval(ownedPollInterval);
|
||||
ownedPollInterval = null;
|
||||
nextPollInterval = setInterval(async () => {
|
||||
if (appState.state === STATES.NO_NFT) {
|
||||
await checkCanMint();
|
||||
await loadNext();
|
||||
renderNftApp();
|
||||
} else {
|
||||
clearInterval(nextPollInterval);
|
||||
nextPollInterval = null;
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
async function mintNft() {
|
||||
if (!appState.canMint) return;
|
||||
appState.error = null;
|
||||
appState.state = STATES.MINTING;
|
||||
renderNftApp();
|
||||
try {
|
||||
const tx = await minterContract.mint();
|
||||
await tx.wait();
|
||||
mintPollInterval = setInterval(async () => {
|
||||
try {
|
||||
const balance = Number(await nftContract.balanceOf(appState.address));
|
||||
if (balance > 0) {
|
||||
clearInterval(mintPollInterval);
|
||||
mintPollInterval = null;
|
||||
appState.hasToken = true;
|
||||
appState.tokenId = Number(await nftContract.tokenOfOwnerByIndex(appState.address, 0));
|
||||
const uri = await nftContract.tokenURI(appState.tokenId);
|
||||
appState.totalMinted = await nftContract.totalSupply();
|
||||
appState.metadata = await loadMetadata(uri);
|
||||
appState.error = null;
|
||||
appState.state = STATES.MINTED;
|
||||
renderNftApp();
|
||||
startOwnedPolling();
|
||||
}
|
||||
} catch (error) {
|
||||
// TODO show error
|
||||
console.error('Polling error:', error);
|
||||
}
|
||||
}, 5000);
|
||||
} catch (error) {
|
||||
handleError(error, mintNft);
|
||||
appState.state = STATES.NO_NFT;
|
||||
renderNftApp();
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(error, retryFunc) {
|
||||
console.error(error);
|
||||
appState.error = error.reason || error.message || 'An unknown error occurred';
|
||||
appState.lastRetry = retryFunc;
|
||||
renderNftApp();
|
||||
}
|
||||
|
||||
function renderNftApp() {
|
||||
const title = document.getElementById('nft-app-title');
|
||||
title.innerText = appState.metadata?.name ?? appState.name
|
||||
|
||||
const content = document.getElementById('nft-app-content');
|
||||
content.innerHTML = '';
|
||||
|
||||
if (appState.address) {
|
||||
const ellipsisAddr = appState.address.slice(0, 6) + '...' + appState.address.slice(-4);
|
||||
content.innerHTML += `<div class="account-info"><span>Account: ${ellipsisAddr}</span><a id="switchAccount" class="switch-link">Change</a></div>`;
|
||||
setTimeout(() => document.getElementById('switchAccount').onclick = switchAccount, 0);
|
||||
}
|
||||
|
||||
switch (appState.state) {
|
||||
case STATES.NOT_CONNECTED:
|
||||
if (window.ethereum) {
|
||||
content.innerHTML += `<div style="text-align: center; margin-top: 20px;">
|
||||
<p class="mb-5"><img src="/img/design_3/simplex_nft_smpx.jpg" width="200" style="display: inline; border-radius: 10px;"></p>
|
||||
<button class="btn btn-primary" id="connect">Connect Wallet</button>
|
||||
<p>Arbitrum One network</p>
|
||||
</div>`;
|
||||
setTimeout(() => document.getElementById('connect').onclick = connectWallet, 0);
|
||||
} else {
|
||||
content.innerHTML += `<p class="no-metamask"><img src="/img/design_3/simplex_nft_smpx.jpg"></p>
|
||||
<p class="no-metamask">
|
||||
<a href="https://metamask.io/download/" target="_blank">Install MetaMask wallet</a>
|
||||
</p>`;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATES.LOADING:
|
||||
content.innerHTML += `<div style="display: flex; justify-content: center; align-items: center; margin: 0 auto;">
|
||||
<p>Connecting...</p>
|
||||
<div class="spinner" style="margin-left: 10px;"></div>
|
||||
</div>`;
|
||||
break;
|
||||
|
||||
case STATES.MINTED:
|
||||
case STATES.OWNED:
|
||||
const meta = appState.metadata;
|
||||
let html = `<div class="nft-display">`;
|
||||
if (meta) {
|
||||
title.innerText = `Your ${meta.name}`;
|
||||
if (meta.image) {
|
||||
html += `<div id="nft-image-container">
|
||||
<img src="${replaceIPFS(meta.image)}" alt="NFT Image" class="nft-image">
|
||||
<div class="nft-id-overlay">#${appState.tokenId}</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
html += `<div class="metadata">`;
|
||||
html += appState.state == STATES.MINTED
|
||||
? `<p class="congrats">You minted SimpleX NFT #${appState.tokenId}!</p>`
|
||||
: `<p class="congrats">You have SimpleX NFT #${appState.tokenId}</p>`;
|
||||
if (meta) {
|
||||
if (meta.description) html += `<p>${meta.description}</p>`;
|
||||
html += renderAttributes(meta.attributes, appState.openedTraits);
|
||||
html += '<p>';
|
||||
html += `<strong>${appState.totalMinted} NFTs minted</strong>. `;
|
||||
html += `<a href="https://${currentConfig.explorerHost}/nft/${appState.nftAddress}/${appState.tokenId}" target="_blank"><span>Explorer ${linkSvg}</span></a>`;
|
||||
html += '</p>';
|
||||
}
|
||||
html += `</div></div>`;
|
||||
content.innerHTML += html
|
||||
|
||||
content.innerHTML += renderSignupForm();
|
||||
break;
|
||||
|
||||
case STATES.NO_NFT:
|
||||
if (appState.canMint) {
|
||||
title.innerText = `${appState.nextMetadata?.name}`;
|
||||
content.innerHTML += renderNextNFT() + renderMintButton();
|
||||
} else {
|
||||
content.innerHTML += renderNextNFT(false) + renderCannotMint() + renderSignupForm();
|
||||
}
|
||||
break;
|
||||
|
||||
case STATES.MINTING:
|
||||
content.innerHTML += renderNextNFT();
|
||||
content.innerHTML += `<div style="display: flex; justify-content: center; align-items: center; margin: 0 auto;">
|
||||
<p>Minting... Please wait.</p>
|
||||
<div class="spinner" style="margin-left: 10px;"></div>
|
||||
</div>`;
|
||||
break;
|
||||
}
|
||||
if (appState.error) {
|
||||
content.innerHTML += renderError();
|
||||
}
|
||||
}
|
||||
|
||||
const linkSvg = '<svg style="display: inline-block;" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg>'
|
||||
|
||||
function renderNextNFT(withAttributes = true) {
|
||||
let html = '';
|
||||
const nextMeta = appState.nextMetadata;
|
||||
if (nextMeta) {
|
||||
html += `<div class="nft-display">`;
|
||||
if (nextMeta.image) {
|
||||
const img = `<img src="${replaceIPFS(nextMeta.image)}" alt="Next NFT Image" class="next-nft-image">`;
|
||||
if (appState.canMint && appState.state == STATES.NO_NFT) {
|
||||
html += `<div id="next-nft-image-container" style="cursor: pointer">
|
||||
${img}
|
||||
<div class="mint-it-overlay">Mint it</div>
|
||||
</div>`;
|
||||
setTimeout(() => document.getElementById('next-nft-image-container').onclick = mintNft, 0);
|
||||
} else {
|
||||
html += `<div id="next-nft-image-container">${img}</div>`;
|
||||
}
|
||||
}
|
||||
html += `<div class="metadata">`;
|
||||
if (nextMeta.description) html += `<p>${nextMeta.description}</p>`;
|
||||
html += '<p>';
|
||||
html += `<strong>${appState.totalMinted} NFTs minted</strong>. `;
|
||||
html += `<a href="https://${currentConfig.explorerHost}/token/${appState.nftAddress}" target="_blank"><span>Explorer ${linkSvg}</span></a>`;
|
||||
html += '</p>';
|
||||
if (withAttributes) {
|
||||
html += renderAttributes(nextMeta.attributes, appState.nextOpenedTraits);
|
||||
}
|
||||
html += `</div></div>`;
|
||||
}
|
||||
return html
|
||||
}
|
||||
|
||||
function renderMintButton() {
|
||||
let html = '<div style="text-align: center; margin-top: 20px;">';
|
||||
html += `<button class="btn btn-primary btn-large" id="mintBtn">Mint SimpleX NFT #${appState.nextId}</button>`;
|
||||
if (appState.mintEndTime > 0) {
|
||||
const endDate = new Date(appState.mintEndTime * 1000).toLocaleString();
|
||||
html += `<p>Until ${endDate}</p>`;
|
||||
}
|
||||
html += '</div>';
|
||||
setTimeout(() => document.getElementById('mintBtn').onclick = mintNft, 0);
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderCannotMint() {
|
||||
let html = '<div style="text-align: center; margin-top: 20px; font-size: 1.5em;">';
|
||||
if (appState.locked) {
|
||||
html += `<p>NFT minting ended.</p>`;
|
||||
} else if (appState.paused) {
|
||||
html += `<p>NFT minting is temporarily paused.</p>`;
|
||||
} else {
|
||||
if (appState.currentTime > appState.mintEndTime && appState.mintEndTime > 0) {
|
||||
const endDate = new Date(appState.mintEndTime * 1000).toLocaleString();
|
||||
html += `<p>Minting ended on ${endDate}.</p>`;
|
||||
} else if (appState.currentTime <= appState.mintStartTime) {
|
||||
const startDate = new Date(appState.mintStartTime * 1000).toLocaleString();
|
||||
html += `<p>Minting will start on ${startDate}.</p>`;
|
||||
}
|
||||
}
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderAttributes(attributes, openedTraits) {
|
||||
let html = '';
|
||||
if (Array.isArray(attributes)) {
|
||||
attributes.forEach((attr, i) => {
|
||||
if (attr.trait_type && attr.value) {
|
||||
const isOpened = openedTraits.has(String(i));
|
||||
let truncatedValue = attr.value;
|
||||
html += `<p class="trait-line" data-key="${i}">`
|
||||
if (attr.value.length > 41) {
|
||||
truncatedValue = attr.value.slice(0, 41) + '...';
|
||||
html += `<span class="trait-type">${attr.trait_type}: </span>
|
||||
<span class="truncated-value" style="display: ${isOpened ? 'none' : 'inline'};">${truncatedValue}</span>
|
||||
<a href="#" class="more-link" style="display: ${!isOpened ? 'inline' : 'none'};">More</a>
|
||||
<span class="full-value" style="display: ${isOpened ? 'inline' : 'none'};">${attr.value}</span>
|
||||
<a href="#" class="collapse-link" style="display: ${isOpened ? 'inline' : 'none'};">◀</a>`;
|
||||
} else {
|
||||
html += `<span class="trait-type">${attr.trait_type}: </span>
|
||||
<span class="full-value">${attr.value}</span>`;
|
||||
}
|
||||
html += '</p>';
|
||||
}
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.trait-line').forEach(line => {
|
||||
const key = line.dataset.key;
|
||||
const truncated = line.querySelector('.truncated-value');
|
||||
const more = line.querySelector('.more-link');
|
||||
const full = line.querySelector('.full-value');
|
||||
const collapse = line.querySelector('.collapse-link');
|
||||
if (more) {
|
||||
more.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
truncated.style.display = 'none';
|
||||
more.style.display = 'none';
|
||||
full.style.display = 'inline';
|
||||
collapse.style.display = 'inline';
|
||||
openedTraits.add(key);
|
||||
});
|
||||
}
|
||||
if (collapse) {
|
||||
collapse.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
full.style.display = 'none';
|
||||
collapse.style.display = 'none';
|
||||
truncated.style.display = 'inline';
|
||||
more.style.display = 'inline';
|
||||
openedTraits.delete(key);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderSignupForm() {
|
||||
return `<form class="flex items-center w-full max-w-[540px]" action="https://chat.us2.list-manage.com/subscribe/post?u=ddd892b258ae36e5438e6d4e1&id=ad6037a2fe" method="post" target="_blank" novalidate="" style="margin: 20px auto 0;">
|
||||
<input name="EMAIL" type="text" class="h-[44px] ltr:rounded-l-[34px] rtl:rounded-r-[34px] bg-transparent border border-primary-light dark:border-primary-dark focus:outline-none text-primary-light dark:text-primary-dark text-base w-full max-w-[400px] px-5 placeholder:text-grey-black placeholder:dark:text-white placeholder:text-base placeholder:font-normal placeholder:tracking-[0.01em]" placeholder="Enter email address for updates">
|
||||
<span aria-hidden="true" class="hidden">
|
||||
<input type="text" name="b_ddd892b258ae36e5438e6d4e1_ad6037a2fe" tabindex="-1" value="">
|
||||
</span>
|
||||
<input type="submit" class="h-[44px] ltr:rounded-r-[34px] rtl:rounded-l-[34px] bg-primary-light dark:bg-primary-dark text-white dark:text-black text-center px-8">
|
||||
</form>`;
|
||||
}
|
||||
|
||||
function renderError() {
|
||||
setTimeout(() => document.getElementById('retry_after_error').onclick = async () => {
|
||||
appState.error = null;
|
||||
appState.state = STATES.LOADING; // Or keep current state, but reload data
|
||||
renderNftApp();
|
||||
try {
|
||||
await appState.lastRetry();
|
||||
} catch (error) {
|
||||
handleError(error, appState.lastRetry);
|
||||
}
|
||||
}, 0);
|
||||
return `<div style="text-align: center; margin-top: 20px;">
|
||||
<span class="error">Error: ${appState.error}</span> <a id="retry_after_error">Retry</a>
|
||||
</div>`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svg class="close-overlay-btn" id="cross" width="16" height="16" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.7973 11.5525L7.59762 6.49833L12.7947 1.44675C13.055 1.19371 13.0658 0.771991 12.8188 0.505331C12.5718 0.238674 12.1602 0.227644 11.8999 0.480681L6.65343 5.58028L1.09979 0.182228C0.839522 -0.070157 0.427909 -0.059127 0.18094 0.207531C-0.0660305 0.474191 -0.0552645 0.895911 0.205003 1.14894L5.70862 6.49833L0.20247 11.851C-0.0577975 12.104 -0.0685635 12.5257 0.178407 12.7924C0.306324 12.9306 0.477936 13 0.650181 13C0.811033 13 0.971873 12.9397 1.09726 12.817L6.65343 7.41639L11.9025 12.5186C12.0285 12.6406 12.1893 12.7015 12.3495 12.7015C12.5218 12.7015 12.6934 12.6321 12.8213 12.4939C13.0689 12.2273 13.0582 11.8062 12.7973 11.5525Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "footer.html" %}
|
||||
<script src="/js/prism.min.js"></script>
|
||||
</body>
|
||||
<script src="/js/swiper-bundle.min.js"></script>
|
||||
<script src="/js/script.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
{% endfor %}
|
||||
|
||||
<header id="navbar">
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo flex dark:hidden ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-light.png" alt="logo" /></a>
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo hidden dark:flex ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-dark.png" alt="logo" /></a>
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo flex dark:hidden ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-light.png" alt="logo"></a>
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo hidden dark:flex ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-dark.png" alt="logo"></a>
|
||||
|
||||
<nav id="menu">
|
||||
<ul>
|
||||
@@ -111,6 +111,9 @@
|
||||
</nav>
|
||||
|
||||
<div class="right-links">
|
||||
<a class="box-btn token md:hidden" href="/token">
|
||||
{{ 'navbar-token' | i18n({}, lang) }}
|
||||
</a>
|
||||
<button href="#" class="theme-switch-btn">
|
||||
<svg class="sun" width="469" height="469" viewBox="0 0 469 469" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M225.768 1.44594C220.301 3.84594 214.701 10.6459 213.768 15.9793C213.234 18.3793 213.101 31.7126 213.368 45.7126L213.768 71.1793L217.634 76.1126C226.701 88.1126 242.968 87.5793 252.301 75.0459C254.968 71.5793 255.101 69.5793 255.101 42.2459C255.101 14.9126 254.968 12.9126 252.301 9.44594C250.834 7.44594 248.034 4.51261 246.168 3.17928C241.768 -0.154057 231.101 -1.08739 225.768 1.44594Z"/>
|
||||
@@ -150,14 +153,10 @@
|
||||
<li>
|
||||
{% if language.flag %}
|
||||
<a href="{% completeRoute {url:page.url,lang:language.label} %}" class="flag-anchor">
|
||||
<img src="{{ language.flag }}" alt="" srcset="">
|
||||
<p>{{ language.name }}</p>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% completeRoute {url:page.url,lang:language.label} %}" class="flag-anchor">
|
||||
<div style="background-color:{{ language.iconBg }}; color:{{ language.textColor }}; width:21.33px; height:16px">
|
||||
<p style="text-align:center; font-size:7px">{{ language.iconText }}</p>
|
||||
</div>
|
||||
<p>{{ language.name }}</p>
|
||||
</a>
|
||||
{% endif %}
|
||||
@@ -171,14 +170,10 @@
|
||||
<li>
|
||||
{% if language.flag %}
|
||||
<a href="{% completeRoute {url:page.url,lang:language.label} %}" class="flag-anchor">
|
||||
<img src="{{ language.flag }}" alt="" srcset="">
|
||||
<p>{{ language.name }}</p>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% completeRoute {url:page.url,lang:language.label} %}" class="flag-anchor">
|
||||
<div style="background-color:{{ language.iconBg }}; color:{{ language.textColor }}; width:21.33px; height:16px">
|
||||
<p style="text-align:center; font-size:7px">{{ language.iconText }}</p>
|
||||
</div>
|
||||
<p>{{ language.name }}</p>
|
||||
</a>
|
||||
{% endif %}
|
||||
@@ -274,11 +269,14 @@ window.addEventListener('click',(e)=>{
|
||||
}
|
||||
}
|
||||
else if(e.target.closest('.nav-toggle-btn')){
|
||||
document.body.classList.toggle('lock-scroll');
|
||||
if(nav.classList.contains('open')){
|
||||
nav.classList.remove('open');
|
||||
document.getElementById('mobile-header').classList.remove('nav-open');
|
||||
document.documentElement.classList.remove('lock-scroll');
|
||||
}
|
||||
else{
|
||||
document.documentElement.classList.add('lock-scroll');
|
||||
document.getElementById('mobile-header').classList.add('nav-open');
|
||||
nav.classList.add('open');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ body.change-nav-color {
|
||||
--nav-color: #001796;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
@media screen and (min-width: 960px) {
|
||||
.logo {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
@@ -70,14 +70,15 @@ header#navbar {
|
||||
header#navbar>a.logo {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
padding-left: 16px;
|
||||
padding-left: 30px;
|
||||
padding-top: 2px;
|
||||
height: 100%;
|
||||
/* display: flex; */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header#navbar>a.logo img {
|
||||
height: 32px;
|
||||
height: 40px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@@ -102,12 +103,24 @@ header#navbar ul {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 49px;
|
||||
gap: 60px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1279px) {
|
||||
header#navbar ul {
|
||||
gap: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
header#navbar ul {
|
||||
gap: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
header#navbar ul a {
|
||||
font-family: "Manrope", "GT-Walsheim", sans-serif;
|
||||
font-family: "Manrope", sans-serif;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
@@ -121,6 +134,60 @@ header#navbar ul a {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 960px) {
|
||||
header#navbar ul .nav-link a,
|
||||
header#navbar ul .nav-link-text {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
header#navbar ul .nav-link .sub-menu a {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.box-btn {
|
||||
height: 36px;
|
||||
padding: 0 11px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 21px !important;
|
||||
font-family: 'Manrope', sans-serif !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: -0.25px !important;
|
||||
border-radius: 6px !important;
|
||||
}
|
||||
|
||||
.box-btn.btn-1 {
|
||||
background-color: #639bd9;
|
||||
}
|
||||
|
||||
.dark .box-btn.btn-1 {
|
||||
background-color: #44547D;
|
||||
}
|
||||
|
||||
.box-btn.token {
|
||||
height: 34px;
|
||||
font-size: 17px !important;
|
||||
font-weight: 500 !important;
|
||||
background: linear-gradient(90deg, #019bfe 0%, #2e3fa0 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.box-btn.gold-gradient,
|
||||
.dark .box-btn.token {
|
||||
background: linear-gradient(90deg,
|
||||
#ffb55d 0%,
|
||||
#fff494 50%,
|
||||
#fbffdd 100%);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.change-nav-color .box-btn.token {
|
||||
background: linear-gradient(90deg, #019bfe 0%, #2e3fa0 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
header#navbar ul a span {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
@@ -143,7 +210,7 @@ header#navbar ul a span svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
@media screen and (min-width: 960px) {
|
||||
header#navbar nav#menu>ul>li>a {
|
||||
color: var(--nav-color);
|
||||
}
|
||||
@@ -245,7 +312,7 @@ header#navbar .nav-link:focus-within .sub-menu {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
@media screen and (min-width: 960px) {
|
||||
.flag-container>a {
|
||||
color: var(--nav-color) !important;
|
||||
}
|
||||
@@ -256,7 +323,8 @@ header#navbar .nav-link:focus-within .sub-menu {
|
||||
}
|
||||
|
||||
.flag-container>a p {
|
||||
font-size: 17px;
|
||||
font-size: 14px;
|
||||
font-family: "Manrope", sans-serif;
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
@@ -287,7 +355,7 @@ header#navbar button.theme-switch-btn {
|
||||
}
|
||||
|
||||
header#navbar button.theme-switch-btn svg {
|
||||
width: 18px;
|
||||
width: 20px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -300,7 +368,7 @@ header#navbar button.theme-switch-btn svg path {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
@media screen and (min-width: 960px) {
|
||||
header#navbar button.theme-switch-btn svg path {
|
||||
fill: var(--nav-color) !important;
|
||||
transition: all 0.3s ease;
|
||||
@@ -311,7 +379,7 @@ header#navbar button.theme-switch-btn svg path {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0px;
|
||||
padding-right: 16px;
|
||||
padding-right: 20px;
|
||||
height: 54px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -362,7 +430,7 @@ button#cross-btn {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 949px) {
|
||||
@media (max-width: 959px) {
|
||||
#mobile-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -538,4 +606,37 @@ button#cross-btn {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
#mobile-header.cover {
|
||||
background: #d7fbfe09;
|
||||
}
|
||||
|
||||
.dark #mobile-header.cover {
|
||||
background: #283b630a;
|
||||
}
|
||||
|
||||
#mobile-header.main {
|
||||
background: #ffefd60e;
|
||||
}
|
||||
|
||||
.dark #mobile-header.main {
|
||||
background: #fff6e00a;
|
||||
}
|
||||
|
||||
#mobile-header.nav-open {
|
||||
background: #ffffff0c;
|
||||
}
|
||||
|
||||
.dark #mobile-header.nav-open {
|
||||
background: #0a0f2b0f;
|
||||
}
|
||||
|
||||
#mobile-header.footer {
|
||||
background: #d3e9ff11;
|
||||
}
|
||||
|
||||
.dark #mobile-header.footer {
|
||||
background: #080d250e;
|
||||
--nav-color: #ffffff;
|
||||
}
|
||||
@@ -171,7 +171,7 @@
|
||||
#fff6e0 90%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 949px) {
|
||||
@media screen and (max-width: 959px) {
|
||||
:root {
|
||||
--bg-grad: linear-gradient(30deg,
|
||||
#e8f3ff 0%,
|
||||
@@ -218,15 +218,15 @@ html {
|
||||
|
||||
html,
|
||||
body {
|
||||
background: #ccf9fc;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.dark html,
|
||||
.dark body {
|
||||
background: #2d3f64;
|
||||
background: #0a0f2b;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
@media screen and (min-width: 960px) {
|
||||
html {
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
@@ -301,13 +301,21 @@ p {
|
||||
}
|
||||
|
||||
/* Make the viewport the scroll snap container (works in iOS Safari) */
|
||||
html,
|
||||
body {
|
||||
html {
|
||||
scroll-snap-type: y mandatory;
|
||||
scroll-behavior: smooth;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
html.lock-scroll {
|
||||
scroll-snap-type: none !important;
|
||||
}
|
||||
|
||||
.lock-scroll body {
|
||||
overflow: hidden !important;
|
||||
height: 100svh;
|
||||
}
|
||||
|
||||
section.page {
|
||||
min-height: 100svh;
|
||||
/* fallback */
|
||||
@@ -421,7 +429,7 @@ section.cover div.content p {
|
||||
.publications-btns {
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
left: 16px;
|
||||
left: 30px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
@@ -430,7 +438,15 @@ section.cover div.content p {
|
||||
}
|
||||
|
||||
.publications-btns img {
|
||||
height: 36px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.publications-btns img.kuketz {
|
||||
width: 38px;
|
||||
}
|
||||
|
||||
.publications-btns img.whonix {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.security-btns {
|
||||
@@ -449,30 +465,6 @@ section.cover div.content p {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.box-btn {
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px !important;
|
||||
font-family: 'Manrope', 'GT-Walsheim', sans-serif !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: -0.25px !important;
|
||||
border-radius: 6px !important;
|
||||
}
|
||||
|
||||
.box-btn.btn-1 {
|
||||
background-color: #639bd9;
|
||||
}
|
||||
|
||||
.box-btn.btn-2 {
|
||||
background: linear-gradient(100deg,
|
||||
#ffb55d 0%,
|
||||
#fff494 50%,
|
||||
#fbffdd 100%);
|
||||
}
|
||||
|
||||
.security-audits {
|
||||
font-size: 14px !important;
|
||||
font-family: 'Manrope', 'GT-Walsheim', sans-serif !important;
|
||||
@@ -482,7 +474,7 @@ section.cover div.content p {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1280px) {
|
||||
@media screen and (max-width: 1279px) {
|
||||
.publications-btns {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
@@ -497,7 +489,7 @@ section.cover div.content p {
|
||||
.socials {
|
||||
position: absolute;
|
||||
bottom: 22px;
|
||||
right: 16px;
|
||||
right: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -521,27 +513,26 @@ section.cover div.content p {
|
||||
background-color: #000;
|
||||
border: #4f4f4f 1px solid;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
padding: 6px 10px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.desktop-app-btn .btn-content p {
|
||||
margin: 0;
|
||||
font-size: 10px !important;
|
||||
font-size: 11px !important;
|
||||
font-family: 'Manrope', 'GT-Walsheim', sans-serif !important;
|
||||
font-weight: 300 !important;
|
||||
line-height: 1.2 !important;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.desktop-app-btn .btn-content img {
|
||||
height: 22px !important;
|
||||
height: 24px !important;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@@ -549,10 +540,10 @@ section.cover div.content p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 949px) {
|
||||
@media screen and (max-width: 959px) {
|
||||
|
||||
.publications-btns,
|
||||
.security-btns,
|
||||
@@ -633,7 +624,7 @@ main .section-bg {
|
||||
background-image: url("/img/design_3/section-5-desktop.webp");
|
||||
}
|
||||
|
||||
@media screen and (max-width: 949px) {
|
||||
@media screen and (max-width: 959px) {
|
||||
.page-2 {
|
||||
background-image: url("/img/design_3/section-2-mobile-light.webp");
|
||||
background-size: auto calc(var(--cover-bg-h) * 0.84);
|
||||
@@ -845,7 +836,7 @@ main .section-bg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 949px) {
|
||||
@media (max-width: 959px) {
|
||||
section.cover div.content {
|
||||
gap: calc(var(--sec-vhu) * 2.5);
|
||||
transform: translateY(calc(var(--sec-vhu) * 5));
|
||||
@@ -975,7 +966,7 @@ main .section-bg {
|
||||
border-radius: calc(var(--sec-vhu) * 1.8);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 949px) {
|
||||
@media screen and (max-width: 959px) {
|
||||
.page-6 .group-images {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
@@ -998,14 +989,6 @@ main .section-bg {
|
||||
<img class="pos" style="--c:3; --r:2; --w:2; --h:2" src="..." />
|
||||
places the image starting at column 3, row 2, spanning 2 columns × 2 rows.
|
||||
*/
|
||||
.page-6 .group-images>a.simplex-image {
|
||||
--c: 4;
|
||||
--r: 3;
|
||||
--w: 2;
|
||||
--h: 2;
|
||||
margin: 1.15rem;
|
||||
}
|
||||
|
||||
.page-6 .group-images>a.group-image:nth-child(1) {
|
||||
--c: 2;
|
||||
--r: 1;
|
||||
@@ -1188,14 +1171,15 @@ main .section-bg {
|
||||
--h: 1
|
||||
}
|
||||
|
||||
@media screen and (max-width: 949px) {
|
||||
.page-6 .group-images>a.simplex-image {
|
||||
--c: 4;
|
||||
--r: 4;
|
||||
--w: 2;
|
||||
--h: 2
|
||||
}
|
||||
.page-6 .group-images>a.simplex-image {
|
||||
--c: 4;
|
||||
--r: 3;
|
||||
--w: 2;
|
||||
--h: 2;
|
||||
margin: calc(var(--sec-vhu) * 2);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 959px) {
|
||||
.page-6 .group-images>a.group-image:nth-child(1) {
|
||||
--c: 3;
|
||||
--r: 1;
|
||||
@@ -1377,6 +1361,14 @@ main .section-bg {
|
||||
--w: 1;
|
||||
--h: 1
|
||||
}
|
||||
|
||||
.page-6 .group-images>a.simplex-image {
|
||||
--c: 4;
|
||||
--r: 4;
|
||||
--w: 2;
|
||||
--h: 2;
|
||||
margin: calc(var(--sec-vhu) * 1);
|
||||
}
|
||||
}
|
||||
|
||||
.page-6 .group-images a {
|
||||
@@ -1391,7 +1383,7 @@ section.page.footer {
|
||||
min-height: fit-content;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 950px) {
|
||||
@media screen and (max-width: 959px) {
|
||||
section.page.footer {
|
||||
height: 100lvh;
|
||||
min-height: 100lvh;
|
||||
@@ -1426,7 +1418,7 @@ section.page.footer {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 950px) {
|
||||
@media (min-width: 960px) {
|
||||
|
||||
.footer .container-md,
|
||||
.footer .container-sm,
|
||||
|
||||
@@ -264,6 +264,15 @@ a{
|
||||
text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.dark .gradient-text {
|
||||
background: -webkit-linear-gradient(to bottom, #70F0F9 100%, #70F0F9 100%);
|
||||
background: linear-gradient(to bottom, #70F0F9 100%, #70F0F9 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.dark .border-gradient {
|
||||
background:
|
||||
linear-gradient(#11182F, #11182F) padding-box,
|
||||
@@ -304,143 +313,6 @@ a{
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* .menu-link{
|
||||
font-size: 16px;
|
||||
line-height: 33.42px;
|
||||
color: #0D0E12;
|
||||
}
|
||||
.dark .menu-link{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-link ul li a.active{
|
||||
color: #0053D0;
|
||||
|
||||
}
|
||||
.dark .nav-link ul li a.active{
|
||||
color: #66D9E2;
|
||||
}
|
||||
|
||||
@media (min-width:1024px) {
|
||||
.nav-link-text,
|
||||
.menu-link {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
color: #0D0E12;
|
||||
}
|
||||
|
||||
.nav-link-text::before,
|
||||
.active .nav-link-text::before,
|
||||
.menu-link::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transition: width 0.25s ease-out;
|
||||
}
|
||||
|
||||
.menu-link::before {
|
||||
background-color: #0D0E12;
|
||||
}
|
||||
.dark .menu-link::before {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.active .nav-link-text::before {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-link:hover .nav-link-text::before,
|
||||
.menu-link:hover::before {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.sub-menu {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
color: #505158;
|
||||
}
|
||||
|
||||
.sub-menu .no-hover{
|
||||
color: #505158 !important;
|
||||
}
|
||||
|
||||
.dark .sub-menu,
|
||||
.dark .sub-menu .no-hover {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.dark .sub-menu li:hover {
|
||||
color: #66D9E2;
|
||||
}
|
||||
|
||||
.sub-menu li:hover {
|
||||
color: #0053D0;
|
||||
}
|
||||
|
||||
.sub-menu {
|
||||
transition: all .3s ease !important;
|
||||
}
|
||||
|
||||
.nav-link span svg,
|
||||
header nav {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover span svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
@media (min-width:1024px) {
|
||||
|
||||
.nav-link:hover .sub-menu,
|
||||
.nav-link:focus-within .sub-menu {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.sub-menu {
|
||||
max-height: 0;
|
||||
transform: translateY(-10px);
|
||||
transition: all .7s ease !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.active .sub-menu {
|
||||
max-height: 1000px;
|
||||
transform: translateY(0px);
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
header nav {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
header nav.open {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.flag-container .sub-menu{
|
||||
max-height: fit-content;
|
||||
}
|
||||
} */
|
||||
|
||||
.lock-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ active_directory: true
|
||||
{% set lang = page.url | getlang %}
|
||||
{% block js_scripts %}
|
||||
<script src="/js/flag-anchor.js"></script>
|
||||
<script async defer src="/js/directory.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
<style>
|
||||
@@ -271,4 +270,6 @@ active_directory: true
|
||||
<div id="directory" style="height: 3000px;"></div>
|
||||
<div id="bottom-pagination" class="pagination"></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<script src="/js/directory.js"></script>
|
||||
|
||||
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 440 KiB |
|
Before Width: | Height: | Size: 446 KiB |
|
After Width: | Height: | Size: 99 KiB |
@@ -81,7 +81,7 @@ active_home: true
|
||||
<img class="no-border" src="/img/design_3/socials/apple.png" alt="">
|
||||
<img class="no-border" src="/img/design_3/socials/windows.png" alt="">
|
||||
</div>
|
||||
<p>Download Desktop App</p>
|
||||
<p>Download<br>Desktop App</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="apple-store-btn hidden" href="https://apps.apple.com/us/app/simplex-chat/id1605771084" target="_blank"><img src="/img/new/apple_store.svg"></a>
|
||||
@@ -97,7 +97,7 @@ active_home: true
|
||||
<a class="box-btn btn-1" title="{{ 'index-security-review-2022-title' | i18n({}, lang) }}" href="/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html">
|
||||
2022
|
||||
</a>
|
||||
<a class="box-btn btn-2" title="{{ 'index-security-review-2024-title' | i18n({}, lang) }}" href="/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.html">
|
||||
<a class="box-btn gold-gradient" title="{{ 'index-security-review-2024-title' | i18n({}, lang) }}" href="/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.html">
|
||||
2024
|
||||
</a>
|
||||
<p class="security-audits">
|
||||
@@ -109,13 +109,13 @@ active_home: true
|
||||
<img src="/img/design_3/publications/privacy-guides.png" alt="Privacy Guides">
|
||||
</a>
|
||||
<a title="{{ 'index-publications-whonix-title' | i18n({}, lang) }}" href="https://www.whonix.org/wiki/Chat#Recommendation" target="_blank">
|
||||
<img src="/img/design_3/publications/whonix.png" alt="Whonix">
|
||||
<img src="/img/design_3/publications/whonix.png" alt="Whonix" class="whonix">
|
||||
</a>
|
||||
<a title="{{ 'index-publications-heise-title' | i18n({}, lang) }}" href="https://www.heise.de/suche/?q=simplex+chat&sort_by=date&rm=search" target="_blank">
|
||||
<img src="/img/design_3/publications/heise.png" alt="Heise">
|
||||
</a>
|
||||
<a title="{{ 'index-publications-kuketz-title' | i18n({}, lang) }}" href="https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/" target="_blank">
|
||||
<img src="/img/design_3/publications/kuketz-blog.png" alt="Kuketz Blog">
|
||||
<img src="/img/design_3/publications/kuketz-blog.png" alt="Kuketz Blog" class="kuketz">
|
||||
</a>
|
||||
<a title="{{ 'index-publications-optout-title' | i18n({}, lang) }}" href="https://optoutpod.com/episodes/s3e02-simplexchat/" target="_blank">
|
||||
<img src="/img/design_3/publications/optout.png" alt="Opt Out">
|
||||
@@ -250,6 +250,105 @@ active_home: true
|
||||
io.observe(cover);
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
(function sectionHeaderState(){
|
||||
const mobileHeader = document.getElementById('mobile-header');
|
||||
if (!mobileHeader) return;
|
||||
|
||||
const cover = document.querySelector('.page-1.cover');
|
||||
const messaging = document.getElementById('messaging');
|
||||
const nextweb = document.getElementById('nextweb');
|
||||
const token = document.getElementById('token');
|
||||
const roadmap = document.getElementById('roadmap');
|
||||
const directory = document.getElementById('directory');
|
||||
const footer = document.querySelector('footer, .footer, #footer');
|
||||
|
||||
const observed = [
|
||||
{ key: 'cover', el: cover },
|
||||
{ key: 'main', el: messaging },
|
||||
{ key: 'main', el: nextweb },
|
||||
{ key: 'main', el: token },
|
||||
{ key: 'main', el: roadmap },
|
||||
{ key: 'main', el: directory },
|
||||
{ key: 'footer', el: footer }
|
||||
].filter(t => t.el);
|
||||
|
||||
if (!observed.length) return;
|
||||
|
||||
const MIN_COVER = 0.02;
|
||||
|
||||
function clear(){
|
||||
mobileHeader.classList.remove('cover','main','footer');
|
||||
}
|
||||
|
||||
function apply(key){
|
||||
clear();
|
||||
if (key) mobileHeader.classList.add(key);
|
||||
}
|
||||
|
||||
function viewportCoverageY(el){
|
||||
const r = el.getBoundingClientRect();
|
||||
const vh = window.innerHeight || document.documentElement.clientHeight;
|
||||
const visibleY = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top));
|
||||
return visibleY / Math.max(1, vh);
|
||||
}
|
||||
|
||||
function computeKeyMax(){
|
||||
const keyMax = { cover: 0, main: 0, footer: 0 };
|
||||
for (const t of observed) {
|
||||
const ratio = viewportCoverageY(t.el);
|
||||
if (ratio > keyMax[t.key]) keyMax[t.key] = ratio;
|
||||
}
|
||||
return keyMax;
|
||||
}
|
||||
|
||||
let lastKey = null;
|
||||
let rafId = null;
|
||||
|
||||
function update(){
|
||||
rafId = null; // Reset RAF flag
|
||||
|
||||
const keyMax = computeKeyMax();
|
||||
|
||||
let bestKey = null, best = MIN_COVER;
|
||||
for (const [k, r] of Object.entries(keyMax)) {
|
||||
if (r > best) { best = r; bestKey = k; }
|
||||
}
|
||||
|
||||
// Only update DOM if state actually changed
|
||||
if (bestKey !== lastKey) {
|
||||
if (bestKey) {
|
||||
apply(bestKey);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
lastKey = bestKey;
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleUpdate(){
|
||||
if (rafId) return; // Already scheduled
|
||||
rafId = requestAnimationFrame(update);
|
||||
}
|
||||
|
||||
let observer = null;
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
observer = new IntersectionObserver(scheduleUpdate, {
|
||||
threshold: [0, 0.02, 0.1, 0.25, 0.5, 0.75, 1],
|
||||
rootMargin: '0px 0px -35% 0px'
|
||||
});
|
||||
observed.forEach(t => observer.observe(t.el));
|
||||
scheduleUpdate(); // Initial update
|
||||
} else {
|
||||
// Fallback for older browsers
|
||||
['scroll','resize','load'].forEach(ev =>
|
||||
window.addEventListener(ev, scheduleUpdate, { passive: true })
|
||||
);
|
||||
scheduleUpdate();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -5,7 +5,13 @@ const isMobile = {
|
||||
};
|
||||
|
||||
(function() {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initializePage);
|
||||
} else {
|
||||
initializePage();
|
||||
}
|
||||
|
||||
function initializePage() {
|
||||
const googlePlayBtn = document.querySelector('.google-play-btn');
|
||||
const appleStoreBtn = document.querySelector('.apple-store-btn');
|
||||
const fDroidBtn = document.querySelector('.f-droid-btn');
|
||||
@@ -35,7 +41,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
showPromotedGroups();
|
||||
});
|
||||
}
|
||||
|
||||
async function showPromotedGroups() {
|
||||
welcome();
|
||||
|
||||
@@ -14,13 +14,11 @@ These vouchers are blockchain utility tokens — to focus purely on server c
|
||||
|
||||
## SMPX: Community Voucher Token planned for 2026
|
||||
|
||||
<img src="/img/design_3/simplex_nft_light.png" width="30%" class="float-to-right dark:hidden">
|
||||
|
||||
<img src="/img/design_3/simplex_nft_dark.png" width="30%" class="float-to-right hidden dark:block">
|
||||
<a href="javascript:void(0);" data-show-overlay="mint-simplex-nft" class="open-overlay-btn"><img src="/img/design_3/simplex_nft_smpx.jpg" width="200" class="float-to-right" style="border-radius: 10px;"></a>
|
||||
|
||||
SMPX token is v1 of Community Vouchers. We are aiming to launch it in 2026.
|
||||
|
||||
Mint a free SimpleX NFT for testnet access and feedback. The NFT is non-transferable, one per wallet.
|
||||
<a href="javascript:void(0);" data-show-overlay="mint-simplex-nft" class="open-overlay-btn">Mint a free SimpleX NFT</a> on Arbitrum One for testnet access and feedback. The NFT is limited to 1 per wallet, non-transferable.
|
||||
|
||||
**Preliminary token overview**
|
||||
- full name: **SimpleX Community Voucher**.
|
||||
@@ -42,9 +40,9 @@ These are early insights into how Community Vouchers can work.
|
||||
|
||||
### Why Community Vouchers?
|
||||
|
||||
<img src="/img/design_3/community_vouchers_light.jpg" width="30%" class="float-to-right dark:hidden">
|
||||
<img src="/img/design_3/community_vouchers_light.jpg" width="38%" class="float-to-right dark:hidden">
|
||||
|
||||
<img src="/img/design_3/community_vouchers_dark.jpg" width="30%" class="float-to-right hidden dark:block">
|
||||
<img src="/img/design_3/community_vouchers_dark.jpg" width="38%" class="float-to-right hidden dark:block">
|
||||
|
||||
To cover server bills securely and privately.
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ done
|
||||
|
||||
npm install
|
||||
cp node_modules/lottie-web/build/player/lottie.min.js src/js
|
||||
cp node_modules/ethers/dist/ethers.umd.min.js src/js
|
||||
cp node_modules/ethers/dist/ethers.umd.js.map src/js
|
||||
node merge_translations.js
|
||||
node customize_docs_frontmatter.js
|
||||
|
||||
|
||||