diff --git a/assets/images/heart.svg b/assets/images/heart.svg
new file mode 100644
index 0000000..0262800
--- /dev/null
+++ b/assets/images/heart.svg
@@ -0,0 +1,18 @@
+
diff --git a/package-lock.json b/package-lock.json
index 32ae3bd..bc047c8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"@electron/notarize": "^3.0.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
+ "@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
"@xmcl/core": "^2.14.1",
"@xmcl/installer": "^6.1.0",
@@ -3638,6 +3639,32 @@
"url": "https://opencollective.com/mui-org"
}
},
+ "node_modules/@mui/icons-material": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.2.0.tgz",
+ "integrity": "sha512-gRCspp3pfjHQyTmSOmYw7kUQTd9Udpdan4R8EnZvqPeoAtHnPzkvjBrBqzKaoAbbBp5bGF7BcD18zZJh4nwu0A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.27.6"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@mui/material": "^7.2.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@mui/material": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.2.0.tgz",
diff --git a/package.json b/package.json
index c746388..d339171 100644
--- a/package.json
+++ b/package.json
@@ -104,6 +104,7 @@
"@electron/notarize": "^3.0.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
+ "@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
"@xmcl/core": "^2.14.1",
"@xmcl/installer": "^6.1.0",
diff --git a/src/main/main.ts b/src/main/main.ts
index 2e56464..37490c5 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -48,6 +48,16 @@ if (isDebug) {
require('electron-debug').default();
}
+ipcMain.handle('close-app', () => {
+ app.quit();
+ return true;
+});
+
+ipcMain.handle('minimize-app', () => {
+ mainWindow?.minimize();
+ return true;
+});
+
const installExtensions = async () => {
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
@@ -79,6 +89,7 @@ const createWindow = async () => {
width: 1024,
height: 728,
autoHideMenuBar: true,
+ frame: false,
icon: getAssetPath('icon.png'),
webPreferences: {
webSecurity: false,
diff --git a/src/main/minecraft-launcher.ts b/src/main/minecraft-launcher.ts
index ed2a53d..b043b6f 100644
--- a/src/main/minecraft-launcher.ts
+++ b/src/main/minecraft-launcher.ts
@@ -772,9 +772,6 @@ export function initServerStatusHandler() {
try {
// Формируем адрес с портом, если указан
const serverAddress = port ? `${host}:${port}` : host;
- console.log(
- `Запрос статуса сервера: ${MCSTATUS_API_URL}${serverAddress}`,
- );
// Делаем запрос к API mcstatus.io
const response = await fetch(`${MCSTATUS_API_URL}${serverAddress}`);
@@ -786,7 +783,6 @@ export function initServerStatusHandler() {
}
const data = await response.json();
- console.log('Получен ответ от API:', data);
if (data.online) {
return {
diff --git a/src/main/preload.ts b/src/main/preload.ts
index 8541aae..92a8665 100644
--- a/src/main/preload.ts
+++ b/src/main/preload.ts
@@ -5,7 +5,9 @@ export type Channels =
| 'download-progress'
| 'launch-minecraft'
| 'installation-status'
- | 'get-server-status';
+ | 'get-server-status'
+ | 'close-app'
+ | 'minimize-app';
const electronHandler = {
ipcRenderer: {
diff --git a/src/renderer/App.css b/src/renderer/App.css
index 735630a..16d87e1 100644
--- a/src/renderer/App.css
+++ b/src/renderer/App.css
@@ -16,7 +16,7 @@ body {
position: relative;
color: white;
height: 100vh;
- background: linear-gradient(200.96deg, #000000, #3b4187);
+ background: linear-gradient(242.94deg, #000000 39.07%, #3b4187 184.73%);
font-family: 'Benzin-Bold' !important;
overflow-y: hidden;
display: flex;
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index a0857c3..38eaa14 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -8,6 +8,9 @@ import Login from './pages/Login';
import LaunchPage from './pages/LaunchPage';
import { ReactNode, useEffect, useState } from 'react';
import './App.css';
+import TopBar from './components/TopBar';
+import { Box } from '@mui/material';
+import MinecraftBackround from './components/MinecraftBackround';
// Переместите launchOptions сюда, вне компонентов
const launchOptions = {
@@ -71,19 +74,38 @@ const AuthCheck = ({ children }: { children: ReactNode }) => {
};
const App = () => {
+ // Просто используйте window.open без useNavigate
+ const handleRegister = () => {
+ window.open('https://account.ely.by/register', '_blank');
+ };
+
return (
-
- } />
-
-
-
- }
- />
-
+
+
+
+
+ } />
+
+
+
+ }
+ />
+
+
);
};
diff --git a/src/renderer/components/MinecraftBackround.tsx b/src/renderer/components/MinecraftBackround.tsx
new file mode 100644
index 0000000..eba2d4c
--- /dev/null
+++ b/src/renderer/components/MinecraftBackround.tsx
@@ -0,0 +1,73 @@
+import { Box } from '@mui/material';
+import heart from '../../../assets/images/heart.svg';
+
+export default function MinecraftBackround() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/renderer/components/TopBar.tsx b/src/renderer/components/TopBar.tsx
new file mode 100644
index 0000000..fb2e8e3
--- /dev/null
+++ b/src/renderer/components/TopBar.tsx
@@ -0,0 +1,109 @@
+import { Box, Button, Typography } from '@mui/material';
+import CloseIcon from '@mui/icons-material/Close';
+import MinimizeIcon from '@mui/icons-material/Minimize';
+import { useLocation } from 'react-router-dom';
+
+declare global {
+ interface Window {
+ electron: {
+ ipcRenderer: {
+ invoke(channel: string, ...args: unknown[]): Promise;
+ on(channel: string, func: (...args: unknown[]) => void): void;
+ removeAllListeners(channel: string): void;
+ };
+ };
+ }
+}
+
+// Определяем пропсы
+interface TopBarProps {
+ onRegister?: () => void; // Опционально, если нужен обработчик регистрации
+}
+
+export default function TopBar({ onRegister }: TopBarProps) {
+ // Получаем текущий путь
+ const location = useLocation();
+ const isLoginPage = location.pathname === '/login';
+
+ return (
+
+ {/* Правая часть со всеми кнопками */}
+
+ {/* Кнопка регистрации, если на странице логина */}
+ {isLoginPage && (
+
+ )}
+
+ {/* Кнопки управления окном */}
+
+
+
+
+ );
+}
diff --git a/src/renderer/pages/LaunchPage.tsx b/src/renderer/pages/LaunchPage.tsx
index 763c6a2..9e3733a 100644
--- a/src/renderer/pages/LaunchPage.tsx
+++ b/src/renderer/pages/LaunchPage.tsx
@@ -61,21 +61,25 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
setDownloadProgress(progress);
setBuffer(Math.min(progress + 10, 100));
};
+
+ const statusListener = (...args: unknown[]) => {
+ const status = args[0] as { step: string; message: string };
+ setInstallStep(status.step);
+ setInstallMessage(status.message);
+ };
window.electron.ipcRenderer.on('download-progress', progressListener);
- window.electron.ipcRenderer.on(
- 'installation-status',
- (...args: unknown[]) => {
- const status = args[0] as { step: string; message: string };
- setInstallStep(status.step);
- setInstallMessage(status.message);
- },
- );
+ window.electron.ipcRenderer.on('installation-status', statusListener);
return () => {
- window.electron.ipcRenderer.removeAllListeners('download-progress');
- window.electron.ipcRenderer.removeAllListeners('installation-progress');
- window.electron.ipcRenderer.removeAllListeners('installation-status');
+ // Удаляем только конкретных слушателей, а не всех
+ // Это безопаснее, чем removeAllListeners
+ const cleanup = window.electron.ipcRenderer.on;
+ if (typeof cleanup === 'function') {
+ cleanup('download-progress', progressListener);
+ cleanup('installation-status', statusListener);
+ }
+ // Удаляем использование removeAllListeners
};
}, [navigate]);