diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml new file mode 100644 index 0000000000..14e2c3fe12 --- /dev/null +++ b/.github/workflows/web.yml @@ -0,0 +1,36 @@ +name: Build Eleventy + +on: + push: + branches: + - website + - stable + paths: + - blog/** + - website/** + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x] + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies & build + run: | + ./website/web.sh + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + publish_dir: ./website/_site + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 42477be207..7dde754ab4 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,32 @@ stack.yaml.lock # Temporary test files tests/tmp logs/ + + +# for website +website/node_modules/ +website/src/blog/ +website/src/img/images/ +website/src/images/ +# Generated files +website/package/generated* + +# Ignore build tool output, e.g. code coverage +website/.nyc_output/ +website/coverage/ + +# Ignore API documentation +website/api-docs/ + +# Ignore folders from source code editors +website/.vscode +website/.idea + +# Ignore eleventy output when doing manual tests +website/_site/ + +website/package-lock.json + +# Ignore test files +website/.cache +website/test/stubs-layout-cache/_includes/*.js diff --git a/blog/20201022-simplex-chat.md b/blog/20201022-simplex-chat.md index 9b308ae2bd..80312c8647 100644 --- a/blog/20201022-simplex-chat.md +++ b/blog/20201022-simplex-chat.md @@ -1,12 +1,19 @@ +--- +layout: layouts/article.html +title: "Simplex Chat" +date: 2020-10-22 +permalink: "/blog/20201022-simplex-chat.html" +--- + # Simplex chat **Published:** Oct 22, 2020 -https://simplex.chat +[https://simplex.chat](https://simplex.chat) I'd really appreciate your feedback, criticism and suggestions on the open-source idea I was slowly working on since early 2020. I recently made the demo server for the low-level message queue protocol ("simplex messaging protocol") and the website to try to explain the chat idea that would use this protocol. -Haskell protocol implementation: https://github.com/simplex-chat/simplexmq +Haskell protocol implementation: [https://github.com/simplex-chat/simplexmq](https://github.com/simplex-chat/simplexmq) In short, the protocol defines a minimalist set of commands and server responses (just 7 commands and 5 responses sent over TCP) to operate encrypted message queues with in-memory persistence - the implementation uses STM. @@ -14,6 +21,4 @@ If anything, it was definitely helping to get to know Haskell types etc. much de Any criticism would be great - thank you in advance! ---- - -Originally published at https://www.reddit.com/r/haskell/comments/jg6uh4/simplex_chat/ +Originally published at [https://www.reddit.com/r/haskell/comments/jg6uh4/simplex_chat/](https://www.reddit.com/r/haskell/comments/jg6uh4/simplex_chat/) diff --git a/blog/20210512-simplex-chat-terminal-ui.md b/blog/20210512-simplex-chat-terminal-ui.md index d20bd18083..8859f92f53 100644 --- a/blog/20210512-simplex-chat-terminal-ui.md +++ b/blog/20210512-simplex-chat-terminal-ui.md @@ -1,4 +1,11 @@ -## Announcing SimpleX Chat Prototype! +--- +layout: layouts/article.html +title: "Announcing SimpleX Chat Prototype!" +date: 2021-05-12 +permalink: "/blog/20210512-simplex-chat-terminal-ui.html" +--- + +# Announcing SimpleX Chat Prototype! **Published:** May 12, 2021 @@ -6,7 +13,7 @@ For the last six months [me](https://github.com/epoberezkin) and my son [Efim](h We’ve been using the terminal client between us and a few other people for a couple of months now, eating our own “dog food”, and have developed up to version 0.3.1, with most of the messaging protocol features we originally planned -### Features +## Features - End-to-end encryption with protection from man in the middle attack. The connection invitation must be passed out-of-band (see [how to use SimpleX Chat](https://github.com/simplex-chat/simplex-chat#how-to-use-simplex-chat) in the repo). - No global identity or any usernames visible to the server(s), ensuring full privacy of your contacts and conversations. @@ -16,10 +23,8 @@ We’ve been using the terminal client between us and a few other people for a c - Encrypted TCP transport, independent of certificates. - You can deploy your own server, but you don’t have to - the demo SMP server to relay your messages is available at smp1.simplex.im:5223 (pre-configured in the client). -### We need your help! +## We need your help! We're building a new kind of chat network - the only network that lets you control your chat. We'd really appreciate your feedback, criticism and support - a star on the github repo, signing up to the mailing list or any contribution to the project will help. There is so much more to do! ---- - -Originally published at https://www.reddit.com/r/haskell/comments/naw6lz/simplex_chat_prototype_terminal_ui_made_in_haskell/ +Originally published at [https://www.reddit.com/r/haskell/comments/naw6lz/simplex_chat_prototype_terminal_ui_made_in_haskell/](https://www.reddit.com/r/haskell/comments/naw6lz/simplex_chat_prototype_terminal_ui_made_in_haskell/) diff --git a/blog/20210914-simplex-chat-v0.4-released.md b/blog/20210914-simplex-chat-v0.4-released.md index 6a97163ca9..860d4e8646 100644 --- a/blog/20210914-simplex-chat-v0.4-released.md +++ b/blog/20210914-simplex-chat-v0.4-released.md @@ -1,17 +1,23 @@ -# SimpleX announces SimpleX Chat v0.4 +--- +layout: layouts/article.html +title: "SimpleX announces SimpleX Chat v0.4" +date: 2021-09-14 +permalink: "/blog/20210914-simplex-chat-v0.4-released.html" +--- -## Open-source decentralized chat that uses privacy-preserving message routing protocol +# SimpleX announces SimpleX Chat v0.4 **Published:** Sep 14, 2021 -We are building a new platform for distributed Internet applications where privacy of the messages _and_ the network matter. [SimpleX Chat](https://github.com/simplex-chat/simplex-chat) is our first application, a chat application built on the SimpleX platform that serves as an example of the power of the platform and as a reference application. +## Open-source decentralized chat that uses privacy-preserving message routing protocol + +We are building a new platform for distributed Internet applications where privacy of the messages _and_ the network matter. [SimpleX Chat](https://github.com/simplex-chat/simplex-chat) is our first application, a chat application built on the SimpleX platform that serves as an example of the power of the platform and as a reference application. ## What is SimpleX? -We recognised that there is currently no messaging application which respects user privacy and guarantees metadata privacy -- in other words, messages could be private, but a third party can always see who is communicating with whom by examining a central service and the connection graph. SimpleX, at it's core, is designed to be truly distributed with no central server. This allows for enormous scalability at low cost, and also makes it virtually impossible to snoop on the network graph. - -The first application built on the platform is Simplex Chat, which for now is terminal (command line) based with mobile apps in the pipeline. The platform can easily support a private social network feed and a multitude of other services, which can be developed by the Simplex team or third party developers. +We recognised that there is currently no messaging application which respects user privacy and guarantees metadata privacy -- in other words, messages could be private, but a third party can always see who is communicating with whom by examining a central service and the connection graph. SimpleX, at it's core, is designed to be truly distributed with no central server. This allows for enormous scalability at low cost, and also makes it virtually impossible to snoop on the network graph. +The first application built on the platform is Simplex Chat, which for now is terminal (command line) based with mobile apps in the pipeline. The platform can easily support a private social network feed and a multitude of other services, which can be developed by the Simplex team or third party developers. ## What's new in v0.5? @@ -19,7 +25,7 @@ We're exicted to announce that SimpleX Chat now supports group chat and file tra ### Chat groups -To create a group use the `/g ` command. You can then invite contacts to the group by entering the `/a ` command. Your contact(s) will need to use the `/j accept` command to accept the invitation to the group. To send messages to the group, simply enter `# `. +To create a group use the `/g ` command. You can then invite contacts to the group by entering the `/a ` command. Your contact(s) will need to use the `/j accept` command to accept the invitation to the group. To send messages to the group, simply enter `# `. **Please note:** Groups are not stored on any server; they are maintained as a list of members in the app database. Sending a message to the group sends a message to each member of the group. @@ -39,6 +45,4 @@ We'd really appreciate your comments, criticism and support - a star on the GitH Our goal is to create a new kind of chat platform that lets you control your chat! ---- - -Originally published at https://www.reddit.com/r/selfhosted/comments/poal79/simplex_chat_an_opensource_decentralized_chat/ +Originally published at [https://www.reddit.com/r/selfhosted/comments/poal79/simplex_chat_an_opensource_decentralized_chat/](https://www.reddit.com/r/selfhosted/comments/poal79/simplex_chat_an_opensource_decentralized_chat/) diff --git a/blog/20211208-simplex-chat-v0.5-released.md b/blog/20211208-simplex-chat-v0.5-released.md index 2b3bd198db..428e80b338 100644 --- a/blog/20211208-simplex-chat-v0.5-released.md +++ b/blog/20211208-simplex-chat-v0.5-released.md @@ -1,20 +1,28 @@ -# SimpleX announces SimpleX Chat v0.5 +--- +layout: layouts/article.html +title: "SimpleX announces SimpleX Chat v0.5" +date: 2021-12-08 +permalink: "/blog/20211208-simplex-chat-v0.5-released.html" +--- -## Simplex Chat is the first chat platform that is 100% private by design - SimpleX no access to your connections graph +# SimpleX announces SimpleX Chat v0.5 **Published:** Dec 08, 2021 -We are building a new platform for distributed Internet applications where privacy of the messages _and_ the network matter. [SimpleX Chat](https://github.com/simplex-chat/simplex-chat) is our first application, a chat application built on the SimpleX platform that serves as an example of the power of the platform and as a reference application. +## Simplex Chat is the first chat platform that is 100% private by design - SimpleX no access to your connections graph + +We are building a new platform for distributed Internet applications where privacy of the messages _and_ the network matter. [SimpleX Chat](https://github.com/simplex-chat/simplex-chat) is our first application, a chat application built on the SimpleX platform that serves as an example of the power of the platform and as a reference application. ## What is SimpleX? -We recognised that there is currently no messaging application which respects user privacy and guarantees metadata privacy -- in other words, messages could be private, but a third party can always see who is communicating with whom by examining a central service and the connection graph. SimpleX, at it's core, is designed to be truly distributed with no central server. This allows for enormous scalability at low cost, and also makes it virtually impossible to snoop on the network graph. +We recognised that there is currently no messaging application which respects user privacy and guarantees metadata privacy -- in other words, messages could be private, but a third party can always see who is communicating with whom by examining a central service and the connection graph. SimpleX, at it's core, is designed to be truly distributed with no central server. This allows for enormous scalability at low cost, and also makes it virtually impossible to snoop on the network graph. -The first application built on the platform is Simplex Chat, which for now is terminal (command line) based with mobile apps in the pipeline. The platform can easily support a private social network feed and a multitude of other services, which can be developed by the Simplex team or third party developers. +The first application built on the platform is Simplex Chat, which for now is terminal (command line) based with mobile apps in the pipeline. The platform can easily support a private social network feed and a multitude of other services, which can be developed by the Simplex team or third party developers. ## What's new in v0.5? ### Long-term chat addresses + Users can now create long-term chat addresses that they can share with many people (e.g. in email signature, or online), so that any chat user can send them a connection request. This is an ALPHA feature, and we have not yet added any protection against spam contact requests. However, if the address you created starts receiving spam connection requests, you can simply delete it without losing any of your accepted connections and create another address - as many times as you like! @@ -27,6 +35,4 @@ We'd really appreciate your comments, criticism and support - a star on the GitH Our goal is to create a new kind of chat platform that lets you control your chat! ---- - -Originally published at https://www.reddit.com/r/haskell/comments/rc0xkn/simplex_chat_the_first_chat_platform_that_is_100/ +Originally published at [https://www.reddit.com/r/haskell/comments/rc0xkn/simplex_chat_the_first_chat_platform_that_is_100/](https://www.reddit.com/r/haskell/comments/rc0xkn/simplex_chat_the_first_chat_platform_that_is_100/) diff --git a/blog/20220112-simplex-chat-v1-released.md b/blog/20220112-simplex-chat-v1-released.md index 72a4280262..c9a6f0d8af 100644 --- a/blog/20220112-simplex-chat-v1-released.md +++ b/blog/20220112-simplex-chat-v1-released.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX announces SimpleX Chat v1" +date: 2022-01-12 +permalink: "/blog/20220112-simplex-chat-v1-released.html" +--- + # SimpleX announces SimpleX Chat v1 **Published:** Jan 12, 2022 diff --git a/blog/20220214-simplex-chat-ios-public-beta.md b/blog/20220214-simplex-chat-ios-public-beta.md index 4d3a7ab195..99de0f8609 100644 --- a/blog/20220214-simplex-chat-ios-public-beta.md +++ b/blog/20220214-simplex-chat-ios-public-beta.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX announces SimpleX Chat public beta for iOS" +date: 2022-02-14 +permalink: "/blog/20220214-simplex-chat-ios-public-beta.html" +--- + # SimpleX announces SimpleX Chat public beta for iOS **Published:** Feb 14, 2022 @@ -7,12 +14,13 @@ Our new iPhone app is very basic - right now it only supports text messages and emojis. Even though the app is new, it uses the same core code as our terminal app, that was used and stabilized over a long time, and it provides the same level of privacy and security that has been available since the release of v1 a month ago: + - [double-ratchet](https://www.signal.org/docs/specifications/doubleratchet/) E2E encryption. - separate keys for each contact. - additional layer of E2E encryption in each message queue (to prevent traffic correlation when multiple queues are used in a conversation - something we plan later this year). - additional encryption of messages delivered from servers to recipients (also to prevent traffic correlation). -You can read more details in our recent [v1 announcement](https://github.com/simplex-chat/simplex-chat/blob/stable/blog/20220112-simplex-chat-v1-released.md). +You can read more details in our recent [v1 announcement](./20220112-simplex-chat-v1-released.md). ## Join our public beta! @@ -21,6 +29,7 @@ Install the app [via TestFlight](https://testflight.apple.com/join/DWuT2LQu), co We would really appreciate any feedback to improve the app and to decide which additional features should be included in our public release in March. Should it be: + - images, - link previews, - or maybe something else we couldn't think of. diff --git a/blog/20220308-simplex-chat-mobile-apps.md b/blog/20220308-simplex-chat-mobile-apps.md index d4ba9d17dc..10bac3d620 100644 --- a/blog/20220308-simplex-chat-mobile-apps.md +++ b/blog/20220308-simplex-chat-mobile-apps.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX announces SimpleX Chat mobile apps for iOS and Android" +date: 2022-03-08 +permalink: "/blog/20220308-simplex-chat-mobile-apps.html" +--- + # SimpleX announces SimpleX Chat mobile apps for iOS and Android **Published:** March 8, 2022 @@ -35,12 +42,13 @@ You can always migrate from a public App Store version to a TestFlight version. ## It's not all new - our core code has been used for a long time by a few thousand people in our terminal app. The apps use the same core code as our terminal app, that was used and stabilized over a long time, and it provides the same level of privacy and security that has been available since the release of v1 earlier this year: + - [double-ratchet](https://www.signal.org/docs/specifications/doubleratchet/) E2E encryption. - separate keys for each contact. - additional layer of E2E encryption in each message queue (to prevent traffic correlation when multiple queues are used in a conversation - something we plan later this year). - additional encryption of messages delivered from servers to recipients (also to prevent traffic correlation). -You can read more technical details in our recent [v1 announcement](https://github.com/simplex-chat/simplex-chat/blob/stable/blog/20220112-simplex-chat-v1-released.md). +You can read more technical details in our recent [v1 announcement](./20220112-simplex-chat-v1-released.md). A big thank you to [@angerman](https://github.com/angerman) for making it possible to compile our Haskell code to mobile platforms and getting it approved on app stores - it has been a non-trivial project, and it is still ongoing. diff --git a/blog/20220404-simplex-chat-instant-notifications.md b/blog/20220404-simplex-chat-instant-notifications.md index e6d8ec90e5..537eccca6b 100644 --- a/blog/20220404-simplex-chat-instant-notifications.md +++ b/blog/20220404-simplex-chat-instant-notifications.md @@ -1,10 +1,17 @@ +--- +layout: layouts/article.html +title: "Instant notifications for SimpleX Chat mobile apps" +date: 2022-04-04 +permalink: "/blog/20220404-simplex-chat-instant-notifications.html" +--- + # Instant notifications for SimpleX Chat mobile apps **Published:** April 04, 2022 ## SimpleX Chat is the first chat platform that is 100% private by design - it has no access to your connections -Since we released SimpleX Chat mobile apps couple of weeks ago we've had a lot of excitement from our users - nearly 2000 people downloaded the app after [the announcement](https://github.com/simplex-chat/simplex-chat/blob/stable/blog/20220308-simplex-chat-mobile-apps.md)! +Since we released SimpleX Chat mobile apps couple of weeks ago we've had a lot of excitement from our users - nearly 2000 people downloaded the app after [the announcement](./20220308-simplex-chat-mobile-apps.md)! Huge thanks to everybody who downloaded and connected to us via the chat - there were many great questions and suggestions, and on some days I spent most of the time chatting to our users :) @@ -68,7 +75,7 @@ We already have background refresh in the iOS app that periodically checks for n The only solution known to us is using Apple's push notifications service (APN) to deliver push notifications. -We planned for it, so we added to [v1 of SMP](https://github.com/simplex-chat/simplex-chat/blob/stable/blog/20220112-simplex-chat-v1-released.md) (the protocol used by our servers) an extension allowing the client to subscribe to notifications from message queues, via separate queue addresses, and using separate cryptographic keys for each queue. This has to be enabled by the client for each queue separately. We haven't used this extension so far, and now we are building a SimpleX notification service based on it. +We planned for it, so we added to [v1 of SMP](./20220112-simplex-chat-v1-released.md) (the protocol used by our servers) an extension allowing the client to subscribe to notifications from message queues, via separate queue addresses, and using separate cryptographic keys for each queue. This has to be enabled by the client for each queue separately. We haven't used this extension so far, and now we are building a SimpleX notification service based on it. If the user enables push notifications, then for each contact the app would enable a notification subscription and pass credentials to the notification server together with the device token required to deliver push notifications to user's device. diff --git a/blog/20220511-simplex-chat-v2-images-files.md b/blog/20220511-simplex-chat-v2-images-files.md index f7c3a263e6..97b24fbce1 100644 --- a/blog/20220511-simplex-chat-v2-images-files.md +++ b/blog/20220511-simplex-chat-v2-images-files.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX Chat v2.0 - sending images and files in mobile apps" +date: 2022-05-11 +permalink: "/blog/20220511-simplex-chat-v2-images-files.html" +--- + # SimpleX Chat v2.0 - sending images and files in mobile apps **Published:** May 11, 2022 @@ -12,7 +19,7 @@ To make file and images work for mobile apps we made a breaking change in Simple In the mobile app, to send and receive files both devices must have version 2.0 installed - so please check it with your contacts. Receiving images works in the previous version, so even if your contacts did not yet upgrade the app, they should be able to receive the images. -## The first messaging platform without user identifiers. +## The first messaging platform without user identifiers To protect identities of users and their connections, SimpleX Chat has no user identifiers visible to the network – unlike any other messaging platform. @@ -34,4 +41,4 @@ Once you install the app, you can connect to anybody: 2. To make a private connection, you need to create a one-time connection link or a QR code via the app. You can show the QR code to your contact in person or via a video call - this is the most secure way to create a connection - or you can share the link via any other channel. Only one user can connect via this link. 3. Once another user scans the QR code or opens the app via the link the connection will be created and you can send end-to-end encrypted messages privately, without anybody knowing you are connected. -Make a private connection +Make a private connection diff --git a/blog/20220524-simplex-chat-better-privacy.md b/blog/20220524-simplex-chat-better-privacy.md index 3a5658640b..249cef06db 100644 --- a/blog/20220524-simplex-chat-better-privacy.md +++ b/blog/20220524-simplex-chat-better-privacy.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX Chat v2.1 - better conversation privacy" +date: 2022-05-24 +permalink: "/blog/20220524-simplex-chat-better-privacy.html" +--- + # SimpleX Chat v2.1 - better conversation privacy **Published:** May 24, 2022 diff --git a/blog/20220604-simplex-chat-new-privacy-security-settings.md b/blog/20220604-simplex-chat-new-privacy-security-settings.md index 6e40badb85..a828a0a703 100644 --- a/blog/20220604-simplex-chat-new-privacy-security-settings.md +++ b/blog/20220604-simplex-chat-new-privacy-security-settings.md @@ -1,8 +1,15 @@ +--- +layout: layouts/article.html +title: "SimpleX Chat v2.2 - the first messaging platform without user identities - 100% private by design!" +date: 2022-06-04 +permalink: "/blog/20220604-simplex-chat-new-privacy-security-settings.html" +--- + # SimpleX Chat v2.2 - the first messaging platform without user identities - 100% private by design! **Published:** June 4, 2022 -See [v2 announcement](./20220511-simplex-chat-v2-images-files.md) for more information about SimpleX platform and how it protects your privacy by avoiding user identities of any kind in its design - SimpleX, unlike any other messaging platfom, has no identity keys or any numbers that identify its users. +See [v2 announcement](./20220511-simplex-chat-v2-images-files.md) for more information about SimpleX platform and how it protects your privacy by avoiding user identities of any kind in its design - SimpleX, unlike any other messaging platform, has no identity keys or any numbers that identify its users. ## New Privacy and Security settings in version 2.2 diff --git a/blog/20220711-simplex-chat-v3-released-ios-notifications-audio-video-calls-database-export-import-protocol-improvements.md b/blog/20220711-simplex-chat-v3-released-ios-notifications-audio-video-calls-database-export-import-protocol-improvements.md index 522d1bf237..af50b8a4d0 100644 --- a/blog/20220711-simplex-chat-v3-released-ios-notifications-audio-video-calls-database-export-import-protocol-improvements.md +++ b/blog/20220711-simplex-chat-v3-released-ios-notifications-audio-video-calls-database-export-import-protocol-improvements.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX announces SimpleX Chat v3" +date: 2022-07-11 +permalink: "/blog/20220711-simplex-chat-v3-released-ios-notifications-audio-video-calls-database-export-import-protocol-improvements.html" +--- + # SimpleX announces SimpleX Chat v3 **Published:** Jul 11, 2022 diff --git a/blog/20220723-simplex-chat-v3.1-tor-groups-efficiency.md b/blog/20220723-simplex-chat-v3.1-tor-groups-efficiency.md index de92bb6925..7fe296e677 100644 --- a/blog/20220723-simplex-chat-v3.1-tor-groups-efficiency.md +++ b/blog/20220723-simplex-chat-v3.1-tor-groups-efficiency.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX Chat v3.1-beta is released" +date: 2022-07-23 +permalink: "/blog/20220723-simplex-chat-v3.1-tor-groups-efficiency.html" +--- + # SimpleX Chat v3.1-beta is released **Published:** Jul 23, 2022 @@ -17,7 +24,7 @@ While SMP protocol is focussed on protecting application-level meta-data by usin This release of terminal app supports accessing the servers via Tor, but the servers themselves are still available on their usual addresses. We are planning to add .onion addresses (v3 hidden service) to all messaging servers we provide, and the users who self-host the servers will also be able to have dual servers addresses - so that one party in the conversation can access the servers via .onion address without necessarily requiring that the other party uses Tor as well. -To access SimpleX servers via Tor you need to install Tor proxy and run simplex-chat with `-x` option. See [terminal app docs](../docs/CLI.md#access-messaging-servers-via-tor-beta) for more information. +To access SimpleX servers via Tor you need to install Tor proxy and run simplex-chat with `-x` option. See [terminal app docs](https://github.com/simplex-chat/simplex-chat/blob/stable/docs/CLI.md#access-messaging-servers-via-tor-beta) for more information. As this is a beta release, to install it you need to use this command: @@ -41,7 +48,7 @@ To manage groups via terminal app or via chat console in the mobile apps you hav Accepting group invitations, leaving and deleting groups no longer requires using console commands. -### Optimized battery and traffic usage - up to 90x reduction! +### Optimized battery and traffic usage - up to 90x reduction To reduce battery and traffic usage this release updated SMP protocol to allow batching multiple server commands (up to 90!) into one traffic block – provided both the server and the client are upgraded. It means that if you have 90 contacts (or group members) on one server, to subscribe to all messaging queues you now need to send only one 16kb block instead of ~1.5Mb of traffic (90 blocks). It also hides how many contacts you have from any attackers who observe your network. @@ -89,7 +96,7 @@ What we plan to add soon to further improve privacy and security: - using Tor v3 hidden service addresses for messaging servers. - passphrase-based local database encryption. -SimpleX Chat [README page](../README.md#privacy-technical-details-and-limitations) has more details about it. +SimpleX Chat [README page](https://github.com/simplex-chat/simplex-chat/blob/stable/README.md#privacy-technical-details-and-limitations) has more details about it. ## We ask you to help us pay for 3rd party security audit diff --git a/blog/20220808-simplex-chat-v3.1-chat-groups.md b/blog/20220808-simplex-chat-v3.1-chat-groups.md index 6927e403ef..287160dbd8 100644 --- a/blog/20220808-simplex-chat-v3.1-chat-groups.md +++ b/blog/20220808-simplex-chat-v3.1-chat-groups.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX Chat v3.1 is released" +date: 2022-08-08 +permalink: "/blog/20220808-simplex-chat-v3.1-chat-groups.html" +--- + # SimpleX Chat v3.1 is released **Published:** Aug 8, 2022 @@ -5,11 +12,11 @@ ## What's new - [secret chat groups](#secret-chat-groups)! -- [access to messaging servers via Tor](#access-to-messaging-servers-via-tor) +- [access to messaging servers via Tor](#access-messaging-servers-via-tor) - [advanced network settings](#advanced-network-settings) - [published chat protocol](#published-chat-protocol) - [new app icons](#new-app-icons) -- [other changes since v3](#other-changes-since-v3) +- [other changes since v3](#other-changes-since-v3): - optimized battery and traffic usage - up to 90x reduction! - two docker configurations for self-hosted SMP servers @@ -19,7 +26,7 @@ It's been [nearly a year](./20210914-simplex-chat-v0.4-released.md) since the users of SimpleX Chat terminal app started experimenting with the groups, and now it is available to mobile app users as well. Many bugs were fixed, the stability was improved, but there are both the features we need to add and the bugs we need to fix to make groups more useful - we really look forward to your feedback. You can send any suggestions via the app by choosing `Chat with the developers` via app Settings (or using `/simplex` command in the terminal app) – this would connect you to SimpleX team via its [fixed chat address](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D). -SimpleX network is decentralized, so how do groups work? Unlike Matrix or Signal that host the group profile and the list of group members on their servers, SimpleX servers have no information about the group's existence - only its members do. SimpleX network does not assign any globally unique identifiers to the group, there is only a local database identifier and the list of members stored on members' devices. A user has an independent connection to each member in a group. When a user sends a message to the group, the app sends this message independently to each member. You can read more about how groups work in [SimpleX Chat Protocol](../docs/protocol/simplex-chat.md#sub-protocol-for-chat-groups). +SimpleX network is decentralized, so how do groups work? Unlike Matrix or Signal that host the group profile and the list of group members on their servers, SimpleX servers have no information about the group's existence - only its members do. SimpleX network does not assign any globally unique identifiers to the group, there is only a local database identifier and the list of members stored on members' devices. A user has an independent connection to each member in a group. When a user sends a message to the group, the app sends this message independently to each member. You can read more about how groups work in [SimpleX Chat Protocol](https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/simplex-chat.md#sub-protocol-for-chat-groups). But how can it scale, you might ask? It simply won't, and the current design for the groups is only suitable for relatively small groups of people who know each other well, definitely not larger than few hundred members – this design prioritized privacy and security of the group over its size or performance. For example, to send a message to the group of 100 members a user would need to send a total of ~1.6mb of data (as each message uses a fixed size block of 16kb). And if you were to send a 1mb file then it would also require sending it 100 times (provided each member accepts it). @@ -42,7 +49,7 @@ But there are scenarios when users need to protect their IP addresses from the s This release allows to access SimpleX messaging servers via Tor on all platforms: -- terminal app beta supported it for a couple weeks now: to access SimpleX servers via Tor you need to install Tor proxy and run simplex-chat with `-x` option. See [terminal app docs](../docs/CLI.md#access-messaging-servers-via-tor-beta) for more information. +- terminal app beta supported it for a couple weeks now: to access SimpleX servers via Tor you need to install Tor proxy and run simplex-chat with `-x` option. See [terminal app docs](https://github.com/simplex-chat/simplex-chat/blob/stable/docs/CLI.md#access-messaging-servers-via-tor-beta) for more information. - Android app supports access via Tor using Orbot SOCKS proxy. Once you install and start Orbot, you need to enable `Network & Servers / Use SOCKS proxy` setting in the app to access SimpleX servers via Tor. - iOS app can also be used with Orbot iOS app (that is installed as a system-wide VPN provider). The only setting you might need to change is to increase network timeouts in the app - to do that you have to enable `Developer tools`, and then chose `Network & Servers / Advanced network settings / Set timeouts for proxy`. @@ -69,9 +76,9 @@ More options to customize the app are coming - please let us know what are the m The [low level SimpleX protocols](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/) were published long time ago, and updated to reflect the evolution of the protocols, the high level chat protocol was not published before. The reason for that was to allow us to iterate it quickly, without committing to any of the decisions. -This is the [first draft of SimpleX Chat Protocol](../docs/protocol/simplex-chat.md) - let us know any questions or suggestions. +This is the [first draft of SimpleX Chat Protocol](https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/simplex-chat.md) - let us know any questions or suggestions. -### Other changes since v3 +### Other changes since v3 Since v3 release we also optimized battery and traffic usage - with up to 90x traffic reduction in some cases – and published two docker configurations for self-hosted SMP servers. Read more about it in the previous [beta version announcement](./20220723-simplex-chat-v3.1-tor-groups-efficiency.md). @@ -85,7 +92,7 @@ Some links to answer the most common questions: [Technical details and limitations](./20220723-simplex-chat-v3.1-tor-groups-efficiency.md#privacy-technical-details-and-limitations). -[How SimpleX is different from Session, Matrix, Signal, etc.](../README.md#frequently-asked-questions). +[How SimpleX is different from Session, Matrix, Signal, etc.](https://github.com/simplex-chat/simplex-chat/blob/stable/README.md#frequently-asked-questions). ## We ask you to help us pay for 3rd party security audit diff --git a/blog/20220901-simplex-chat-v3.2-incognito-mode.md b/blog/20220901-simplex-chat-v3.2-incognito-mode.md index 662051c0f0..1dbf6b1acf 100644 --- a/blog/20220901-simplex-chat-v3.2-incognito-mode.md +++ b/blog/20220901-simplex-chat-v3.2-incognito-mode.md @@ -1,3 +1,10 @@ +--- +layout: layouts/article.html +title: "SimpleX Chat v3.2 is released" +date: 2022-09-01 +permalink: "/blog/20220901-simplex-chat-v3.2-incognito-mode.html" +--- + # SimpleX Chat v3.2 is released **Published:** Sep 1, 2022 @@ -71,7 +78,7 @@ Some links to answer the most common questions: [Technical details and limitations](./20220723-simplex-chat-v3.1-tor-groups-efficiency.md#privacy-technical-details-and-limitations). -[How SimpleX is different from Session, Matrix, Signal, etc.](../README.md#frequently-asked-questions). +[How SimpleX is different from Session, Matrix, Signal, etc.](https://github.com/simplex-chat/simplex-chat/blob/stable/README.md#frequently-asked-questions). ## We ask you to help us pay for 3rd party security audit diff --git a/images/conversation.png b/images/conversation.png new file mode 100644 index 0000000000..539f43d11f Binary files /dev/null and b/images/conversation.png differ diff --git a/website/.eleventy.js b/website/.eleventy.js new file mode 100644 index 0000000000..fad86990fa --- /dev/null +++ b/website/.eleventy.js @@ -0,0 +1,61 @@ +const markdownIt = require("markdown-it") +const markdownItAnchor = require("markdown-it-anchor") +const markdownItReplaceLink = require('markdown-it-replace-link') +const slugify = require("slugify") +const uri = require('fast-uri') + +module.exports = function (ty) { + // Keeps the same directory structure. + ty.addPassthroughCopy("src/assets/") + ty.addPassthroughCopy("src/img") + ty.addPassthroughCopy("src/css") + ty.addPassthroughCopy("src/js") + ty.addPassthroughCopy("src/contact") + ty.addPassthroughCopy("src/app-demo") + ty.addPassthroughCopy("src/blog/images") + ty.addPassthroughCopy("src/images") + ty.addPassthroughCopy("src/CNAME") + + ty.addCollection('blogs', function (collection) { + return collection.getFilteredByGlob('src/blog/*.md').reverse() + }) + + ty.addWatchTarget("src/css") + ty.addWatchTarget("markdown/") + ty.addWatchTarget("components/Card.js") + + const markdownLib = markdownIt({ + html: true, + breaks: true, + linkify: true, + replaceLink: function (link, _env) { + let parsed = uri.parse(link) + if (parsed.scheme || parsed.host || !parsed.path.endsWith(".md")) { + return link + } + parsed.path = parsed.path.replace(/\.md$/, ".html") + return uri.serialize(parsed) + } + }).use(markdownItAnchor, { + slugify: (str) => + slugify(str, { + lower: true, + strict: true, + }) + }).use(markdownItReplaceLink) + + // replace the default markdown-it instance + ty.setLibrary("md", markdownLib) + + return { + dir: { + input: 'src', + includes: '_includes', + output: '_site', + }, + templateFormats: ['md', 'njk', 'html'], + markdownTemplateEngine: 'njk', + htmlTemplateEngine: 'njk', + dataTemplateEngine: 'njk', + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000000..a252d1a104 --- /dev/null +++ b/website/package.json @@ -0,0 +1,27 @@ +{ + "name": "11ty-demo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "eleventy", + "start": "npx eleventy --serve", + "test": "echo \"Error: no test specified\" && exit 1", + "watch-tailwind": "npx tailwindcss -i ./tailwind.css -o ./_site/css/tailwind.css --watch", + "build-tailwind": "npx tailwindcss -i ./tailwind.css -o ./_site/css/tailwind.css" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@11ty/eleventy": "^1.0.1", + "common-tags": "^1.8.2", + "fast-uri": "^2.1.0", + "markdown-it-anchor": "^8.6.4", + "slugify": "^1.6.5", + "tailwindcss": "^3.0.24" + }, + "dependencies": { + "markdown-it-replace-link": "^1.1.0" + } +} diff --git a/website/src/CNAME b/website/src/CNAME new file mode 100644 index 0000000000..6455e69481 --- /dev/null +++ b/website/src/CNAME @@ -0,0 +1 @@ +new.simplex.chat diff --git a/website/src/_includes/footer.html b/website/src/_includes/footer.html new file mode 100644 index 0000000000..4d91039734 --- /dev/null +++ b/website/src/_includes/footer.html @@ -0,0 +1,21 @@ +
+
+ + +
+ Star +
+
+
diff --git a/website/src/_includes/layouts/article.html b/website/src/_includes/layouts/article.html new file mode 100644 index 0000000000..2982be2912 --- /dev/null +++ b/website/src/_includes/layouts/article.html @@ -0,0 +1,24 @@ + + + + + + + + {{ title }} + + + + + + + + + {% include "nav.html" %} +
+
{{ content | safe }}
+
+ {% include "footer.html" %} + + + diff --git a/website/src/_includes/nav.html b/website/src/_includes/nav.html new file mode 100644 index 0000000000..39ae7f995a --- /dev/null +++ b/website/src/_includes/nav.html @@ -0,0 +1,140 @@ +
+ + + +
+ + + + diff --git a/website/src/blog.html b/website/src/blog.html new file mode 100644 index 0000000000..e48549a0dd --- /dev/null +++ b/website/src/blog.html @@ -0,0 +1,48 @@ + + + + + + + + + + Blogs + + + + + {% include "nav.html" %} + +
+ {% for blog in collections.blogs %} +
+

{{ blog.data.title }}

+

{{ blog.data.date.toUTCString().split(' ').slice(0, 4).join(' ') }}

+ Read More +
+
+ {% endfor %} +
+
+
+ + +
+ Star +
+
+
+ + diff --git a/website/src/contact/index.html b/website/src/contact/index.html new file mode 100644 index 0000000000..49d5b71f18 --- /dev/null +++ b/website/src/contact/index.html @@ -0,0 +1,187 @@ + + + + + + + + SimpleX chat: private, secure, no global identities + + + + + + + + + + + + + + {% include "nav.html" %} + +
+
+

This link is the invitation from a SimpleX Chat user

+ +
+
+

If you already installed SimpleX Chat for the + terminal v1.0.0+, copy the command below and use it in the chat: +

+ +

+ + + Copy to clipboard + + +

+
+ +
+ +
+

Scan QR code from mobile app

+

+ To make a connection: +

+
    +
  1. install SimpleX app
  2. +
  3. tap the button below
  4. +
  5. tap Connect button in the app
  6. +
+ + +
+
+
+
+
+ +
+
+

To install SimpleX Chat for the terminal

+
+

+ use this command: +

+
+

curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/install.sh + | + bash + + Copy to clipboard + + +

+
+
+ +
+

+ See SimpleX Chat + GitHub repository + + for the instructions how to download or compile it from the source code. +

+
+ +
+
+ +
+
+
+

Use SimpleX

+

+ SimpleX chat terminal client + v2.0.0 is + released!
+ – groups and files
+ – two-layer E2E encryption, with double-ratchet algorithm
+ – protocol is compatible with mobile apps
+

+

+ You can use our servers or deploy your own, e.g. using + + StackScript on Linode. +

+

+ Sign up to be updated about the new releases. +

+ +
+ + +
+
+ +
+
+ + +
+ Star +
+
+
+ + + diff --git a/website/src/contact/index.js b/website/src/contact/index.js new file mode 100644 index 0000000000..bf1427628d --- /dev/null +++ b/website/src/contact/index.js @@ -0,0 +1,56 @@ +(async function () { + const connURIel = document.getElementById("conn_req_uri_text"); + const connURI = document.location.toString().replace(/\/(contact|invitation)\//, "/$1"); + connURIel.innerText = "/c " + connURI; + const mobileConnURIanchor = document.getElementById("mobile_conn_req_uri"); + mobileConnURIanchor.href = connURI.replace("https://simplex.chat", "simplex:"); + if (document.location.pathname.indexOf("/contact") >= 0) { + document.querySelector("#conn_req .conn_mode").innerText = "address of"; + } + const els = document.querySelectorAll(".content_copy_with_tooltip"); + if (navigator.clipboard) { + els.forEach(contentCopyWithTooltip) + } else { + els.forEach(el => el.style.visibility = "hidden") + } + + const connQRCode = document.getElementById("conn_req_uri_qrcode"); + try { + await QRCode.toCanvas(connQRCode, connURI, { + errorCorrectionLevel: "M", + color: {dark: "#062D56"} + }); + connQRCode.style.width = "360px"; + connQRCode.style.height = "360px"; + } catch (err) { + console.error(err); + } + + function contentCopyWithTooltip(parent) { + const content = parent.querySelector(".content"); + const tooltip = parent.querySelector(".tooltiptext"); + console.log(parent.querySelector(".content_copy") ,111) + console.log(parent) + const copyButton = parent.querySelector(".content_copy"); + copyButton.addEventListener("click", copyAddress) + copyButton.addEventListener("mouseout", resetTooltip) + + function copyAddress() { + navigator.clipboard.writeText(content.innerText || content.value); + tooltip.innerHTML = "Copied!"; + } + + function resetTooltip() { + tooltip.innerHTML = "Copy to clipboard"; + } + } + + function copyAddress() { + navigator.clipboard.writeText(connURI); + tooltipEl.innerHTML = "Copied!"; + } + + function resetTooltip() { + tooltipEl.innerHTML = "Copy to clipboard"; + } +})(); diff --git a/website/src/contact/qrcode.js b/website/src/contact/qrcode.js new file mode 100644 index 0000000000..95b885fe2c --- /dev/null +++ b/website/src/contact/qrcode.js @@ -0,0 +1 @@ +var QRCode=function(t){"use strict";var r,e=function(){return"function"==typeof Promise&&Promise.prototype&&Promise.prototype.then},n=[0,26,44,70,100,134,172,196,242,292,346,404,466,532,581,655,733,815,901,991,1085,1156,1258,1364,1474,1588,1706,1828,1921,2051,2185,2323,2465,2611,2761,2876,3034,3196,3362,3532,3706],o=function(t){if(!t)throw new Error('"version" cannot be null or undefined');if(t<1||t>40)throw new Error('"version" should be in range from 1 to 40');return 4*t+17},a=function(t){return n[t]},i=function(t){for(var r=0;0!==t;)r++,t>>>=1;return r},u=function(t){if("function"!=typeof t)throw new Error('"toSJISFunc" is not a valid function.');r=t},s=function(){return void 0!==r},f=function(t){return r(t)};function h(t,r){return t(r={exports:{}},r.exports),r.exports}var c=h((function(t,r){r.L={bit:1},r.M={bit:0},r.Q={bit:3},r.H={bit:2},r.isValid=function(t){return t&&void 0!==t.bit&&t.bit>=0&&t.bit<4},r.from=function(t,e){if(r.isValid(t))return t;try{return function(t){if("string"!=typeof t)throw new Error("Param is not a string");switch(t.toLowerCase()){case"l":case"low":return r.L;case"m":case"medium":return r.M;case"q":case"quartile":return r.Q;case"h":case"high":return r.H;default:throw new Error("Unknown EC Level: "+t)}}(t)}catch(t){return e}}}));function g(){this.buffer=[],this.length=0}c.L,c.M,c.Q,c.H,c.isValid,g.prototype={get:function(t){var r=Math.floor(t/8);return 1==(this.buffer[r]>>>7-t%8&1)},put:function(t,r){for(var e=0;e>>r-e-1&1))},getLengthInBits:function(){return this.length},putBit:function(t){var r=Math.floor(this.length/8);this.buffer.length<=r&&this.buffer.push(0),t&&(this.buffer[r]|=128>>>this.length%8),this.length++}};var d=g;function l(t){if(!t||t<1)throw new Error("BitMatrix size must be defined and greater than 0");this.size=t,this.data=new Uint8Array(t*t),this.reservedBit=new Uint8Array(t*t)}l.prototype.set=function(t,r,e,n){var o=t*this.size+r;this.data[o]=e,n&&(this.reservedBit[o]=!0)},l.prototype.get=function(t,r){return this.data[t*this.size+r]},l.prototype.xor=function(t,r,e){this.data[t*this.size+r]^=e},l.prototype.isReserved=function(t,r){return this.reservedBit[t*this.size+r]};var v=l,p=h((function(t,r){var e=o;r.getRowColCoords=function(t){if(1===t)return[];for(var r=Math.floor(t/7)+2,n=e(t),o=145===n?26:2*Math.ceil((n-13)/(2*r-2)),a=[n-7],i=1;i=0&&t<=7},r.from=function(t){return r.isValid(t)?parseInt(t,10):void 0},r.getPenaltyN1=function(t){for(var r=t.size,n=0,o=0,a=0,i=null,u=null,s=0;s=5&&(n+=e+(o-5)),i=h,o=1),(h=t.get(f,s))===u?a++:(a>=5&&(n+=e+(a-5)),u=h,a=1)}o>=5&&(n+=e+(o-5)),a>=5&&(n+=e+(a-5))}return n},r.getPenaltyN2=function(t){for(var r=t.size,e=0,o=0;o=10&&(1488===n||93===n)&&e++,a=a<<1&2047|t.get(u,i),u>=10&&(1488===a||93===a)&&e++}return e*o},r.getPenaltyN4=function(t){for(var r=0,e=t.data.length,n=0;n=0;){for(var n=e[0],o=0;o0){var o=new Uint8Array(this.degree);return o.set(e,n),o}return e};var L=T,b=function(t){return!isNaN(t)&&t>=1&&t<=40},U="(?:[u3000-u303F]|[u3040-u309F]|[u30A0-u30FF]|[uFF00-uFFEF]|[u4E00-u9FAF]|[u2605-u2606]|[u2190-u2195]|u203B|[u2010u2015u2018u2019u2025u2026u201Cu201Du2225u2260]|[u0391-u0451]|[u00A7u00A8u00B1u00B4u00D7u00F7])+",x="(?:(?![A-Z0-9 $%*+\\-./:]|"+(U=U.replace(/u/g,"\\u"))+")(?:.|[\r\n]))+",k=new RegExp(U,"g"),F=new RegExp("[^A-Z0-9 $%*+\\-./:]+","g"),S=new RegExp(x,"g"),D=new RegExp("[0-9]+","g"),Y=new RegExp("[A-Z $%*+\\-./:]+","g"),_=new RegExp("^"+U+"$"),z=new RegExp("^[0-9]+$"),H=new RegExp("^[A-Z0-9 $%*+\\-./:]+$"),J={KANJI:k,BYTE_KANJI:F,BYTE:S,NUMERIC:D,ALPHANUMERIC:Y,testKanji:function(t){return _.test(t)},testNumeric:function(t){return z.test(t)},testAlphanumeric:function(t){return H.test(t)}},K=h((function(t,r){r.NUMERIC={id:"Numeric",bit:1,ccBits:[10,12,14]},r.ALPHANUMERIC={id:"Alphanumeric",bit:2,ccBits:[9,11,13]},r.BYTE={id:"Byte",bit:4,ccBits:[8,16,16]},r.KANJI={id:"Kanji",bit:8,ccBits:[8,10,12]},r.MIXED={bit:-1},r.getCharCountIndicator=function(t,r){if(!t.ccBits)throw new Error("Invalid mode: "+t);if(!b(r))throw new Error("Invalid version: "+r);return r>=1&&r<10?t.ccBits[0]:r<27?t.ccBits[1]:t.ccBits[2]},r.getBestModeForData=function(t){return J.testNumeric(t)?r.NUMERIC:J.testAlphanumeric(t)?r.ALPHANUMERIC:J.testKanji(t)?r.KANJI:r.BYTE},r.toString=function(t){if(t&&t.id)return t.id;throw new Error("Invalid mode")},r.isValid=function(t){return t&&t.bit&&t.ccBits},r.from=function(t,e){if(r.isValid(t))return t;try{return function(t){if("string"!=typeof t)throw new Error("Param is not a string");switch(t.toLowerCase()){case"numeric":return r.NUMERIC;case"alphanumeric":return r.ALPHANUMERIC;case"kanji":return r.KANJI;case"byte":return r.BYTE;default:throw new Error("Unknown mode: "+t)}}(t)}catch(t){return e}}}));K.NUMERIC,K.ALPHANUMERIC,K.BYTE,K.KANJI,K.MIXED,K.getCharCountIndicator,K.getBestModeForData,K.isValid;var O=h((function(t,r){var e=i(7973);function n(t,r){return K.getCharCountIndicator(t,r)+4}function o(t,r){var e=0;return t.forEach((function(t){var o=n(t.mode,r);e+=o+t.getBitsLength()})),e}r.from=function(t,r){return b(t)?parseInt(t,10):r},r.getCapacity=function(t,r,e){if(!b(t))throw new Error("Invalid QR Code version");void 0===e&&(e=K.BYTE);var o=8*(a(t)-M(t,r));if(e===K.MIXED)return o;var i=o-n(e,t);switch(e){case K.NUMERIC:return Math.floor(i/10*3);case K.ALPHANUMERIC:return Math.floor(i/11*2);case K.KANJI:return Math.floor(i/13);case K.BYTE:default:return Math.floor(i/8)}},r.getBestVersionForData=function(t,e){var n,a=c.from(e,c.M);if(Array.isArray(t)){if(t.length>1)return function(t,e){for(var n=1;n<=40;n++){if(o(t,n)<=r.getCapacity(n,e,K.MIXED))return n}}(t,a);if(0===t.length)return 1;n=t[0]}else n=t;return function(t,e,n){for(var o=1;o<=40;o++)if(e<=r.getCapacity(o,n,t))return o}(n.mode,n.getLength(),a)},r.getEncodedBits=function(t){if(!b(t)||t<7)throw new Error("Invalid QR Code version");for(var r=t<<12;i(r)-e>=0;)r^=7973<=0;)n^=1335<0&&(e=this.data.substr(r),n=parseInt(e,10),t.put(n,3*o+1))};var j=q,$=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"," ","$","%","*","+","-",".","/",":"];function X(t){this.mode=K.ALPHANUMERIC,this.data=t}X.getBitsLength=function(t){return 11*Math.floor(t/2)+t%2*6},X.prototype.getLength=function(){return this.data.length},X.prototype.getBitsLength=function(){return X.getBitsLength(this.data.length)},X.prototype.write=function(t){var r;for(r=0;r+2<=this.data.length;r+=2){var e=45*$.indexOf(this.data[r]);e+=$.indexOf(this.data[r+1]),t.put(e,11)}this.data.length%2&&t.put($.indexOf(this.data[r]),6)};var Z=X;function W(t){this.mode=K.BYTE,this.data=new Uint8Array(function(t){for(var r=[],e=t.length,n=0;n=55296&&o<=56319&&e>n+1){var a=t.charCodeAt(n+1);a>=56320&&a<=57343&&(o=1024*(o-55296)+a-56320+65536,n+=1)}o<128?r.push(o):o<2048?(r.push(o>>6|192),r.push(63&o|128)):o<55296||o>=57344&&o<65536?(r.push(o>>12|224),r.push(o>>6&63|128),r.push(63&o|128)):o>=65536&&o<=1114111?(r.push(o>>18|240),r.push(o>>12&63|128),r.push(o>>6&63|128),r.push(63&o|128)):r.push(239,191,189)}return new Uint8Array(r).buffer}(t))}W.getBitsLength=function(t){return 8*t},W.prototype.getLength=function(){return this.data.length},W.prototype.getBitsLength=function(){return W.getBitsLength(this.data.length)},W.prototype.write=function(t){for(var r=0,e=this.data.length;r=33088&&e<=40956)e-=33088;else{if(!(e>=57408&&e<=60351))throw new Error("Invalid SJIS character: "+this.data[r]+"\nMake sure your charset is UTF-8");e-=49472}e=192*(e>>>8&255)+(255&e),t.put(e,13)}};var rt=tt,et=h((function(t){var r={single_source_shortest_paths:function(t,e,n){var o={},a={};a[e]=0;var i,u,s,f,h,c,g,d=r.PriorityQueue.make();for(d.push(e,0);!d.empty();)for(s in u=(i=d.pop()).value,f=i.cost,h=t[u]||{})h.hasOwnProperty(s)&&(c=f+h[s],g=a[s],(void 0===a[s]||g>c)&&(a[s]=c,d.push(s,c),o[s]=u));if(void 0!==n&&void 0===a[n]){var l=["Could not find a path from ",e," to ",n,"."].join("");throw new Error(l)}return o},extract_shortest_path_from_predecessor_list:function(t,r){for(var e=[],n=r;n;)e.push(n),n=t[n];return e.reverse(),e},find_path:function(t,e,n){var o=r.single_source_shortest_paths(t,e,n);return r.extract_shortest_path_from_predecessor_list(o,n)},PriorityQueue:{make:function(t){var e,n=r.PriorityQueue,o={};for(e in t=t||{},n)n.hasOwnProperty(e)&&(o[e]=n[e]);return o.queue=[],o.sorter=t.sorter||n.default_sorter,o},default_sorter:function(t,r){return t.cost-r.cost},push:function(t,r){var e={value:t,cost:r};this.queue.push(e),this.queue.sort(this.sorter)},pop:function(){return this.queue.shift()},empty:function(){return 0===this.queue.length}}};t.exports=r})),nt=h((function(t,r){function e(t){return unescape(encodeURIComponent(t)).length}function n(t,r,e){for(var n,o=[];null!==(n=t.exec(e));)o.push({data:n[0],index:n.index,mode:r,length:n[0].length});return o}function o(t){var r,e,o=n(J.NUMERIC,K.NUMERIC,t),a=n(J.ALPHANUMERIC,K.ALPHANUMERIC,t);return s()?(r=n(J.BYTE,K.BYTE,t),e=n(J.KANJI,K.KANJI,t)):(r=n(J.BYTE_KANJI,K.BYTE,t),e=[]),o.concat(a,r,e).sort((function(t,r){return t.index-r.index})).map((function(t){return{data:t.data,mode:t.mode,length:t.length}}))}function a(t,r){switch(r){case K.NUMERIC:return j.getBitsLength(t);case K.ALPHANUMERIC:return Z.getBitsLength(t);case K.KANJI:return rt.getBitsLength(t);case K.BYTE:return G.getBitsLength(t)}}function i(t,r){var e,n=K.getBestModeForData(t);if((e=K.from(r,n))!==K.BYTE&&e.bit=0?t[t.length-1]:null;return e&&e.mode===r.mode?(t[t.length-1].data+=r.data,t):(t.push(r),t)}),[])}(s))},r.rawSplit=function(t){return r.fromArray(o(t))}}));function ot(t,r,e){var n,o,a=t.size,i=V(r,e);for(n=0;n<15;n++)o=1==(i>>n&1),n<6?t.set(n,8,o,!0):n<8?t.set(n+1,8,o,!0):t.set(a-15+n,8,o,!0),n<8?t.set(8,a-n-1,o,!0):n<9?t.set(8,15-n-1+1,o,!0):t.set(8,15-n-1,o,!0);t.set(a-8,8,1,!0)}function at(t,r,e){var n=new d;e.forEach((function(r){n.put(r.mode.bit,4),n.put(r.getLength(),K.getCharCountIndicator(r.mode,t)),r.write(n)}));var o=8*(a(t)-M(t,r));for(n.getLengthInBits()+4<=o&&n.put(0,4);n.getLengthInBits()%8!=0;)n.putBit(0);for(var i=(o-n.getLengthInBits())/8,u=0;u=0&&u<=6&&(0===s||6===s)||s>=0&&s<=6&&(0===u||6===u)||u>=2&&u<=4&&s>=2&&s<=4?t.set(a+u,i+s,!0,!0):t.set(a+u,i+s,!1,!0))}(c,r),function(t){for(var r=t.size,e=8;e=7&&function(t,r){for(var e,n,o,a=t.size,i=O.getEncodedBits(r),u=0;u<18;u++)e=Math.floor(u/3),n=u%3+a-8-3,o=1==(i>>u&1),t.set(e,n,o,!0),t.set(n,e,o,!0)}(c,r),function(t,r){for(var e=t.size,n=-1,o=e-1,a=7,i=0,u=e-1;u>0;u-=2)for(6===u&&u--;;){for(var s=0;s<2;s++)if(!t.isReserved(o,u-s)){var f=!1;i>>a&1)),t.set(o,u-s,f),-1===--a&&(i++,a=7)}if((o+=n)<0||e<=o){o-=n,n=-n;break}}}(c,f),isNaN(n)&&(n=E.getBestMask(c,ot.bind(null,c,e))),E.applyMask(n,c),ot(c,e,n),{modules:c,version:r,errorCorrectionLevel:e,maskPattern:n,segments:a}}nt.fromArray,nt.fromString,nt.rawSplit;var ut=function(t,r){if(void 0===t||""===t)throw new Error("No input text");var e,n,o=c.M;return void 0!==r&&(o=c.from(r.errorCorrectionLevel,c.M),e=O.from(r.version),n=E.from(r.maskPattern),r.toSJISFunc&&u(r.toSJISFunc)),it(t,e,o,n)},st=h((function(t,r){function e(t){if("number"==typeof t&&(t=t.toString()),"string"!=typeof t)throw new Error("Color should be defined as hex string");var r=t.slice().replace("#","").split("");if(r.length<3||5===r.length||r.length>8)throw new Error("Invalid hex color: "+t);3!==r.length&&4!==r.length||(r=Array.prototype.concat.apply([],r.map((function(t){return[t,t]})))),6===r.length&&r.push("F","F");var e=parseInt(r.join(""),16);return{r:e>>24&255,g:e>>16&255,b:e>>8&255,a:255&e,hex:"#"+r.slice(0,6).join("")}}r.getOptions=function(t){t||(t={}),t.color||(t.color={});var r=void 0===t.margin||null===t.margin||t.margin<0?4:t.margin,n=t.width&&t.width>=21?t.width:void 0,o=t.scale||4;return{width:n,scale:n?4:o,margin:r,color:{dark:e(t.color.dark||"#000000ff"),light:e(t.color.light||"#ffffffff")},type:t.type,rendererOpts:t.rendererOpts||{}}},r.getScale=function(t,r){return r.width&&r.width>=t+2*r.margin?r.width/(t+2*r.margin):r.scale},r.getImageWidth=function(t,e){var n=r.getScale(t,e);return Math.floor((t+2*e.margin)*n)},r.qrToImageData=function(t,e,n){for(var o=e.modules.size,a=e.modules.data,i=r.getScale(o,n),u=Math.floor((o+2*n.margin)*i),s=n.margin*i,f=[n.color.light,n.color.dark],h=0;h=s&&c>=s&&h':"",s="0&&s>0&&t[u-1]||(n+=a?ct("M",s+e,.5+f+e):ct("m",o,0),o=0,a=!1),s+1',f='viewBox="0 0 '+i+" "+i+'"',h=''+u+s+"\n";return"function"==typeof e&&e(null,h),h};function dt(t,r,n,o,a){var i=[].slice.call(arguments,1),u=i.length,s="function"==typeof i[u-1];if(!s&&!e())throw new Error("Callback required as last argument");if(!s){if(u<1)throw new Error("Too few arguments provided");return 1===u?(n=r,r=o=void 0):2!==u||r.getContext||(o=n,n=r,r=void 0),new Promise((function(e,a){try{var i=ut(n,o);e(t(i,r,o))}catch(t){a(t)}}))}if(u<2)throw new Error("Too few arguments provided");2===u?(a=n,n=r,r=o=void 0):3===u&&(r.getContext&&void 0===a?(a=o,o=void 0):(a=o,o=n,n=r,r=void 0));try{var f=ut(n,o);a(null,t(f,r,o))}catch(t){a(t)}}var lt=ut,vt=dt.bind(null,ft.render),pt=dt.bind(null,ft.renderToDataURL),wt=dt.bind(null,(function(t,r,e){return gt(t,e)})),mt={create:lt,toCanvas:vt,toDataURL:pt,toString:wt};return t.create=lt,t.default=mt,t.toCanvas=vt,t.toDataURL=pt,t.toString=wt,Object.defineProperty(t,"__esModule",{value:!0}),t}({}); diff --git a/website/src/css/Raleway-Bold.woff2 b/website/src/css/Raleway-Bold.woff2 new file mode 100644 index 0000000000..4361c92c55 Binary files /dev/null and b/website/src/css/Raleway-Bold.woff2 differ diff --git a/website/src/css/Raleway-Medium.woff2 b/website/src/css/Raleway-Medium.woff2 new file mode 100644 index 0000000000..ec11f6bf70 Binary files /dev/null and b/website/src/css/Raleway-Medium.woff2 differ diff --git a/website/src/css/Raleway-Regular.woff2 b/website/src/css/Raleway-Regular.woff2 new file mode 100644 index 0000000000..ba1d8a9e29 Binary files /dev/null and b/website/src/css/Raleway-Regular.woff2 differ diff --git a/website/src/css/blog.css b/website/src/css/blog.css new file mode 100644 index 0000000000..3cceb226f8 --- /dev/null +++ b/website/src/css/blog.css @@ -0,0 +1,81 @@ +h1 { + font-size: 1.8rem; + font-weight: 600; + letter-spacing: 0.6px; +} + +section.container > div > p:nth-child(2) { + margin: 0; + margin-bottom: 2rem; + margin-top: 4px; + font-size: 1rem; +} + +h2 { + font-size: 1.6rem; + font-weight: 600; + letter-spacing: 0.6px; + margin-bottom: 1rem; + margin-top: 2.2rem; +} + +h3 { + font-size: 1.2rem; + font-weight: 600; + letter-spacing: 0.6px; + margin-bottom: 1rem; + margin-top: 1.8rem; +} + +ul li, +ol li { + text-decoration: none; + /* list-style: none; */ + font-size: 1.1rem; + line-height: 30px; + letter-spacing: 0.6px; + margin: 0.5rem 0; + -webkit-margin-start: 1rem; +} +ul, +ol { + list-style-position: inside; + /* list-style-type: decimal; */ + overflow: auto; +} +ul li::marker, +ol li::marker { + /* content: "-> "; */ + font-weight: 600; + /* color: #fbd561; */ +} +pre { + overflow: auto; +} +/* code{ + width: 100%; + height: auto; +} */ + +p { + margin: 1rem 0; +} + +html { + scroll-behavior: smooth; +} + +h1::before, +h2::before, +h3::before { + display: block; + content: " "; + margin-top: -80px; + height: 80px; + visibility: hidden; + pointer-events: none; +} + +:focus { + outline: none; +} diff --git a/website/src/css/blogs.css b/website/src/css/blogs.css new file mode 100644 index 0000000000..e58e9cbc0c --- /dev/null +++ b/website/src/css/blogs.css @@ -0,0 +1,208 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} +body { + font-family: Raleway, Arial, Helvetica, sans-serif; +} +h2, +h3, +p { + color: #062d56; +} +h2 { + font-size: 2.2rem; +} +h3 { + font-size: 1.8rem; +} +@media (max-width: 576px) { + h2 { + font-size: 1.9rem; + } + h3 { + font-size: 1.5rem; + } +} +p { + font-size: 1.1rem; + letter-spacing: 0.6px; + line-height: 30px; +} +a { + color: tomato; + font-size: 1.1rem; + letter-spacing: 0.6px; + text-decoration: none; +} +input, +button, +span.button { + border: none; + outline: none; + padding: 0.8rem 1.5rem; + font-size: 1rem; + letter-spacing: 1px; + border-radius: 25px; +} +input { + color: #062d56; + font-family: Raleway, Arial, Helvetica, sans-serif; + background-color: #f1f1f1; + width: 280px; +} +button, +span.button { + display: inline-block; + background-color: #02C0FF; + color: #fff; + text-align: center; + cursor: pointer; +} +button:active, +button:hover, +span.button:active, +span.button:hover { + filter: saturate(125%); +} +header { + background-color: #fbd561; + width: 100%; + position: sticky; + top: 0; + z-index: 10; + border-bottom: 1px solid #fff; +} +nav { + display: flex; + align-items: center; + justify-content: space-between; +} +nav .nav__buttons { + display: flex; + align-items: center; + flex-direction: row-reverse; + gap: 1rem; +} +nav .nav__buttons .button { + padding: 0.4rem 1.5rem; +} +nav .logo { + padding: 0.6rem 0rem; +} +button, +span.button { + display: inline-block; + background-color: #02C0FF; + color: #fff; + font-family: Raleway; + text-align: center; + cursor: pointer; +} +button:active, +span.button:active { + filter: saturate(125%); +} +.github-button { + visibility: hidden; +} + +footer { + padding: 1rem 0rem; + background-color: #f8f8f6; +} +footer > div { + display: flex; + justify-content: space-between; + align-items: center; +} +@media (max-width: 992px) { + footer > div { + justify-content: center; + } +} +footer p { + display: flex; + align-items: center; + gap: 0.2rem; +} +@media (max-width: 576px) { + footer p { + text-align: center; + } +} +footer .copyright img { + width: 22px; + height: 22px; +} +footer a { + display: flex; + align-items: center; +} + +@font-face { + font-family: Raleway; + src: url(./Raleway-Regular.woff2); + font-weight: normal; +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Medium.woff2); + font-weight: 500; +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Bold.woff2); + font-weight: bold; +} +.container { + max-width: 1320px; + margin: auto; + padding: 0rem 2rem; +} +@media (max-width: 1200px) { + .container { + max-width: 1140px; + } +} +@media (max-width: 992px) { + .container { + max-width: 960px; + } + .d-none-992 { + display: none !important; + } +} +@media (max-width: 768px) { + .container { + max-width: 720px; + } + .d-none-768 { + display: none !important; + } +} +@media (max-width: 576px) { + .container { + max-width: 100%; + padding: 0rem 1rem; + } + .d-none-576 { + display: none !important; + } +} +@media (min-width: 992px) { + .d-none-on-mobile { + display: none; + } +} +@media (max-width: 890px) { + .d-none-890 { + display: none; + } +} + +img{ + max-width: 100%; + height: auto; +} \ No newline at end of file diff --git a/website/src/css/contact.css b/website/src/css/contact.css new file mode 100644 index 0000000000..706c6fe90a --- /dev/null +++ b/website/src/css/contact.css @@ -0,0 +1,487 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} +body { + font-family: Raleway, Arial, Helvetica, sans-serif; +} +h2, +h3, +p, +ol { + color: #062d56; +} +h2 { + font-size: 2.2rem; +} +h3 { + font-size: 1.8rem; +} +@media (max-width: 576px) { + h2 { + font-size: 1.9rem; + } + h3 { + font-size: 1.5rem; + } +} +p, +ol { + font-size: 1.1rem; + letter-spacing: 0.6px; + line-height: 30px; +} +a { + color: tomato; + font-size: 1.1rem; + letter-spacing: 0.6px; + text-decoration: none; +} +input, +button, +span.button { + border: none; + outline: none; + padding: 0.8rem 1.5rem; + font-size: 1rem; + letter-spacing: 1px; + border-radius: 25px; +} +input { + color: #062d56; + font-family: Raleway, Arial, Helvetica, sans-serif; + background-color: #f1f1f1; + width: 280px; +} +button, +span.button { + display: inline-block; + background-color: #02c0ff; + color: #fff; + text-align: center; + cursor: pointer; +} +button:active, +button:hover, +span.button:active, +span.button:hover { + filter: saturate(125%); +} +header { + background-color: #fbd561; + width: 100%; + position: sticky; + top: 0; + z-index: 10; +} +nav { + display: flex; + align-items: center; + justify-content: space-between; +} +nav .nav__buttons { + display: flex; + align-items: center; + flex-direction: row-reverse; + gap: 1rem; +} +nav .nav__buttons .button { + padding: 0.4rem 1.5rem; +} +nav .logo { + padding: 0.6rem 0rem; +} +#simplex { + background-color: #fff; + padding: 5rem 0rem; +} +#simplex > div { + display: flex; + justify-content: space-between; + gap: 8rem; +} +#simplex > div .half { + flex: 1; + display: flex; + flex-direction: column; + gap: 1.5rem; + align-items: center; + justify-content: space-between; +} +@media (max-width: 1050px) { + #simplex > div { + flex-direction: column; + } +} +#simplex form.sign-up { + width: 100%; +} +#simplex form.sign-up div { + display: flex; + gap: 0.5rem; +} +@media (max-width: 576px) { + #simplex form.sign-up div { + flex-direction: column; + align-items: center; + width: inherit; + gap: 1rem; + } + #simplex form.sign-up div input { + display: block; + width: inherit; + } +} +#simplex #use-simplex a { + font-weight: 500; +} +#simplex #contacts { + display: flex; + flex-direction: column; + gap: 1rem; +} +#simplex #contacts span.button { + width: 100px; +} +#simplex #contacts a { + color: #062d56; + font-size: 1.1rem; + letter-spacing: 0.53px; + line-height: 25px; +} +#conn_req { + position: relative; + padding: 2rem 0rem; + overflow: hidden !important; +} +#conn_req h2 { + text-align: center; + margin-bottom: 2rem; +} +@media (max-width: 576px) { + #conn_req h2 { + text-align: left; + margin-bottom: 1rem; + } +} +#conn_req h2, +#conn_req p { + text-align: center; +} +#conn_req div.app__links { + width: 100%; + display: inline-flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 1.2rem; + line-height: 32px; + margin-bottom: 5rem; + vertical-align: middle; +} +#conn_req div.app__links a { + color: #062d56; + display: inline-flex; + font-weight: 400; + align-items: center; +} +#conn_req div.app__links a span { + font-weight: 500; + color: tomato; +} +@media (max-width: 576px) { + #conn_req div.app__links { + margin-bottom: 1.5rem; + } +} +#conn_req div.app__links img { + display: inline; + vertical-align: middle; +} +#conn_req .conn_req_uri { + display: flex; + justify-content: space-around; +} +@media (max-width: 1050px) { + #conn_req .conn_req_uri { + justify-content: flex-start; + gap: 10rem; + } +} +@media (max-width: 992px) { + #conn_req .conn_req_uri { + justify-content: center; + } +} +#conn_req .conn_req_uri > div, +#conn_req .conn_req_uri .conn_req__qrcode > div { + display: flex; + flex-direction: column; + align-items: center; + width: 360px; +} +@media (max-width: 576px) { + #conn_req .conn_req_uri > div, + #conn_req .conn_req_uri .conn_req__qrcode > div { + width: 100%; + } +} +#conn_req .conn_req_uri .conn_req__qrcode > div p { + padding: 0 2.4rem; + width: 360px; + margin-top: 40px; +} +@media (max-width: 576px) { + #conn_req .conn_req_uri .conn_req__qrcode > div p { + padding: 0; + width: 100%; + margin-top: 10px; + } +} +#conn_req .conn_req_uri .conn_req__qrcode > div .open-in-mobile { + padding: 0 2.4rem; + width: 360px; + margin: 30px 0 20px 0; +} +@media (max-width: 576px) { + #conn_req .conn_req_uri .conn_req__qrcode > div .open-in-mobile { + padding: 0; + width: 100%; + } +} +@media (min-width: 992px) { + #conn_req .conn_req_uri .open-in-mobile { + display: none; + } +} +@media (max-width: 576px) { + #conn_req .conn_req_uri .open-in-mobile { + display: flex; + align-items: center; + justify-content: center; + } + #conn_req .conn_req_uri .open-in-mobile a span { + padding: 0.8rem 2rem; + font-size: 1.3rem; + font-weight: 500; + } +} +#conn_req .conn_req_uri .conn_req__qrcode { + position: relative; + display: flex; + align-items: center; + justify-content: flex-start; +} +@media (max-width: 576px) { + #conn_req .conn_req_uri .conn_req__qrcode { + flex: 1; + } +} +#conn_req .conn_req_uri .conn_req__qrcode > div { + z-index: 2; +} +#conn_req .conn_req_uri .conn_req__qrcode img { + position: absolute; + width: 460px; + top: -3rem; +} +#conn_req .conn_req_uri .content_copy_with_tooltip { + display: flex; + align-items: flex-end; + gap: 1rem; + position: relative; +} +#conn_req .conn_req_uri .content_copy_with_tooltip .tooltip { + position: absolute; + display: inline-block; + right: -2rem; + bottom: 0; +} +#conn_req .conn_req_uri .content_copy_with_tooltip textarea { + word-break: break-all; + width: 360px; + height: 360px; + resize: none; + outline: none; + padding: 1rem; + box-sizing: border-box; + border-radius: 7px; + border-color: #ccc; +} +#conn_req .conn_req_uri canvas { + width: 100%; + height: auto; + margin-top: 20px; +} +@media (max-width: 576px) { + #conn_req .conn_req_uri canvas { + margin-top: 0px; + align-self: center; + } +} +#conn_req .conn_req_uri a.chat-for-terminal { + font-weight: 500; +} +#conn_req .content, +#install_chat .content { + font-family: "Courier New", Courier, monospace; + font-size: 1rem; + color: #000; +} +#conn_req .content_copy:hover, +#install_chat .content_copy:hover { + cursor: pointer; +} +#conn_req .tooltip, +#install_chat .tooltip { + position: relative; + display: inline-block; + font-family: Raleway, Arial, Helvetica, sans-serif; +} +#conn_req .tooltip .tooltiptext, +#install_chat .tooltip .tooltiptext { + visibility: hidden; + width: 140px; + background-color: rgba(255, 255, 255, 0.8); + text-align: center; + border: 1px solid #ccc; + border-radius: 4px; + padding: 5px; + position: absolute; + z-index: 1; + bottom: 150%; + left: 50%; + margin-left: -75px; + opacity: 0; + transition: opacity 0.3s; + font-size: 14px; +} +#conn_req .tooltip .tooltiptext::after, +#install_chat .tooltip .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #062d56 rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) rgba(0, 0, 0, 0); +} +#conn_req .tooltip:hover .tooltiptext, +#install_chat .tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} +#install_chat { + background-color: #f8f8f6; + padding: 4rem 0rem; +} +#install_chat h2, +#install_chat .install__command, +#install_chat .github__redirection { + display: flex; + flex-direction: column; + align-items: center; +} +#install_chat .github__redirection a { + font-weight: 500; + color: #062d56; + letter-spacing: 0.53px; + text-decoration: underline; +} +#install_chat .github__redirection a.github { + vertical-align: -5px; +} +#install_chat > div { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 1.5rem; +} +footer { + padding: 1rem 0rem; + background-color: #f8f8f6; +} +footer > div { + display: flex; + justify-content: space-between; + align-items: center; +} +@media (max-width: 992px) { + footer > div { + justify-content: center; + } +} +footer p { + display: flex; + align-items: center; + gap: 0.2rem; +} +@media (max-width: 576px) { + footer p { + text-align: center; + } +} +footer .copyright img { + width: 22px; + height: 22px; +} +footer a { + display: flex; + align-items: center; +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Regular.woff2); + font-weight: normal; +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Medium.woff2); + font-weight: 500; +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Bold.woff2); + font-weight: bold; +} +.container { + max-width: 1320px; + margin: auto; + padding: 0rem 2rem; +} +@media (max-width: 1200px) { + .container { + max-width: 1140px; + } +} +@media (max-width: 992px) { + .container { + max-width: 960px; + } + .d-none-992 { + display: none !important; + } +} +@media (max-width: 768px) { + .container { + max-width: 720px; + } +} +@media (max-width: 576px) { + .container { + max-width: 100%; + padding: 0rem 1rem; + } + .d-none-576 { + display: none !important; + visibility: hidden !important; + } +} +@media (min-width: 992px) { + .d-none-on-mobile { + display: none !important; + visibility: hidden !important; + } +} /*# sourceMappingURL=contact.css.map */ diff --git a/website/src/css/index.css b/website/src/css/index.css new file mode 100644 index 0000000000..17952ed2dc --- /dev/null +++ b/website/src/css/index.css @@ -0,0 +1,1209 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} +body { + font-family: Raleway, Arial, Helvetica, sans-serif; +} +h2, +h3, +p { + color: #062d56; +} +h2 { + font-size: 2.2rem; +} +h3 { + font-size: 1.8rem; +} +@media (max-width: 576px) { + h2 { + font-size: 1.9rem; + } + h3 { + font-size: 1.5rem; + } +} +p { + font-size: 1.1rem; + letter-spacing: 0.6px; + line-height: 30px; +} +a { + color: tomato; + font-size: 1.1rem; + letter-spacing: 0.6px; + text-decoration: none; +} +input, +button, +span.button { + border: none; + outline: none; + padding: 0.8rem 1.5rem; + font-size: 1rem; + letter-spacing: 1px; + border-radius: 25px; +} +input { + color: #062d56; + font-family: Raleway, Arial, Helvetica, sans-serif; + background-color: #f1f1f1; + width: 280px; +} +button, +span.button { + display: inline-block; + background-color: #02c0ff; + color: #fff; + text-align: center; + cursor: pointer; +} +button:active, +button:hover, +span.button:active, +span.button:hover { + filter: saturate(125%); +} +header { + background-color: #fbd561; + width: 100%; + position: sticky; + top: 0; + z-index: 10; + border-bottom: 1px solid #fff; +} +nav { + display: flex; + align-items: center; + justify-content: space-between; +} +nav .nav__buttons { + display: flex; + align-items: center; + flex-direction: row-reverse; + gap: 1rem; +} +nav .nav__buttons .button { + padding: 0.4rem 1.5rem; +} +nav .logo { + padding: 0.6rem 0rem; +} +button, +span.button { + display: inline-block; + background-color: #02c0ff; + color: #fff; + font-family: Raleway; + text-align: center; + cursor: pointer; +} +button:active, +span.button:active { + filter: saturate(125%); +} +.github-button { + visibility: hidden; +} +@media (max-width: 1050px) { + #demo div.all-users { + width: 100%; + } + #demo div.all-users div.user.bob .terminal, + #demo div.all-users div.user.tom .terminal { + width: 477px; + } + #demo div.all-users div.user.bob { + left: 397px; + } +} +@media (min-width: 1050px) { + #demo div.all-users { + width: 1020px; + } + #demo div.all-users div.user.bob .terminal, + #demo div.all-users div.user.tom .terminal { + width: 492px; + } + #demo div.all-users div.user.bob { + left: 528px; + } +} +#demo { + padding: 2rem 0rem; + background-image: url(../img/bg.svg), linear-gradient(180deg, #fbd561 0%, #efa647 100%); + background-size: 800px 800px, auto; + text-align: center; +} +#demo h1 { + font-size: 2.3rem; + margin-bottom: 2.5rem; +} +#demo h3 { + font-size: 1.5rem; + margin-bottom: 2rem; +} +#demo a { + font-weight: 500; +} +#demo div.container { + display: flex; + flex-direction: column; + align-items: center; +} +#demo div.app__links { + width: 100%; + display: inline-flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 1.2rem; + line-height: 32px; + vertical-align: middle; + margin-bottom: 2rem; +} +#demo div.app__links p { + font-size: 19px; + padding: 0; + width: fit-content; +} +#demo div.app__links a { + display: inline-flex; + align-items: center; + margin: 0; + font-family: Raleway, Arial, Helvetica, sans-serif; + font-weight: bold; + text-decoration: none; + color: #062d56; + letter-spacing: 0.53px; + font-weight: 400; +} +#demo div.app__links a span { + font-weight: 500; + color: tomato; +} +#demo div.app__links img { + display: inline; + vertical-align: middle; +} +#demo div.all-users { + position: relative; + height: 612px; +} +@media (max-width: 890px) { + #demo div.all-users { + display: flex; + align-items: center; + justify-content: center; + height: 450px; + } + #demo div.all-users div.user { + left: auto; + } +} +#demo div.all-users .simplex_mobile { + right: 30px; +} +@media (max-width: 1050px) { + #demo div.all-users .simplex_mobile { + right: 0; + } +} +#demo div.all-users .run-demo, +#demo div.all-users .run-faster, +#demo div.all-users .try-it { + position: absolute; + width: 140px; + height: 40px; + bottom: 70px; + border-radius: 20px; + font-size: 17px; + letter-spacing: 0.53px; +} +#demo div.all-users .run-demo:disabled, +#demo div.all-users .run-faster:disabled, +#demo div.all-users .try-it:disabled { + filter: brightness(75%); +} +#demo div.all-users .run-demo { + left: 60px; +} +#demo div.all-users .run-faster { + left: 60px; + display: none; +} +#demo div.all-users .try-it { + left: 220px; +} +#demo div.user { + position: absolute; +} +#demo div.user h3 { + position: absolute; + top: -38px; + text-align: left; + padding: 0 0 9px 12px; + color: #ffb; + mix-blend-mode: difference; +} +#demo div.user .terminal { + text-align: left; + position: relative; + background-color: rgba(0, 0, 0, 0.8); + border: 1px solid #fff; + border-radius: 10px; + box-sizing: border-box; + padding: 10px 10px; + color: #fff; + overflow-y: hidden; +} +#demo div.user .terminal:before { + content: ""; + display: block; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 20px; + background: #666 url(../img/topbar.svg) no-repeat 8px center; + border-radius: 10px 10px 0 0; + z-index: 2; +} +#demo div.user .terminal input, +#demo div.user .terminal .input, +#demo div.user .terminal .display { + font-family: Menlo, "Lucida Console", Monaco, monospace; + font-size: 14px; + letter-spacing: 0.27px; + line-height: 28px; + position: absolute; + left: 10px; + right: 10px; +} +#demo div.user .terminal input, +#demo div.user .terminal .input { + color: #fff; + display: block; + height: 30px; + bottom: 0; +} +#demo div.user .terminal input { + background-color: #000; + display: none; + left: 25px; + width: calc(100% - 30px); +} +#demo div.user .terminal .display { + color: #fff; + text-align: left; + bottom: 30px; + z-index: 1; + overflow-x: hidden; + word-wrap: break-word; +} +#demo div.user .terminal .display div.info { + color: #fff; +} +#demo div.user .terminal .display div.error { + color: tomato; +} +#demo div.user .terminal .display div.sent span.group { + color: aqua; +} +#demo div.user .terminal .display div.received span.group { + color: #ff0; +} +#demo div.user .terminal .display span { + font-family: Menlo, Monaco, "Lucida Console", monospace; + text-align: left; + margin: 0 0; +} +#demo div.user .terminal .display span.recipient { + color: aqua; +} +#demo div.user .terminal .display span.sender { + color: #ff0; +} +#demo div.user .terminal .display span.secret { + color: rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 0.4); +} +#demo div.user.alice { + left: 0; + top: 68px; +} +#demo div.user.alice .terminal { + width: 503px; + height: 362px; +} +@media (max-width: 570px) { + #demo div.user.alice .terminal { + width: 400px; + } +} +@media (max-width: 440px) { + #demo div.user.alice .terminal { + width: 330px; + } +} +#demo div.user.bob { + top: 38px; +} +#demo div.user.bob .terminal { + height: 282px; +} +#demo div.user.tom { + left: 397px; + top: 358px; +} +#demo div.user.tom .terminal { + height: 254px; +} +#demo .content_copy_with_tooltip { + background-color: #f8f8f6; + border-radius: 50px; + padding-bottom: 4px; + padding-top: 8px; + margin-top: 16px; + margin-bottom: 16px; +} +#demo .content_copy_with_tooltip .tooltip { + vertical-align: -6px; +} +#demo .content_copy_with_tooltip .content { + font-size: 15px; +} +#conn_req h1, +#conn_req h2, +#conn_req h3, +#install_chat h1, +#install_chat h2, +#install_chat h3, +#demo h1, +#demo h2, +#demo h3 { + color: #062d56; +} +#conn_req p.install, +#install_chat p.install, +#demo p.install { + padding: 0.4rem 1.5rem; +} +#conn_req a.github, +#install_chat a.github, +#demo a.github { + vertical-align: -5px; +} +#conn_req a, +#install_chat a, +#demo a { + color: #124d86; +} +#conn_req .content, +#install_chat .content, +#demo .content { + font-family: "Courier New", Courier, monospace; + font-size: 14px; + color: #000; +} +#conn_req .content_copy:hover, +#install_chat .content_copy:hover, +#demo .content_copy:hover { + cursor: pointer; +} +#conn_req .tooltip, +#install_chat .tooltip, +#demo .tooltip { + position: relative; + display: inline-block; +} +#conn_req .tooltip .tooltiptext, +#install_chat .tooltip .tooltiptext, +#demo .tooltip .tooltiptext { + visibility: hidden; + width: 140px; + background-color: rgba(255, 255, 255, 0.8); + text-align: center; + border: 1px solid #ccc; + border-radius: 4px; + padding: 5px; + position: absolute; + z-index: 1; + bottom: 150%; + left: 50%; + margin-left: -75px; + opacity: 0; + transition: opacity 0.3s; + font-size: 14px; +} +#conn_req .tooltip .tooltiptext::after, +#install_chat .tooltip .tooltiptext::after, +#demo .tooltip .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #062d56 rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) rgba(0, 0, 0, 0); +} +#conn_req .tooltip:hover .tooltiptext, +#install_chat .tooltip:hover .tooltiptext, +#demo .tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} +#conn_req { + margin-top: 120px; + height: 700px; + position: relative; + display: block; +} +#conn_req .conn_req_uri { + width: 960px; + margin: 0 auto; +} +#conn_req .conn_req_uri .content_copy_with_tooltip { + float: left; + width: 500px; + padding: 24px 0 0 24px; +} +#conn_req .conn_req_uri .content_copy_with_tooltip textarea { + word-break: break-all; + width: 340px; + height: 340px; + resize: none; + outline: none; + padding: 5px; + box-sizing: border-box; + border-radius: 4px; + border-color: #ccc; +} +#conn_req .conn_req_uri canvas { + width: 360px; + height: 360px; + padding-top: 12px; +} +#conn_req span.button { + border-radius: 18px; + height: 40px; + line-height: 40px; + width: 200px; +} +#install_chat { + padding-top: 24px; + height: 345px; + background-color: #f8f8f6; +} +figure.party { + display: flex; + align-items: flex-end; + justify-content: center; + width: 102px; + height: 139px; + background: no-repeat center top; + position: relative; +} +figure.party.small { + width: 90px; + height: 118px; + background-size: 88px 88px; +} +figure.party.small:before { + width: 88px; + height: 88px; +} +figure.party:before { + content: ""; + display: block; + position: absolute; + border-radius: 100%; + top: 0; + left: 1px; + width: 99px; + height: 99px; + background-color: #fff; + z-index: -1; +} +figure.party.transparent:before { + opacity: 25%; +} +figure.party.alice { + background-image: url(../img/alice.svg); +} +figure.party.bob { + background-image: url(../img/bob.svg); +} +#introduction { + padding: 2rem 0rem; + background-image: linear-gradient(180deg, #fbd561 0%, #efa647 100%); +} +#introduction div.container { + display: flex; + flex-direction: column; + align-items: center; +} +#introduction h2 { + color: tomato; + margin-bottom: 2.5rem; +} +#introduction p { + text-align: center; + padding-top: 3px; +} +#introduction p:first-of-type { + padding-top: 36px; +} +#introduction figure.section { + display: flex; + align-items: center; + justify-content: space-between; + max-width: 900px; + margin-top: 70px; +} +#introduction figure.section .feature { + display: flex; + align-items: center; + flex-direction: column; + width: 300px; +} +#introduction figure.section .feature p { + text-align: center; + width: 85%; + padding-top: 12px; +} +#introduction figure.section .feature h4 { + padding-top: 12px; +} +#introduction figure.section .doodle { + border: 4px solid #fff; + border-radius: 100%; + width: 163px; + height: 163px; + position: relative; + margin: 0 auto; + background: url(../img/doodle.svg); + background-size: 400px 400px; + background-clip: content-box; +} +#introduction figure.section .doodle:before { + content: ""; + display: block; + position: absolute; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + border: 7px solid #f8b650; + border-radius: 100%; +} +#introduction figure.section .doodle.control { + background-position: 410px 235px; +} +#introduction figure.section .doodle.privacy { + background-position: 185px 350px; +} +#introduction figure.section .doodle.convenience { + background-position: 305px 160px; +} +@media (max-width: 930px) { + #introduction figure.section { + flex-direction: column; + gap: 2.5rem; + } +} +#problem { + padding: 3rem 0rem; + background-color: #f8f8f6; + position: relative; + z-index: 1; +} +#problem .problem-section { + align-self: start; + overflow: auto; +} +#problem .explained { + display: none; +} +#problem div.container { + display: flex; + flex-direction: column; + align-items: center; +} +#problem h2 { + color: tomato; + text-align: center; + margin-bottom: 2.5rem; +} +#problem h3 { + text-align: center; + margin-bottom: 1.5rem; +} +#problem p { + text-align: center; +} +#problem #problem-explained { + display: none; +} +#problem #problem-explained:target ~ .intro { + display: none; +} +#problem #problem-explained:target ~ .explained { + display: block; +} +#problem #problem-explained:target ~ .pagination .page.intro { + background-color: #cdcdcd; +} +#problem #problem-explained:target ~ .pagination .page.explained { + background-color: #3e96fc; +} +#problem .pagination { + width: 44px; + height: 12px; + margin: 0 auto; +} +#problem .pagination .page { + display: block; + float: left; + background-color: #cdcdcd; + border-radius: 100%; + width: 14px; + height: 14px; +} +#problem .pagination .page.intro { + margin-right: 16px; + background-color: #3e96fc; +} +#problem .communication { + width: 645px; + height: 148px; + margin: 40px auto 15px auto; +} +#problem .communication figure { + float: left; +} +#problem .communication img { + display: block; + float: left; + margin: 20px -12px; + position: relative; + z-index: -2; +} +#problem .communication .tom { + text-align: center; + position: relative; + bottom: 48px; + right: 3px; +} +@supports (background-blend-mode: multiply) and (not (-webkit-touch-callout: none)) { + #solution { + background-image: url(../img/doodle.svg); + background-blend-mode: multiply; + } +} +#solution { + padding: 4rem 0rem; + background-color: #062d56; + background-size: 800px 800px; +} +#solution div.container { + display: flex; + flex-direction: column; + align-items: center; +} +#solution h2 { + color: tomato; + text-align: center; +} +#solution p { + text-align: center; + color: #fff; + padding-top: 12px; +} +#solution p:first-of-type { + padding-top: 53px; +} +#connection { + padding: 2rem 0rem; + background-color: #f8f8f6; +} +#connection h2 { + color: tomato; + margin-bottom: 2rem; + text-align: center; +} +#connection h3 { + margin-bottom: 1.5rem; + text-align: center; +} +#connection h4 { + color: #062d56; + font-size: 21px; + line-height: 29px; + margin-bottom: 1rem; + text-align: center; +} +#connection p { + margin-bottom: 2.5rem; + text-align: center; +} +#connection a { + font-weight: 500; +} +#connection div.container { + display: flex; + flex-direction: column; + align-items: center; +} +#connection figure.section { + display: flex; + align-items: center; + gap: 0.5rem; +} +#connection figure.section .arrow { + width: 40px; +} +#connection figure.section .party { + margin-top: 2rem; +} +#connection figure.conn-stage { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} +#connection figure.conn-stage p { + width: 170px; + font-size: 15px; + color: #062d56; + margin-bottom: 0.3rem; +} +#connection figure.device { + width: 185px; +} +@media (max-width: 992px) { + #connection figure.section { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + } + #connection figure.section > *:nth-child(2) { + margin: auto; + width: 50px; + } + #connection figure.section > *:nth-child(4) { + grid-column-start: 3; + margin: auto; + width: 50px; + transform: rotate(90deg); + padding: 3.5rem 0; + } + #connection figure.section > *:nth-child(5) { + grid-column-start: 3; + } + #connection figure.section > *:nth-child(6) { + grid-column-start: 2; + grid-row-start: 3; + margin: auto; + width: 50px; + transform: rotate(180deg); + } + #connection figure.section > *:nth-child(7) { + grid-column-start: 1; + grid-row-start: 3; + } +} +@media (max-width: 500px) { + #connection figure.section { + display: flex; + flex-direction: column; + } + #connection figure.section > *:nth-child(even) { + transform: rotate(90deg); + padding: 1.5rem 0; + } +} +#comparison { + padding: 2rem 0rem; + overflow: hidden; + position: relative; +} +#comparison .table-holder { + /* align-self: start; */ + overflow: auto; +} +#comparison div.container { + display: flex; + flex-direction: column; + align-items: center; +} +#comparison h2 { + color: tomato; + text-align: center; +} +#comparison table, +#comparison ol, +#comparison ul { + font-family: Raleway, Arial, Helvetica, sans-serif; + color: #062d56; +} +#comparison table { + border-collapse: separate; + border-spacing: 10px; + margin: 59px auto 0 auto; + background-color: #f8f8f6; + padding-bottom: 60px; +} +#comparison table th { + font-weight: normal; + height: 40px; +} +#comparison table th.simplex { + font-weight: bold; +} +#comparison table th.simplex img { + width: 25px; + height: 23px; + vertical-align: -6px; +} +#comparison table td, +#comparison table th { + font-size: 15px; + font-weight: normal; + font-stretch: normal; + font-style: normal; + line-height: 1.4; + letter-spacing: 0.47px; +} +#comparison table td .equal, +#comparison table th .equal { + font-size: 18px; +} +#comparison table td { + height: 40px; + margin: 0 10px 10px 0; +} +#comparison table td.highlight { + text-align: center; + color: #fff; + font-weight: 500; +} +#comparison table td.bad { + background-color: #f95a2c; +} +#comparison table td.medium { + background-color: #ffbd12; +} +#comparison table td.good { + background-color: #02c0ff; +} +#comparison .table-comments { + padding: 0 2rem; + display: flex; + gap: 4rem; +} +#comparison .table-comments ul.legend, +#comparison .table-comments .footnotes { + font-size: 13px; + font-weight: normal; + font-stretch: normal; + font-style: normal; + margin-top: 29px; +} +#comparison .table-comments ul.legend { + list-style: none; + line-height: 2.31; + letter-spacing: 0.41px; + padding-top: 20px; +} +#comparison .table-comments ul.legend li.bad::before { + background-color: #f95a2c; +} +#comparison .table-comments ul.legend li.medium::before { + background-color: #ffbd12; +} +#comparison .table-comments ul.legend li.good::before { + background-color: #02c0ff; +} +#comparison .table-comments ul.legend li::before { + content: ""; + display: inline-block; + width: 20px; + height: 20px; + margin-right: 10px; + vertical-align: -6px; +} +#comparison .table-comments .footnotes { + line-height: 1.62; + letter-spacing: 0.41px; +} +#comparison .table-comments .footnotes a { + color: #f95a2c; + font-weight: bold; + font-size: 0.8rem; + text-decoration: underline; +} +@media (max-width: 1050px) { + #comparison table { + width: 900px; + } + #comparison table td { + width: 190px; + } + #comparison table td.highlight { + width: 140px; + } + #comparison .table-comments { + max-width: 900px; + } +} +@media (min-width: 1050px) { + #comparison table { + width: 1050px; + } + #comparison table td { + width: 215px; + } + #comparison table td.highlight { + width: 155px; + } + #comparison .table-comments { + max-width: 1050px; + } +} +#messaging { + background-image: linear-gradient(180deg, #fbd561 0%, #efa647 100%); + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem 0rem; +} +#messaging img { + max-width: 100%; +} +#messaging div.container div { + max-width: 750px; + text-align: center; +} +#messaging h2 { + color: tomato; + margin-bottom: 2rem; +} +#messaging h3 { + margin-bottom: 1.5rem; +} +#messaging p { + margin-bottom: 2.5rem; +} +#messaging figure.section { + display: flex; + align-items: center; + justify-content: center; +} +#messaging figure.section .party { + margin: 80px 9px; +} +#chat { + background-color: #f8f8f6; + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem 0rem; +} +#chat img { + max-width: 100%; +} +#chat div.container div { + max-width: 740px; + text-align: center; +} +#chat div.container div h2 { + color: tomato; + margin-bottom: 2rem; +} +#chat div.container div h3 { + margin-bottom: 1.5rem; +} +#chat div.container div p { + margin-bottom: 2.5rem; +} +#chat figure.section { + display: flex; + align-items: center; + justify-content: center; +} +#chat figure.section .party { + margin: 80px 9px; +} +#simplex { + background-color: #fff; + padding: 5rem 0rem; +} +#simplex h3 { + text-align: center; +} +#simplex > div { + display: flex; + justify-content: space-between; + gap: 8rem; +} +#simplex > div .half { + flex: 1; + display: flex; + flex-direction: column; + gap: 1.5rem; + justify-content: space-between; +} +@media (max-width: 1050px) { + #simplex > div { + flex-direction: column; + } +} +#simplex form.sign-up { + width: 100%; +} +#simplex form.sign-up div { + display: flex; + gap: 0.5rem; +} +@media (max-width: 576px) { + #simplex form.sign-up div { + flex-direction: column; + align-items: center; + width: inherit; + gap: 1rem; + } + #simplex form.sign-up div input { + display: block; + width: inherit; + } +} +#simplex #use-simplex a { + font-weight: 500; +} +#simplex #contacts { + display: flex; + flex-direction: column; + gap: 1rem; +} +#simplex #contacts span.button { + width: 100px; +} +#simplex #contacts a { + color: #062d56; + font-size: 1.1rem; + letter-spacing: 0.53px; + line-height: 25px; +} +.d-none { + display: none; +} +footer { + padding: 1rem 0rem; + background-color: #f8f8f6; +} +footer > div { + display: flex; + justify-content: space-between; + align-items: center; +} +@media (max-width: 992px) { + footer > div { + justify-content: center; + } +} +footer p { + display: flex; + align-items: center; + gap: 0.2rem; +} +@media (max-width: 576px) { + footer p { + text-align: center; + } +} +footer .copyright img { + width: 22px; + height: 22px; +} +footer a { + display: flex; + align-items: center; +} +footer .copyright__text_and_links { + display: flex; +} +footer .copyright__text_and_links div { + padding: 0 0.2rem; + display: flex; + gap: 3px; +} +@media (max-width: 576px) { + footer .copyright__text_and_links { + flex-direction: column; + align-items: center; + gap: 1rem; + } +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Regular.woff2); + font-weight: normal; +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Medium.woff2); + font-weight: 500; +} +@font-face { + font-family: Raleway; + src: url(./Raleway-Bold.woff2); + font-weight: bold; +} +.container { + max-width: 1320px; + margin: auto; + padding: 0rem 2rem; +} +@media (max-width: 1200px) { + .container { + max-width: 1140px; + } +} +@media (max-width: 992px) { + .container { + max-width: 960px; + } + .d-none-992 { + display: none !important; + } +} +@media (max-width: 768px) { + .container { + max-width: 720px; + } + .d-none-768 { + display: none !important; + } +} +@media (max-width: 576px) { + .container { + max-width: 100%; + padding: 0rem 1rem; + } + .d-none-576 { + display: none !important; + } +} +@media (min-width: 992px) { + .d-none-on-mobile { + display: none; + } +} +@media (max-width: 890px) { + .d-none-890 { + display: none; + } +} /*# sourceMappingURL=index.css.map */ diff --git a/website/src/css/reset.css b/website/src/css/reset.css new file mode 100644 index 0000000000..69e48f238a --- /dev/null +++ b/website/src/css/reset.css @@ -0,0 +1,74 @@ +/* Box sizing rules */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margin */ +body, +h1, +h2, +h3, +h4, +p, +figure, +blockquote, +dl, +dd { + margin: 0; +} + +/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */ +ul[role="list"], +ol[role="list"] { + list-style: none; +} + +/* Set core root defaults */ +html:focus-within { + scroll-behavior: smooth; +} + +/* Set core body defaults */ +body { + min-height: 100vh; + text-rendering: optimizeSpeed; + line-height: 1.5; +} + +/* A elements that don't have a class get default styles */ +a:not([class]) { + text-decoration-skip-ink: auto; +} + +/* Make images easier to work with */ +img, +picture { + max-width: 100%; + display: block; +} + +/* Inherit fonts for inputs and buttons */ +input, +button, +textarea, +select { + font: inherit; +} + +/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */ +@media (prefers-reduced-motion: reduce) { + html:focus-within { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/website/src/css/style.css b/website/src/css/style.css new file mode 100644 index 0000000000..209fb3514f --- /dev/null +++ b/website/src/css/style.css @@ -0,0 +1,17 @@ +html { + scroll-behavior: smooth; +} + +#comparison::before { + display: block; + content: " "; + margin-top: -80px; + height: 120px; + visibility: hidden; + pointer-events: none; +} + +/* .nav-button-active{ + background-color: #00C8FB; + color: #fff; +} */ diff --git a/website/src/img/1_show_qr_code.png b/website/src/img/1_show_qr_code.png new file mode 100644 index 0000000000..95bec2bf14 Binary files /dev/null and b/website/src/img/1_show_qr_code.png differ diff --git a/website/src/img/2_scan_qr_code.png b/website/src/img/2_scan_qr_code.png new file mode 100644 index 0000000000..9a6166f510 Binary files /dev/null and b/website/src/img/2_scan_qr_code.png differ diff --git a/website/src/img/3_connected.png b/website/src/img/3_connected.png new file mode 100644 index 0000000000..b7b2e0c38f Binary files /dev/null and b/website/src/img/3_connected.png differ diff --git a/website/src/img/4_chat.png b/website/src/img/4_chat.png new file mode 100644 index 0000000000..b977f57850 Binary files /dev/null and b/website/src/img/4_chat.png differ diff --git a/website/src/img/alice.svg b/website/src/img/alice.svg new file mode 100644 index 0000000000..5501242644 --- /dev/null +++ b/website/src/img/alice.svg @@ -0,0 +1,25 @@ + + + Group 4 + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/apk_icon.png b/website/src/img/apk_icon.png new file mode 100644 index 0000000000..60ff342d36 Binary files /dev/null and b/website/src/img/apk_icon.png differ diff --git a/website/src/img/apple_store.svg b/website/src/img/apple_store.svg new file mode 100644 index 0000000000..b977fa2cf9 --- /dev/null +++ b/website/src/img/apple_store.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/arrow.svg b/website/src/img/arrow.svg new file mode 100644 index 0000000000..31157c4b9b --- /dev/null +++ b/website/src/img/arrow.svg @@ -0,0 +1,19 @@ + + + + 4BF2AAB0-320C-476F-91DE-744A4F8CD509 + Created with sketchtool. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/src/img/bg.svg b/website/src/img/bg.svg new file mode 100644 index 0000000000..958b8e76b1 --- /dev/null +++ b/website/src/img/bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/bob.svg b/website/src/img/bob.svg new file mode 100644 index 0000000000..31be156266 --- /dev/null +++ b/website/src/img/bob.svg @@ -0,0 +1,20 @@ + + + Group 3 + + + + + + + + + + + + + + + + + diff --git a/website/src/img/chat.png b/website/src/img/chat.png new file mode 100644 index 0000000000..57afdf6135 Binary files /dev/null and b/website/src/img/chat.png differ diff --git a/website/src/img/chat.svg b/website/src/img/chat.svg new file mode 100644 index 0000000000..4560615f2b --- /dev/null +++ b/website/src/img/chat.svg @@ -0,0 +1,150 @@ + + + row 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/conn1.svg b/website/src/img/conn1.svg new file mode 100644 index 0000000000..dcb676411f --- /dev/null +++ b/website/src/img/conn1.svg @@ -0,0 +1,29 @@ + + + Group 12 + + + + + + + + + + + + + + + + + + + + ADD CONTACT + + + + + + \ No newline at end of file diff --git a/website/src/img/conn2.svg b/website/src/img/conn2.svg new file mode 100644 index 0000000000..6a21cadb2e --- /dev/null +++ b/website/src/img/conn2.svg @@ -0,0 +1,29 @@ + + + Group 15 + + + + + + + + + + + + + + + + + + + SCAN QR CODE + + + + + + + diff --git a/website/src/img/conn3.svg b/website/src/img/conn3.svg new file mode 100644 index 0000000000..d92b939302 --- /dev/null +++ b/website/src/img/conn3.svg @@ -0,0 +1,45 @@ + + + Group 25 + + + + + + + + + + + + + + + + + + + + + + Bob + + + + + + + + + + + + + CONFIRM + + + + + + + diff --git a/website/src/img/conn4.svg b/website/src/img/conn4.svg new file mode 100644 index 0000000000..84a5c4041b --- /dev/null +++ b/website/src/img/conn4.svg @@ -0,0 +1,34 @@ + + + button + + + + + + + + + + + + CHAT: ALICE + + + Alice: Hello + + + + + + Alice: Where + are you? + + + Hello + + + + + + diff --git a/website/src/img/doodle.svg b/website/src/img/doodle.svg new file mode 100644 index 0000000000..78f0ae2bd2 --- /dev/null +++ b/website/src/img/doodle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/f_droid.svg b/website/src/img/f_droid.svg new file mode 100644 index 0000000000..2d6ec68710 --- /dev/null +++ b/website/src/img/f_droid.svg @@ -0,0 +1,372 @@ + + + + + + + + + + + + + GET IT ON + F-Droid + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/favicon.ico b/website/src/img/favicon.ico new file mode 100644 index 0000000000..c45f3acf04 Binary files /dev/null and b/website/src/img/favicon.ico differ diff --git a/website/src/img/google_play.svg b/website/src/img/google_play.svg new file mode 100644 index 0000000000..6cda5f6d31 --- /dev/null +++ b/website/src/img/google_play.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/icon.svg b/website/src/img/icon.svg new file mode 100644 index 0000000000..6872614cf3 --- /dev/null +++ b/website/src/img/icon.svg @@ -0,0 +1,16 @@ + + + + 7D1F159A-EDEB-4E00-A1B8-9D852C99C1C8 + Created with sketchtool. + + + + + + + + + + + \ No newline at end of file diff --git a/website/src/img/icons/at.svg b/website/src/img/icons/at.svg new file mode 100644 index 0000000000..a71ceff309 --- /dev/null +++ b/website/src/img/icons/at.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/icons/content-copy.svg b/website/src/img/icons/content-copy.svg new file mode 100644 index 0000000000..0968db1a75 --- /dev/null +++ b/website/src/img/icons/content-copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/icons/email.svg b/website/src/img/icons/email.svg new file mode 100644 index 0000000000..b43653a071 --- /dev/null +++ b/website/src/img/icons/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/icons/github.svg b/website/src/img/icons/github.svg new file mode 100644 index 0000000000..8d837acd02 --- /dev/null +++ b/website/src/img/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/icons/linkedin.svg b/website/src/img/icons/linkedin.svg new file mode 100644 index 0000000000..65f54f28d9 --- /dev/null +++ b/website/src/img/icons/linkedin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/icons/reddit.svg b/website/src/img/icons/reddit.svg new file mode 100644 index 0000000000..1c9a4ab7a4 --- /dev/null +++ b/website/src/img/icons/reddit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/icons/twitter.svg b/website/src/img/icons/twitter.svg new file mode 100644 index 0000000000..bb5c58d872 --- /dev/null +++ b/website/src/img/icons/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/img/iphone.png b/website/src/img/iphone.png new file mode 100644 index 0000000000..af008e1ff4 Binary files /dev/null and b/website/src/img/iphone.png differ diff --git a/website/src/img/messaging.png b/website/src/img/messaging.png new file mode 100644 index 0000000000..9430c09876 Binary files /dev/null and b/website/src/img/messaging.png differ diff --git a/website/src/img/messaging.svg b/website/src/img/messaging.svg new file mode 100644 index 0000000000..51c8581561 --- /dev/null +++ b/website/src/img/messaging.svg @@ -0,0 +1,124 @@ + + + Group 14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/mobile.png b/website/src/img/mobile.png new file mode 100644 index 0000000000..fbda63a576 Binary files /dev/null and b/website/src/img/mobile.png differ diff --git a/website/src/img/problem11.svg b/website/src/img/problem11.svg new file mode 100644 index 0000000000..ff73751ebb --- /dev/null +++ b/website/src/img/problem11.svg @@ -0,0 +1,103 @@ + + + Group 22 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/problem12.svg b/website/src/img/problem12.svg new file mode 100644 index 0000000000..66f585bc37 --- /dev/null +++ b/website/src/img/problem12.svg @@ -0,0 +1,118 @@ + + + Group 7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/problem21.svg b/website/src/img/problem21.svg new file mode 100644 index 0000000000..d711fba43a --- /dev/null +++ b/website/src/img/problem21.svg @@ -0,0 +1,121 @@ + + + Group 21 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/problem22.svg b/website/src/img/problem22.svg new file mode 100644 index 0000000000..d4c542339f --- /dev/null +++ b/website/src/img/problem22.svg @@ -0,0 +1,147 @@ + + + Group 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/img/share_simplex.png b/website/src/img/share_simplex.png new file mode 100644 index 0000000000..17e4c05bbc Binary files /dev/null and b/website/src/img/share_simplex.png differ diff --git a/website/src/img/simplex.svg b/website/src/img/simplex.svg new file mode 100644 index 0000000000..b845ff219d --- /dev/null +++ b/website/src/img/simplex.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/website/src/img/simplex_mobile.png b/website/src/img/simplex_mobile.png new file mode 100644 index 0000000000..e1e51d1f46 Binary files /dev/null and b/website/src/img/simplex_mobile.png differ diff --git a/website/src/img/testflight.png b/website/src/img/testflight.png new file mode 100644 index 0000000000..8111a69d54 Binary files /dev/null and b/website/src/img/testflight.png differ diff --git a/website/src/img/topbar.svg b/website/src/img/topbar.svg new file mode 100644 index 0000000000..0280d9f620 --- /dev/null +++ b/website/src/img/topbar.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/website/src/index.html b/website/src/index.html new file mode 100644 index 0000000000..2d961cc478 --- /dev/null +++ b/website/src/index.html @@ -0,0 +1,426 @@ + + + + + + + SimpleX Chat: private, secure, no user identitifiers + + + + + + + + + + + + + + + + + + {% include "nav.html" %} + +
+
+

SimpleX Chat

+

Private & encrypted - the only platform without user identifiers!

+ +

You can download terminal chat app from GitHub or using this command:

+

+ curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/install.sh | bash + + Copy to clipboard + + +

+
+
+

Alice

+
+
+
+ +
+
+
+

Bob

+
+
+
+ +
+
+
+ +
+
+

Tom

+
+
+
+ +
+
+ + + +
+
+
+ +
+
+

Privacy redefined

+

SimpleX advantage:

+

SimpleX ensures data and meta-data privacy

+

with a new protocol design – it delivers messages

+

without using senders' and recipients' identifiers,

+

unlike any other messaging platform.

+
+
+
+

Security

+

Two-layer encryption and communication integrity

+
+
+
+

Privacy

+

Your contacts and messages are stored on your device

+
+
+
+

Convenience

+

Easy to add contacts and start talking

+
+
+
+
+ +
+
+

Problem: chat security

+ +
+

End-to-end encryption?

+

Alice sends the key to Bob (e.g. via p2p network or via chat server)

+
+
+
Alice
+ Alice sends the key to Bob +
Bob
+
+
+

Now Bob can send encrypted messages to Alice - he believes it is secure!

+
+
+
Alice
+ They believe it is secure +
Bob
+
+
+
+
+

Man-in-the-middle attack!

+

But the key can be intercepted and substituted by Tom (the attacker)

+
+
+
Alice
+ key is intercepted +
Bob
+
Tom
+
+
+

Now the attacker can read the messages without Alice or Bob knowing

+
+
+
Alice
+ attacker can read messages +
Bob
+
Tom
+
+
+
+ +
+
+ +
+
+

What is the solution?

+

+ To create secure encrypted channel you need an existing secure channel +
where you can pass the encryption key (or key fingerprint). +

+

Any alternative solution can be compromised.

+
+
+ +
+
+

Make a private connection!

+

To add contact and to start chat

+

+ Adding a contact in mobile app will require sharing your one-time QR code. +
In the terminal UI you need to share an invitation with your contact. +

+

After creating your chat profile in the app:

+
+
+

1.
Show QR code or share link

+
+ mobile app: show QR code to add contact +
+
Alice
+
+ +
+

2.
Your contact scans QR code or opens link

+
+ mobile app: scan QR code +
+
Bob
+
+ +
+

3.
A private connection is established

+
+ mobile app: confirm contact +
+
Alice
+
+ +
+

4.
Now you can chat privately!

+
+ mobile app: start chat +
+
Bob
+
+
+
+
+ +
+
+

Comparison with other protocols

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
simplex logo SimpleX chatSignal, big platformsXMPP, MatrixP2P protocols
Requires global identityNo - privateYes 1Yes 2Yes 3
Possibility of MITMNo - secureYes 4YesYes
Dependence on DNSNo - resilientYesYesNo
Single or centralized networkNo - decentralizedYesNo - federated 5Yes 6
Central component or other network-wide attackNo - resilientYesYes 2Yes 7
+
+ +
+
    +
  • Good
  • +
  • Bad
  • +
+ +
+
    +
  1. Usually based on a phone number, in some cases on usernames
  2. +
  3. DNS-based addresses
  4. +
  5. Public key or some other globally unique ID
  6. +
  7. If operator’s servers are compromised
  8. +
  9. Does not protect users' metadata
  10. +
  11. + While P2P are distributed, they are not federated - they operate as a single network +
  12. +
  13. + P2P networks either have a central authority or the whole network can be compromised - + see + here +
  14. +
+
+
+
+
+ +
+
+
+

SimpleX messaging protocol

+

Unidirectional (simplex) queues

+

+ Simplex messaging protocol + (SMP) for messaging via secure persistent queues will serve as the low level protocol for + SimpleX chat - see + demo server implementation.
+ It uses different encryption keys for each message queue - the key is passed via existing + secure channel - e.g. QR code or another simplex queue.
+

+
+ simplex messaging protocol +
+
+ +
+
+
+

Chat

+

Communication integrity

+

+ Each chat uses two (or more) simplex queues for duplex communication. Each message contains the + hash of the previous message to detect if messages are lost or changed. +

+
+ simplex chat: duplex conversation +
+
+ +
+
+
+

Use SimpleX

+

+ SimpleX chat terminal client + v2.0.0 is + released!
+ – groups and files
+ – two-layer E2E encryption, with double-ratchet algorithm
+ – protocol is compatible with mobile apps
+

+

+ You can use our servers or deploy your own, e.g. using + + StackScript on Linode. +

+

+ Sign up to be updated about the new releases. +

+ +
+ + +
+
+ +
+
+ + +
+ Star +
+
+
+ + diff --git a/website/src/invitation/index.html b/website/src/invitation/index.html new file mode 100644 index 0000000000..1c12785f73 --- /dev/null +++ b/website/src/invitation/index.html @@ -0,0 +1,187 @@ + + + + + + + + SimpleX chat: private, secure, no global identities + + + + + + + + + + + + + + {% include "nav.html" %} + +
+
+

This link is the invitation from a SimpleX Chat user

+ +
+
+

If you already installed SimpleX Chat for the + terminal v1.0.0+, copy the command below and use it in the chat: +

+ +

+ + + Copy to clipboard + + +

+
+ +
+ +
+

Scan QR code from mobile app

+

+ To make a connection: +

+
    +
  1. install SimpleX app
  2. +
  3. tap the button below
  4. +
  5. tap Connect button in the app
  6. +
+ + +
+
+
+
+
+ +
+
+

To install SimpleX Chat for the terminal

+
+

+ use this command: +

+
+

curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/install.sh + | + bash + + Copy to clipboard + + +

+
+
+ +
+

+ See SimpleX Chat + GitHub repository + + for the instructions how to download or compile it from the source code. +

+
+ +
+
+ +
+
+
+

Use SimpleX

+

+ SimpleX chat terminal client + v2.0.0 is + released!
+ – groups and files
+ – two-layer E2E encryption, with double-ratchet algorithm
+ – protocol is compatible with mobile apps
+

+

+ You can use our servers or deploy your own, e.g. using + + StackScript on Linode. +

+

+ Sign up to be updated about the new releases. +

+ +
+ + +
+
+ +
+
+ + +
+ Star +
+
+
+ + + diff --git a/website/src/js/index.js b/website/src/js/index.js new file mode 100644 index 0000000000..761c53e711 --- /dev/null +++ b/website/src/js/index.js @@ -0,0 +1,382 @@ +(async function () { + let DELAY = 0; + const DISTR = 0.33; + + class User { + constructor(name) { + this.userWindow = document.querySelector(`#demo .user.${name}`); + this.terminal = this.userWindow.querySelector(`.terminal`); + this.input = this.terminal.querySelector(".input"); + this.demoInput = this.terminal.querySelector("input"); + this.resetInput(); + this.setupDemo(); + this.group = []; + this.display = this.terminal.querySelector(".display"); + this.setupMoveWindow(); + this.name = name; + } + + reset() { + this.resetInput(); + this.display.innerHTML = ""; + } + + setGroup(groupName, users) { + this.users = users; + this.group = users.filter((u) => u !== this); + this.groupName = groupName; + } + + tryDemo() { + this.reset(); + show(this.demoInput); + this.demoInput.value = ""; + } + + async send(to, message, typeTo, paste, secret) { + await this._sendMsg(`@${to.name}`, message, typeTo, paste, secret); + await to.receive(this, toSecret(secret, message)); + await delay(20); + } + + async sendGroup(message, typeTo, paste) { + await this._sendMsg(`#${this.groupName}`, message, typeTo, paste); + await this.receiveGroup(message); + await delay(10); + } + + async _sendMsg(toStr, message, typeTo, paste, secret) { + await this.type(`${toStr} `, !typeTo); + if (secret) await this.type("#"); + await this.type(message, paste); + if (secret) await this.type("#"); + await delay(10); + this.resetInput(); + this.show("sent", `${toStr} ${toSecret(secret, message)}`); + } + + async type(str, paste) { + if (paste) { + await delay(10); + this.input.insertAdjacentHTML("beforeend", str); + } else { + for (const char of str) { + await delay(isAlpha(char) ? 1 : 2); + this.input.insertAdjacentHTML("beforeend", char); + } + } + await delay(2); + } + + resetInput() { + this.input.innerHTML = "> "; + show(this.demoInput, false); + } + + async receive(from, message, edit, group) { + await delay(10); + let g = group ? `#${this.groupName} ` : ""; + this.show("received", `${g}${from.name}> ${message}`, edit); + } + + async receiveGroup(message, edit) { + await Promise.all(this.group.map((u) => u.receive(this, message, edit, true))); + } + + show(mode, str, edit) { + if (edit && this.lastMessage) { + this.lastMessage.innerHTML = highlight(str); + return; + } + this.display.insertAdjacentHTML("beforeend", `
${highlight(str)}
`); + } + + setupDemo() { + if (!this.demoInput) return; + let editMode = false; + + on("keypress", this.demoInput, async ({ key }) => { + if (key === "Enter") { + const edit = editMode; + editMode = false; + const [to, ...words] = this.demoInput.value.trim().split(" "); + const message = words.join(" "); + switch (to[0]) { + case undefined: + if (message !== "") { + this.show("error", "Message should start with @user or #group"); + } + break; + case "@": + await this.sendInput(to.slice(1), message, edit); + break; + case "#": + await this.sendInputGroup(to.slice(1), message, edit); + break; + default: + this.show("error", "Message should start with @user or #group"); + } + } else if (this.demoInput.value === "" && key !== "@" && key !== "#") { + const channel = this.currentChannel(); + if (channel) this.demoInput.value = channel + " "; + } + }); + on("keydown", this.demoInput, async (e) => { + switch (e.key) { + case "ArrowUp": + if (this.demoInput.value === "" && this.lastMessage) { + const str = (this.demoInput.value = this.lastMessage.innerText); + editMode = true; + await delay(0); + this.demoInput.selectionStart = str.length; + } + break; + case "Tab": { + e.preventDefault(); + const userIndex = this.users.indexOf(this); + const nextIndex = (userIndex + 1) % this.users.length; + this.users[nextIndex].demoInput.focus(); + } + } + }); + } + + async sendInput(name, message, edit) { + if (name === this.name) { + this.show("error", "Can't send message to yourself"); + return; + } + const recipient = this.group.find((u) => u.name === name); + if (recipient === undefined) { + const knownNames = this.group.map((u) => `@${u.name}`).join(", ") + ` or @${this.name}`; + this.show("error", `Unknown recipient @${name} (try ${knownNames})`); + return; + } + this.show("sent", `@${name} ${message}`, edit); + this.demoInput.value = ""; + await recipient.receive(this, message, edit); + } + + async sendInputGroup(name, message, edit) { + if (name !== this.groupName) { + this.show("error", `Unknown group #${name} (try #team)`); + return; + } + this.show("sent", `#${name} ${message}`, edit); + this.demoInput.value = ""; + await this.receiveGroup(message, edit); + } + + get lastMessage() { + const messages = this.display.childNodes; + return messages[messages.length - 1]; + } + + currentChannel() { + return this.lastMessage && toContact(this.lastMessage.childNodes[0].innerHTML); + } + + setupMoveWindow() { + let moving = false; + let startX, startY; + const user = this.userWindow; + const parent = user.parentNode; + + on("mousedown", this.terminal, (e) => { + if (e.clientY - this.terminal.getBoundingClientRect().top > 20) return; + moving = true; + startX = user.offsetLeft - e.clientX; + startY = user.offsetTop - e.clientY; + parent.removeChild(user); + parent.appendChild(user); + }); + on("mouseup", this.terminal, () => (moving = false)); + on("mouseleave", this.terminal, () => (moving = false)); + on("mousemove", this.terminal, (e) => { + if (!moving) return; + user.style.left = e.clientX + startX + "px"; + user.style.top = e.clientY + startY + "px"; + }); + } + } + + function toContact(str) { + return str.endsWith(">") ? "@" + str.slice(0, -4) : str; + } + + function setGroup(groupName, users) { + users.forEach((u) => u.setGroup(groupName, users)); + } + + const alice = new User("alice"); + const bob = new User("bob"); + const tom = new User("tom"); + const team = [alice, bob, tom]; + setGroup("team", team); + + async function chatDemo() { + team.forEach((u) => u.reset()); + await alice.sendGroup("please review my PR project/site#72", true); + await tom.sendGroup("anybody got application key 🔑?"); + await bob.sendGroup("looking at it now @alice 👀"); + await alice.sendGroup("thanks @bob!"); + await alice.sendGroup("will DM @tom"); + await alice.send(tom, "w3@o6CewoZx#%$SQETXbWnus", true, true, true); + await tom.send(alice, "you're the savior 🙏!"); + await alice.send(bob, "please check the tests too", true); + await bob.send(alice, "all looks good 👍"); + await alice.send(bob, "thank you!"); + DELAY = 80; + } + + const invitation = + "smp::example.com:5223#1XNE1m2E1m0lm92​WG​Ket9CL6+lO742Vy5​G6nsrkvgs8=::St9hPY+k6nfrbaXj::rsa:MII​BoTANBgkqhkiG9w0B​AQEFAAOCAY4AMIIBiQKCAQEA03XGpEqh3faDN​Gl06pPhaT=="; + + async function establishConnection() { + team.forEach((u) => u.reset()); + await alice.type("/add bob"); + await delay(10); + alice.resetInput(); + // alice.show("/add bob"); + alice.show("sent", "pass this invitation to your contact (via any channel):"); + alice.show("sent", " "); + alice.show("sent", invitation); + alice.show("sent", " "); + alice.show("sent", "and ask them to connect:"); + alice.show("sent", "/c name_for_you invitation_above"); + await delay(20); + await bob.type("/connect alice "); + await bob.type(invitation, true); + await delay(20); + bob.resetInput(); + await bob.show("received", "/connect alice " + invitation); + await delay(10); + bob.show("received", "@alice connected"); + await delay(2); + alice.show("received", "@bob connected"); + await alice.send(bob, "hello bob"); + await bob.send(alice, "hi alice"); + } + + await chatDemo(); + const RUN_DEMO = "#demo .run-demo"; + const RUN_FASTER = "#demo .run-faster"; + const TRY_IT = "#demo .try-it"; + onClick(RUN_DEMO, runChatDemo); + // onClick(RUN_DEMO, establishConnection); + onClick(RUN_FASTER, () => (DELAY /= 2)); + onClick(TRY_IT, tryChatDemo); + + async function runChatDemo() { + document.querySelectorAll('.all-users div.user').forEach(e => e.classList.remove('d-none')); + document.querySelector('.all-users div.simplex_mobile').classList.add('d-none'); + show(RUN_DEMO, false); + show(RUN_FASTER); + enable(TRY_IT, false); + await chatDemo(); + show(RUN_DEMO); + show(RUN_FASTER, false); + enable(TRY_IT); + } + + function tryChatDemo() { + document.querySelectorAll('.all-users div.user').forEach(e => e.classList.remove('d-none')); + document.querySelector('.all-users div.simplex_mobile').classList.add('d-none'); + team.forEach((u) => u.tryDemo()); + alice.demoInput.focus(); + } + + async function delay(units) { + // delay is random with `1 +/- DISTR` range + const ms = units * DELAY * (1 - DISTR + 2 * DISTR * Math.random()); + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + function highlight(str) { + return str + .replace(/(@[a-z]+)([^0-9]|$)/gi, `$1$2`) + .replace(/([a-z]+>)([^0-9]|$)/gi, `$1$2`) + .replace(/(#[a-z]+)([^0-9]|$)/gi, `$1$2`) + .replace(/#([^\s]+)#([\s]|$)/gi, `#$1#$2`); + } + + function toSecret(secret, message) { + return secret ? `#${message}#` : message; + } + + function isAlpha(c) { + c = c.toUpperCase(); + return c >= "A" && c <= "Z"; + } + + let flipper = setInterval(flipProblem, 10000); + + onClick("#problem .pagination", () => { + clearInterval(flipper); + flipper = setInterval(flipProblem, 20000); + }); + + function flipProblem() { + if (isElementInViewport(document.getElementById("problem"))) { + window.location.hash = + window.location.hash === "#problem-explained" ? "#problem-intro" : "#problem-explained"; + } + } + + function isElementInViewport(el) { + const r = el.getBoundingClientRect(); + return r.bottom >= 0 && r.top <= window.innerHeight; + } + + function onClick(selector, handler, enable = true) { + const el = document.querySelector(selector); + if (el) on("click", el, handler, enable); + } + + function on(event, el, handler, enable = true) { + const method = enable ? "addEventListener" : "removeEventListener"; + el[method](event, handler); + } + + function show(selector, visible = true) { + const el = typeof selector === "string" ? document.querySelector(selector) : selector; + if (el) el.style.display = visible ? "block" : "none"; + } + + function enable(selector, enabled = true) { + const el = document.querySelector(selector); + el.disabled = enabled ? "" : "true"; + } + + const copyEls = document.querySelectorAll(".content_copy_with_tooltip"); + if (navigator.clipboard) { + copyEls.forEach(contentCopyWithTooltip) + } else { + copyEls.forEach(el => el.style.visibility = "hidden") + } + + function contentCopyWithTooltip(parent) { + const content = parent.querySelector(".content"); + const tooltip = parent.querySelector(".tooltiptext"); + const copyButton = parent.querySelector(".content_copy"); + copyButton.addEventListener("click", copyAddress) + copyButton.addEventListener("mouseout", resetTooltip) + + function copyAddress() { + navigator.clipboard.writeText(content.innerText || content.value); + tooltip.innerHTML = "Copied!"; + } + + function resetTooltip() { + tooltip.innerHTML = "Copy to clipboard"; + } + } + + // Setting width for scrollable sections + const element = document.querySelector('.container'); + const computedStyle = getComputedStyle(element); + const screenWidth = element.clientWidth - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)); + document.querySelector('.table-holder').style.width = `${screenWidth}px`; + Array.from(document.querySelectorAll('.problem-section')).forEach(ele => ele.style.width = `${screenWidth}px`); + +})(); diff --git a/website/src/js/script.js b/website/src/js/script.js new file mode 100644 index 0000000000..88a6409d7c --- /dev/null +++ b/website/src/js/script.js @@ -0,0 +1,8 @@ +// console.log(window.location.href) +// if(window.location.href.includes('comparison')){ +// console.log('Comparison'); +// document.querySelector('.comparison-nav-btn').classList.add('nav-button-active'); +// } +// else if(window.location.href.includes('blog')){ +// document.querySelector('.blog-nav-btn').classList.add('nav-button-active'); +// } diff --git a/website/tailwind.config.js b/website/tailwind.config.js new file mode 100644 index 0000000000..e9f30401c7 --- /dev/null +++ b/website/tailwind.config.js @@ -0,0 +1,7 @@ +module.exports = { + content: ["./src/**/*.{html,js,njk}"], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/website/tailwind.css b/website/tailwind.css new file mode 100644 index 0000000000..dc83d5cf59 --- /dev/null +++ b/website/tailwind.css @@ -0,0 +1,3 @@ +/* @tailwind base; */ +@tailwind components; +@tailwind utilities; diff --git a/website/web.sh b/website/web.sh new file mode 100755 index 0000000000..b1d67be57b --- /dev/null +++ b/website/web.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cp -R blog website/src +cp -R images website/src +rm website/src/blog/README.md +cd website +npm install +npm run build +npm run build-tailwind