diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 275b2e0..8177b51 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -2,9 +2,11 @@ import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { ElectronTitleBar } from "./pages/app/ui/components/Electron";
import { useAppState } from "./pages/app/ui/state";
import { useEffect, useState } from "react";
+import HomePage from "./pages/HomePage";
import LoginPage from "./pages/LoginPage";
import RegisterPage from "./pages/RegisterPage";
import ChatPage from "./pages/ChatPage";
+import DownloadAppPage from "./pages/DownloadAppPage";
import NotFoundPage from "./pages/NotFoundPage";
import ProtectedRoute from "./pages/ProtectedRoute";
import { isElectron } from "./pages/app/electron/electron";
@@ -29,9 +31,10 @@ export default function App() {
- } />
+ } />
} />
} />
+ } />
diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx
new file mode 100644
index 0000000..4ab16a4
--- /dev/null
+++ b/frontend/src/pages/HomePage.tsx
@@ -0,0 +1,267 @@
+import { useNavigate } from "react-router-dom";
+import { useAppState } from "./app/ui/state";
+
+function GitHubLink({ children }: { children: React.ReactNode }) {
+ return (
+ {children}
+ );
+}
+
+function SupportLink({ children }: { children: React.ReactNode }) {
+ return (
+ {children}
+ );
+}
+
+export default function HomePage() {
+ const navigate = useNavigate();
+ const { user } = useAppState();
+
+ const handleGetStarted = () => {
+ if (user.authToken && user.currentUser) {
+ navigate("/chat");
+ } else {
+ navigate("/login");
+ }
+ };
+
+ return (
+
+
+
+
+
+
FromChat
+ 100% открытый мессенджер
+
+
+
+ GitHub
+
+
+ Поддержка
+
+
+ {user.authToken ? (
+ navigate("/chat")}>
+ Перейти в чат
+
+ ) : (
+ navigate("/login")}>
+ Войти
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ Безопасный мессенджер с открытым исходным кодом
+
+
+ FromChat — это полностью открытый мессенджер с end-to-end шифрованием,
+ поддержкой файлов и уведомлений. Создан для тех, кто ценит приватность и свободу.
+
+
+
+ {user.authToken ? "Перейти в чат" : "Начать общение"}
+
+ navigate("/register")}
+ >
+ Зарегистрироваться
+
+
+
+
+
+
+
+
+
+
А
+
+
Привет! Как дела?
+
14:30
+
+
+
+
+
Всё отлично! А у тебя как?
+
14:32
+
+
+
+
Б
+
+
Отправляю файл 📎
+
14:35
+
+
+
+
+
+
+
+
+
+
+
+
Возможности
+
+
+
+
+
+
End-to-End Шифрование
+
+ Ваши личные сообщения защищены современным шифрованием X25519 + AES-GCM.
+ Только вы и получатель можете прочитать сообщения.
+
+
+
+
+
+
+
+
100% открытый код
+
+ Весь исходный код доступен на GitHub . Вы можете проверить безопасность,
+ внести изменения или развернуть свой сервер.
+
+
+
+
+
+
+
+
Обмен Файлами
+
+ Отправляйте файлы до 4 ГБ. Файлы в личных сообщениях шифруются.
+ В общем чате шифрования нет, так как ваши сообщения могут читать все пользователи FromChat.
+
+
+
+
+
+
+
+
Уведомления
+
+ Получайте push-уведомления в браузере и настольном приложении.
+ Никогда не пропустите важное сообщение.
+
+
+
+
+
+
+
+
Редактирование
+
+ Редактируйте и удаляйте свои сообщения. Отвечайте на сообщения
+ для лучшего контекста общения.
+
+
+
+
+
+
+
+
Кроссплатформенность
+
+ Работает в браузере и как настольное приложение для Windows,
+ macOS и Linux. Единый интерфейс везде.
+
+
+
+
+
+
+
+
+
+
Скачайте приложение
+
+ Для лучшего опыта используйте настольное приложение с поддержкой
+ уведомлений и автономной работы.
+
+
+
+
+
+
+
+
+
+
Готовы начать общение?
+
+ Присоединяйтесь к FromChat и общайтесь безопасно с друзьями и коллегами.
+
+
+ navigate("/register")}
+ >
+ Создать аккаунт
+
+ navigate("/login")}
+ >
+ Войти
+
+
+
+
+
+
+
+
+
+
+
+
Ссылки
+ GitHub
+ Поддержка
+
+
+
+
+
© 2025 FromChat. Сделано программистом denis0001-dev с ❤️ для свободы общения.
+
+
+
+
+ );
+}
diff --git a/frontend/src/pages/app/resources/css/_homepage.scss b/frontend/src/pages/app/resources/css/_homepage.scss
new file mode 100644
index 0000000..15aee92
--- /dev/null
+++ b/frontend/src/pages/app/resources/css/_homepage.scss
@@ -0,0 +1,657 @@
+@use "common/material" as *;
+
+.homepage {
+ min-height: 100vh;
+ background: linear-gradient(135deg, $color-dark-background 0%, $color-dark-surface-container 50%, $color-dark-primary-container 100%);
+ color: $color-dark-on-background;
+ font-family: 'Montserrat', sans-serif;
+ position: relative;
+
+ &::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background:
+ radial-gradient(circle at 20% 80%, rgba($color-dark-primary, 0.3) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba($color-dark-tertiary, 0.3) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba($color-dark-secondary, 0.2) 0%, transparent 50%);
+ pointer-events: none;
+ z-index: 0;
+ }
+
+ // Cascaded styles for all child elements
+ * {
+ position: relative;
+ z-index: 1;
+ }
+
+ // Container styles
+ .container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+ }
+
+ // Header styles
+ .homepage-header {
+ padding: 1rem 0;
+ background: rgba($color-dark-surface-container, 0.8);
+ backdrop-filter: blur(20px);
+ border-bottom: 1px solid rgba($color-dark-primary, 0.3);
+ box-shadow: 0 4px 20px rgba($color-dark-primary, 0.1);
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+ transition: all 0.3s ease;
+
+ .header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .logo {
+ h1 {
+ font-size: 2rem;
+ font-weight: 700;
+ margin: 0;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ .tagline {
+ font-size: 0.9rem;
+ }
+ }
+
+ .header-nav {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ a {
+ display: flex;
+ }
+ }
+ }
+ }
+
+ // Hero section
+ .hero {
+ padding: 4rem 0;
+ display: flex;
+ align-items: center;
+ min-height: 80vh;
+ margin-top: 0;
+
+ .hero-content {
+ flex: 1;
+ max-width: 600px;
+
+ .hero-title {
+ font-size: 3.5rem;
+ font-weight: 800;
+ line-height: 1.1;
+ margin-bottom: 1.5rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary, $color-dark-secondary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 30px rgba($color-dark-primary, 0.5);
+ animation: neonGlow 3s ease-in-out infinite alternate;
+ }
+
+ .hero-description {
+ font-size: 1.25rem;
+ line-height: 1.6;
+ margin-bottom: 2.5rem;
+ opacity: 0.9;
+ }
+
+ .hero-actions {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+ }
+ }
+
+ .hero-visual {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 2rem;
+
+ .chat-preview {
+ perspective: 1000px;
+
+ .chat-window {
+ background: rgba($color-dark-surface-container, 0.95);
+ border-radius: 20px;
+ padding: 1.5rem;
+ box-shadow:
+ 0 20px 40px rgba(0, 0, 0, 0.5),
+ 0 0 20px rgba($color-dark-primary, 0.3),
+ inset 0 1px 0 rgba($color-dark-primary, 0.2);
+ transform: rotateY(-5deg) rotateX(5deg);
+ color: $color-dark-on-surface;
+ max-width: 400px;
+ width: 100%;
+ border: 1px solid rgba($color-dark-primary, 0.3);
+
+ .chat-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid rgba($color-dark-primary, 0.3);
+ margin-bottom: 1rem;
+
+ .chat-title {
+ font-weight: 600;
+ font-size: 1.1rem;
+ }
+
+ .online-indicator {
+ color: $color-dark-primary;
+ font-size: 0.8rem;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.8);
+ animation: pulse 2s ease-in-out infinite;
+ }
+ }
+
+ .chat-messages {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ .message {
+ display: flex;
+ gap: 0.75rem;
+ align-items: flex-start;
+
+ &.sent {
+ flex-direction: row-reverse;
+
+ .message-content {
+ background: linear-gradient(135deg, $color-dark-primary, $color-dark-primary-container);
+ color: $color-dark-on-primary;
+ box-shadow: 0 0 15px rgba($color-dark-primary, 0.3);
+ }
+ }
+
+ &.received {
+ .message-content {
+ background: rgba($color-dark-surface-variant, 0.8);
+ color: $color-dark-on-surface-variant;
+ border: 1px solid rgba($color-dark-outline-variant, 0.3);
+ }
+ }
+
+ .message-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, $color-dark-primary, $color-dark-tertiary);
+ color: $color-dark-on-primary;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 600;
+ font-size: 0.8rem;
+ flex-shrink: 0;
+ box-shadow: 0 0 10px rgba($color-dark-primary, 0.4);
+ }
+
+ .message-content {
+ max-width: 70%;
+ padding: 0.75rem 1rem;
+ border-radius: 18px;
+ position: relative;
+
+ .message-text {
+ font-size: 0.9rem;
+ line-height: 1.4;
+ }
+
+ .message-time {
+ font-size: 0.75rem;
+ opacity: 0.7;
+ margin-top: 0.25rem;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Features section
+ .features {
+ padding: 6rem 0;
+ background: rgba($color-dark-surface-container, 0.3);
+ backdrop-filter: blur(20px);
+ border-top: 1px solid rgba($color-dark-primary, 0.2);
+ border-bottom: 1px solid rgba($color-dark-primary, 0.2);
+
+ .section-title {
+ text-align: center;
+ font-size: 2.5rem;
+ font-weight: 700;
+ margin-bottom: 3rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ .features-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
+ gap: 2rem;
+
+ .feature-card {
+ background: rgba($color-dark-surface-container, 0.6);
+ backdrop-filter: blur(20px);
+ border-radius: 20px;
+ padding: 2rem;
+ border: 1px solid rgba($color-dark-primary, 0.3);
+ transition: all 0.3s ease;
+ position: relative;
+ overflow: hidden;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba($color-dark-primary, 0.1), rgba($color-dark-tertiary, 0.1));
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ z-index: 0;
+ }
+
+ > * {
+ position: relative;
+ z-index: 1;
+ }
+
+ &:hover {
+ transform: translateY(-5px);
+ box-shadow:
+ 0 20px 40px rgba(0, 0, 0, 0.3),
+ 0 0 30px rgba($color-dark-primary, 0.2);
+ border-color: rgba($color-dark-primary, 0.5);
+
+ &::before {
+ opacity: 1;
+ }
+ }
+
+ .feature-icon {
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(135deg, rgba($color-dark-primary, 0.3), rgba($color-dark-tertiary, 0.3));
+ border-radius: 15px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 1.5rem;
+ border: 1px solid rgba($color-dark-primary, 0.4);
+ box-shadow: 0 0 15px rgba($color-dark-primary, 0.2);
+ user-select: none;
+
+ mdui-icon {
+ font-size: 1.5rem;
+ color: $color-dark-primary;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.8);
+ }
+ }
+
+ h4 {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin-bottom: 1rem;
+ color: $color-dark-on-surface;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.3);
+ }
+
+ p {
+ line-height: 1.6;
+ opacity: 0.9;
+ color: $color-dark-on-surface-variant;
+ }
+ }
+ }
+ }
+
+ // Download section
+ .download {
+ padding: 6rem 0;
+
+ .download-content {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+
+ h3 {
+ font-size: 2.5rem;
+ font-weight: 700;
+ margin-bottom: 1.5rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ p {
+ font-size: 1.25rem;
+ line-height: 1.6;
+ margin-bottom: 2.5rem;
+ opacity: 0.9;
+ }
+
+ .download-buttons {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+ }
+ }
+
+ // CTA section
+ .cta {
+ padding: 6rem 0;
+ background: rgba($color-dark-surface-container, 0.3);
+ backdrop-filter: blur(20px);
+ border-top: 1px solid rgba($color-dark-primary, 0.2);
+
+ .cta-content {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+
+ h3 {
+ font-size: 2.5rem;
+ font-weight: 700;
+ margin-bottom: 1.5rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ p {
+ font-size: 1.25rem;
+ line-height: 1.6;
+ margin-bottom: 2.5rem;
+ opacity: 0.9;
+ }
+
+ .cta-actions {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+ }
+ }
+
+ // Footer
+ .homepage-footer {
+ background: rgba($color-dark-surface-container, 0.8);
+ backdrop-filter: blur(20px);
+ padding: 3rem 0 1rem;
+ border-top: 1px solid rgba($color-dark-primary, 0.3);
+ box-shadow: 0 -4px 20px rgba($color-dark-primary, 0.1);
+
+ .footer-content {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 2rem;
+ margin-bottom: 2rem;
+
+ .footer-section {
+ h4 {
+ font-size: 1.1rem;
+ font-weight: 600;
+ margin-bottom: 1rem;
+ color: $color-dark-on-surface;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.3);
+ }
+
+ p {
+ opacity: 0.8;
+ line-height: 1.6;
+ color: $color-dark-on-surface-variant;
+ }
+
+ a {
+ color: $color-dark-on-surface;
+ text-decoration: none;
+ opacity: 0.8;
+ display: block;
+ margin-bottom: 0.5rem;
+ transition: all 0.3s ease;
+ padding: 0.25rem 0;
+ border-radius: 4px;
+
+ &:hover {
+ opacity: 1;
+ color: $color-dark-primary;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.5);
+ background: rgba($color-dark-primary, 0.1);
+ }
+ }
+ }
+ }
+
+ .footer-bottom {
+ text-align: center;
+ padding-top: 2rem;
+ border-top: 1px solid rgba($color-dark-primary, 0.3);
+
+ p {
+ opacity: 0.7;
+ margin: 0;
+ color: $color-dark-on-surface-variant;
+ }
+ }
+ }
+}
+
+// Keyframes for animations
+@keyframes neonGlow {
+ 0% {
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5), 0 0 40px rgba($color-dark-primary, 0.3);
+ }
+ 100% {
+ text-shadow: 0 0 30px rgba($color-dark-primary, 0.8), 0 0 60px rgba($color-dark-primary, 0.5);
+ }
+}
+
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+// Responsive Design
+@media (min-width: 1024px) {
+ .homepage {
+ .homepage-header {
+ position: fixed;
+ top: 1rem;
+ left: 50%;
+ transform: translateX(-50%);
+ width: calc(100% - 2rem);
+ max-width: 1200px;
+ border-radius: 20px;
+ border: 1px solid rgba($color-dark-primary, 0.3);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.3),
+ 0 0 20px rgba($color-dark-primary, 0.2);
+ backdrop-filter: blur(30px);
+ background: rgba($color-dark-surface-container, 0.9);
+ }
+
+ .hero {
+ margin-top: 6rem;
+ }
+ }
+}
+
+@media (max-width: 1023px) {
+ .homepage {
+ .homepage-header {
+ position: sticky;
+ top: 0;
+ border-radius: 0;
+ margin: 0;
+ width: 100%;
+ transform: none;
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .homepage {
+ .container {
+ padding: 0 1rem;
+ }
+
+ .homepage-header {
+ .header-content {
+ flex-direction: column;
+ gap: 1rem;
+ }
+ }
+
+ .hero {
+ flex-direction: column;
+ text-align: center;
+ padding: 2rem 0;
+
+ .hero-content {
+ .hero-title {
+ font-size: 2.5rem;
+ }
+
+ .hero-description {
+ font-size: 1.1rem;
+ }
+ }
+
+ .hero-visual {
+ padding: 1rem;
+
+ .chat-preview {
+ .chat-window {
+ transform: none;
+ max-width: 100%;
+ }
+ }
+ }
+ }
+
+ .features {
+ .features-grid {
+ grid-template-columns: 1fr;
+
+ .feature-card {
+ padding: 1.5rem;
+ }
+ }
+
+ .section-title {
+ font-size: 2rem;
+ }
+ }
+
+ .download {
+ .download-content {
+ h3 {
+ font-size: 2rem;
+ }
+
+ .download-buttons {
+ flex-direction: column;
+ align-items: center;
+ }
+ }
+ }
+
+ .cta {
+ .cta-content {
+ h3 {
+ font-size: 2rem;
+ }
+
+ .cta-actions {
+ flex-direction: column;
+ align-items: center;
+ }
+ }
+ }
+
+ .homepage-footer {
+ .footer-content {
+ grid-template-columns: 1fr;
+ text-align: center;
+ }
+ }
+ }
+}
+
+@media (max-width: 480px) {
+ .homepage {
+ .hero {
+ .hero-content {
+ .hero-title {
+ font-size: 2rem;
+ }
+
+ .hero-description {
+ font-size: 1rem;
+ }
+ }
+ }
+
+ .features {
+ .section-title {
+ font-size: 1.75rem;
+ }
+ }
+
+ .download {
+ .download-content {
+ h3 {
+ font-size: 1.75rem;
+ }
+ }
+ }
+
+ .cta {
+ .cta-content {
+ h3 {
+ font-size: 1.75rem;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/pages/app/resources/css/style.scss b/frontend/src/pages/app/resources/css/style.scss
index 3e06041..225262c 100644
--- a/frontend/src/pages/app/resources/css/style.scss
+++ b/frontend/src/pages/app/resources/css/style.scss
@@ -11,6 +11,7 @@
@use "dialogs/reply";
@use "download-app";
@use "404" as not-found;
+@use "homepage";
@use "lib/fonts/montserrat";
@use "lib/fonts/material-symbols";