mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-23 03:25:07 +03:00
Merge branch 'electron'
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
name: Build Electron Apps
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: ["main", "electron"]
|
||||||
|
paths:
|
||||||
|
- 'frontend/**'
|
||||||
|
- 'package.json'
|
||||||
|
- 'package-lock.json'
|
||||||
|
- 'frontend/electron/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build (${{ matrix.os }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
|
||||||
|
- name: Install root deps (no scripts)
|
||||||
|
run: npm install --ignore-scripts --no-audit --no-fund
|
||||||
|
|
||||||
|
- name: Install Electron Forge deps
|
||||||
|
run: npm run frontend:electron:dependencies
|
||||||
|
|
||||||
|
- name: Build Electron app
|
||||||
|
run: npm run build:electron
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: fromchat-${{ runner.os }}
|
||||||
|
path: |
|
||||||
|
frontend/electron/forge/out/**
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
@@ -380,3 +380,4 @@ data
|
|||||||
.vite
|
.vite
|
||||||
*.db
|
*.db
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
dist-electron
|
||||||
Vendored
+18
-2
@@ -15,8 +15,24 @@
|
|||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build"
|
||||||
"isDefault": true
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Run (Electron)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm run dev:electron",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"kind": "build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
export type Platform = "win32" | "darwin" | "linux"
|
||||||
|
|
||||||
|
export interface ElectronInterface {
|
||||||
|
desktop: true,
|
||||||
|
platform: Platform
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
electronInterface: ElectronInterface
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { FusesPlugin } from '@electron-forge/plugin-fuses';
|
||||||
|
import { FuseV1Options, FuseVersion } from '@electron/fuses';
|
||||||
|
import type { ForgeConfig } from "@electron-forge/shared-types";
|
||||||
|
|
||||||
|
const config: ForgeConfig = {
|
||||||
|
packagerConfig: {
|
||||||
|
asar: true
|
||||||
|
},
|
||||||
|
rebuildConfig: {},
|
||||||
|
makers: [
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-squirrel',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-zip',
|
||||||
|
config: {},
|
||||||
|
platforms: ['darwin'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-deb',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-rpm',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@electron-forge/plugin-auto-unpack-natives',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
// Fuses are used to enable/disable various Electron functionality
|
||||||
|
// at package time, before code signing the application
|
||||||
|
new FusesPlugin({
|
||||||
|
version: FuseVersion.V1,
|
||||||
|
[FuseV1Options.RunAsNode]: false,
|
||||||
|
[FuseV1Options.EnableCookieEncryption]: true,
|
||||||
|
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
|
||||||
|
[FuseV1Options.EnableNodeCliInspectArguments]: false,
|
||||||
|
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
|
||||||
|
[FuseV1Options.OnlyLoadAppFromAsar]: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Hello World!</title>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>💖 Hello World!</h1>
|
||||||
|
<p>Welcome to your Electron application.</p>
|
||||||
|
<script type="module" src="/src/renderer.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "FromChat",
|
||||||
|
"productName": "FromChat",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A 100% Open Source Messenger",
|
||||||
|
"main": "dist-electron/main.js",
|
||||||
|
"scripts": {
|
||||||
|
"package": "electron-forge package",
|
||||||
|
"make": "electron-forge make",
|
||||||
|
"publish": "electron-forge publish"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": {
|
||||||
|
"name": "denis0001-dev",
|
||||||
|
"email": "denis0001.dev@ya.ru"
|
||||||
|
},
|
||||||
|
"license": "GPL-2.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@electron-forge/cli": "^7.8.3",
|
||||||
|
"@electron-forge/maker-deb": "^7.8.3",
|
||||||
|
"@electron-forge/maker-rpm": "^7.8.3",
|
||||||
|
"@electron-forge/maker-squirrel": "^7.8.3",
|
||||||
|
"@electron-forge/maker-zip": "^7.8.3",
|
||||||
|
"@electron-forge/plugin-auto-unpack-natives": "^7.8.3",
|
||||||
|
"@electron-forge/plugin-fuses": "^7.8.3",
|
||||||
|
"@electron/fuses": "^1.8.0",
|
||||||
|
"electron": "37.3.1",
|
||||||
|
"vite": "^5.4.19"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"electron-squirrel-startup": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { app, BrowserWindow } from 'electron';
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
title: 'Main window',
|
||||||
|
minWidth: 650,
|
||||||
|
minHeight: 420,
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(import.meta.dirname, "preload.mjs")
|
||||||
|
},
|
||||||
|
titleBarStyle: "hidden",
|
||||||
|
...(process.platform !== 'darwin' ? { titleBarOverlay: true } : {}),
|
||||||
|
trafficLightPosition: {
|
||||||
|
x: 16 - 4,
|
||||||
|
y: 16 - 4
|
||||||
|
},
|
||||||
|
titleBarOverlay: process.platform !== "darwin"
|
||||||
|
})
|
||||||
|
|
||||||
|
// You can use `process.env.VITE_DEV_SERVER_URL` when the vite command is called `serve`
|
||||||
|
if (process.env.VITE_DEV_SERVER_URL) {
|
||||||
|
win.loadURL(process.env.VITE_DEV_SERVER_URL)
|
||||||
|
} else {
|
||||||
|
// Load your file
|
||||||
|
win.loadFile('dist/index.html');
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { contextBridge } from "electron";
|
||||||
|
import type { ElectronInterface, Platform } from "../electron";
|
||||||
|
|
||||||
|
const electronInterface: ElectronInterface = {
|
||||||
|
desktop: true,
|
||||||
|
platform: process.platform as Platform
|
||||||
|
}
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld("electronInterface", electronInterface);
|
||||||
+181
-169
@@ -7,185 +7,197 @@
|
|||||||
<link rel="icon" href="./src/images/logo.png" />
|
<link rel="icon" href="./src/images/logo.png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Login Form -->
|
<div id="electron-title-bar">
|
||||||
<div id="login-form" class="auth-container">
|
<div class="macos-padding"></div>
|
||||||
<div class="auth-card fade-in">
|
<div id="window-title"></div>
|
||||||
<div class="auth-header">
|
<!-- <div class="window-controls">
|
||||||
<h2>
|
<mdui-button-icon icon="remove" id="window-minimize"></mdui-button-icon>
|
||||||
<span class="material-symbols filled large">login</span>
|
<mdui-button-icon icon="stack--outlined" id="window-restore" class="hidden"></mdui-button-icon>
|
||||||
Добро пожаловать!
|
<mdui-button-icon icon="ad--outlined" id="window-maximize"></mdui-button-icon>
|
||||||
</h2>
|
<mdui-button-icon icon="close" id="window-close"></mdui-button-icon>
|
||||||
<p>Войдите в свой аккаунт</p>
|
</div> -->
|
||||||
</div>
|
|
||||||
<div class="auth-body">
|
|
||||||
<div id="login-alerts"></div>
|
|
||||||
|
|
||||||
<form id="login-form-element">
|
|
||||||
<mdui-text-field
|
|
||||||
label="Имя пользователя"
|
|
||||||
id="login-username"
|
|
||||||
name="username"
|
|
||||||
variant="outlined"
|
|
||||||
icon="person--filled"
|
|
||||||
autocomplete="username"
|
|
||||||
required>
|
|
||||||
</mdui-text-field>
|
|
||||||
<mdui-text-field
|
|
||||||
label="Пароль"
|
|
||||||
id="login-password"
|
|
||||||
name="password"
|
|
||||||
variant="outlined"
|
|
||||||
type="password"
|
|
||||||
toggle-password
|
|
||||||
icon="password--filled"
|
|
||||||
autocomplete="current-password"
|
|
||||||
required>
|
|
||||||
</mdui-text-field>
|
|
||||||
|
|
||||||
<mdui-button type="submit">Войти</mdui-button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="text-center">
|
|
||||||
<p>Ещё нет аккаунта? <a href="#" id="register-link" class="link">Зарегистрируйтесь</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="main-wrapper">
|
||||||
<!-- Register Form -->
|
<!-- Login Form -->
|
||||||
<div id="register-form" class="auth-container" style="display: none;">
|
<div id="login-form" class="auth-container">
|
||||||
<div class="auth-card fade-in">
|
<div class="auth-card fade-in">
|
||||||
<div class="auth-header">
|
<div class="auth-header">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="material-symbols filled large">person_add</span>
|
<span class="material-symbols filled large">login</span>
|
||||||
Регистрация
|
Добро пожаловать!
|
||||||
</h2>
|
</h2>
|
||||||
<p>Создайте новый аккаунт</p>
|
<p>Войдите в свой аккаунт</p>
|
||||||
</div>
|
|
||||||
<div class="auth-body">
|
|
||||||
<div id="register-alerts"></div>
|
|
||||||
|
|
||||||
<form id="register-form-element">
|
|
||||||
<mdui-text-field
|
|
||||||
label="Имя пользователя"
|
|
||||||
id="register-username"
|
|
||||||
name="username"
|
|
||||||
variant="outlined"
|
|
||||||
icon="person--filled"
|
|
||||||
autocomplete="username"
|
|
||||||
maxlength="20"
|
|
||||||
counter
|
|
||||||
required>
|
|
||||||
</mdui-text-field>
|
|
||||||
<mdui-text-field
|
|
||||||
label="Пароль"
|
|
||||||
id="register-password"
|
|
||||||
name="password"
|
|
||||||
variant="outlined"
|
|
||||||
type="password"
|
|
||||||
toggle-password
|
|
||||||
icon="password--filled"
|
|
||||||
autocomplete="new-password"
|
|
||||||
required>
|
|
||||||
</mdui-text-field>
|
|
||||||
<mdui-text-field
|
|
||||||
label="Подтвердите пароль"
|
|
||||||
id="register-confirm-password"
|
|
||||||
name="confirm_password"
|
|
||||||
variant="outlined"
|
|
||||||
type="password"
|
|
||||||
toggle-password
|
|
||||||
icon="password--filled"
|
|
||||||
autocomplete="new-password"
|
|
||||||
required>
|
|
||||||
</mdui-text-field>
|
|
||||||
|
|
||||||
<mdui-button type="submit">Зарегистрироваться</mdui-button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="text-center">
|
|
||||||
<p>
|
|
||||||
Уже есть аккаунт?
|
|
||||||
<a href="#" id="login-link" class="link">Войдите</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="auth-body">
|
||||||
</div>
|
<div id="login-alerts"></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chat Interface -->
|
<form id="login-form-element">
|
||||||
<div id="chat-interface" style="display: none;">
|
<mdui-text-field
|
||||||
<div class="all-container">
|
label="Имя пользователя"
|
||||||
<div class="chat-list" id="chat-list">
|
id="login-username"
|
||||||
<header class="chat-header-left">
|
name="username"
|
||||||
<div id="productname">Loading...</div>
|
variant="outlined"
|
||||||
<div class="profile">
|
icon="person--filled"
|
||||||
<a href="#" id="profile-open">
|
autocomplete="username"
|
||||||
<img src="./src/images/default-avatar.png" alt="" id="preview1" />
|
required>
|
||||||
</a>
|
</mdui-text-field>
|
||||||
|
<mdui-text-field
|
||||||
|
label="Пароль"
|
||||||
|
id="login-password"
|
||||||
|
name="password"
|
||||||
|
variant="outlined"
|
||||||
|
type="password"
|
||||||
|
toggle-password
|
||||||
|
icon="password--filled"
|
||||||
|
autocomplete="current-password"
|
||||||
|
required>
|
||||||
|
</mdui-text-field>
|
||||||
|
|
||||||
|
<mdui-button type="submit">Войти</mdui-button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<p>Ещё нет аккаунта? <a href="#" id="register-link" class="link">Зарегистрируйтесь</a></p>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
|
||||||
<div class="chat-tabs">
|
|
||||||
<mdui-tabs value="chats" full-width>
|
|
||||||
<mdui-tab value="chats">
|
|
||||||
Чаты
|
|
||||||
</mdui-tab>
|
|
||||||
<mdui-tab value="channels">
|
|
||||||
Каналы
|
|
||||||
</mdui-tab>
|
|
||||||
<mdui-tab value="contacts">
|
|
||||||
Контакты
|
|
||||||
</mdui-tab>
|
|
||||||
|
|
||||||
<mdui-tab-panel slot="panel" value="chats">
|
|
||||||
<mdui-list>
|
|
||||||
<mdui-list-item headline="Общий чат" description="Вы: Последнее сообщение" id="chat-list-chat-1">
|
|
||||||
<img src="./src/images/default-avatar.png" alt="" slot="icon" />
|
|
||||||
</mdui-list-item>
|
|
||||||
<mdui-list-item headline="Общий чат 2" description="Вы: Последнее сообщение" id="chat-list-chat-2">
|
|
||||||
<img src="./src/images/default-avatar.png" alt="" slot="icon" />
|
|
||||||
</mdui-list-item>
|
|
||||||
</mdui-list>
|
|
||||||
</mdui-tab-panel>
|
|
||||||
<mdui-tab-panel slot="panel" value="channels">Скоро будет...</mdui-tab-panel>
|
|
||||||
<mdui-tab-panel slot="panel" value="contacts">Скоро будет...</mdui-tab-panel>
|
|
||||||
</mdui-tabs>
|
|
||||||
</div>
|
</div>
|
||||||
<mdui-bottom-app-bar>
|
|
||||||
<mdui-button-icon icon="settings--filled" id="settings-open"></mdui-button-icon>
|
|
||||||
<mdui-button-icon icon="group_add--filled"></mdui-button-icon>
|
|
||||||
<div style="flex-grow: 1"></div>
|
|
||||||
<mdui-fab icon="edit--filled"></mdui-fab>
|
|
||||||
</mdui-bottom-app-bar>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-container">
|
</div>
|
||||||
<div class="chat-main" id="chat-inner">
|
|
||||||
<div class="chat-header">
|
<!-- Register Form -->
|
||||||
<img src="src/images/default-avatar.png" alt="Avatar" class="chat-header-avatar">
|
<div id="register-form" class="auth-container" style="display: none;">
|
||||||
<div class="chat-header-info">
|
<div class="auth-card fade-in">
|
||||||
<div class="info-chat">
|
<div class="auth-header">
|
||||||
<h4 id="chat-name">Общий чат</h4>
|
<h2>
|
||||||
<p>
|
<span class="material-symbols filled large">person_add</span>
|
||||||
<span class="online-status"></span>
|
Регистрация
|
||||||
Онлайн
|
</h2>
|
||||||
</p>
|
<p>Создайте новый аккаунт</p>
|
||||||
</div>
|
</div>
|
||||||
<a href="#" id="hide-chat">Свернуть чат</a>
|
<div class="auth-body">
|
||||||
|
<div id="register-alerts"></div>
|
||||||
|
|
||||||
|
<form id="register-form-element">
|
||||||
|
<mdui-text-field
|
||||||
|
label="Имя пользователя"
|
||||||
|
id="register-username"
|
||||||
|
name="username"
|
||||||
|
variant="outlined"
|
||||||
|
icon="person--filled"
|
||||||
|
autocomplete="username"
|
||||||
|
maxlength="20"
|
||||||
|
counter
|
||||||
|
required>
|
||||||
|
</mdui-text-field>
|
||||||
|
<mdui-text-field
|
||||||
|
label="Пароль"
|
||||||
|
id="register-password"
|
||||||
|
name="password"
|
||||||
|
variant="outlined"
|
||||||
|
type="password"
|
||||||
|
toggle-password
|
||||||
|
icon="password--filled"
|
||||||
|
autocomplete="new-password"
|
||||||
|
required>
|
||||||
|
</mdui-text-field>
|
||||||
|
<mdui-text-field
|
||||||
|
label="Подтвердите пароль"
|
||||||
|
id="register-confirm-password"
|
||||||
|
name="confirm_password"
|
||||||
|
variant="outlined"
|
||||||
|
type="password"
|
||||||
|
toggle-password
|
||||||
|
icon="password--filled"
|
||||||
|
autocomplete="new-password"
|
||||||
|
required>
|
||||||
|
</mdui-text-field>
|
||||||
|
|
||||||
|
<mdui-button type="submit">Зарегистрироваться</mdui-button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<p>
|
||||||
|
Уже есть аккаунт?
|
||||||
|
<a href="#" id="login-link" class="link">Войдите</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat Interface -->
|
||||||
|
<div id="chat-interface" style="display: none;">
|
||||||
|
<div class="all-container">
|
||||||
|
<div class="chat-list" id="chat-list">
|
||||||
|
<header class="chat-header-left">
|
||||||
|
<div id="productname">Loading...</div>
|
||||||
|
<div class="profile">
|
||||||
|
<a href="#" id="profile-open">
|
||||||
|
<img src="./src/images/default-avatar.png" alt="" id="preview1" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</header>
|
||||||
|
<div class="chat-tabs">
|
||||||
|
<mdui-tabs value="chats" full-width>
|
||||||
|
<mdui-tab value="chats">
|
||||||
|
Чаты
|
||||||
|
</mdui-tab>
|
||||||
|
<mdui-tab value="channels">
|
||||||
|
Каналы
|
||||||
|
</mdui-tab>
|
||||||
|
<mdui-tab value="contacts">
|
||||||
|
Контакты
|
||||||
|
</mdui-tab>
|
||||||
|
|
||||||
<div class="chat-messages" id="chat-messages">
|
<mdui-tab-panel slot="panel" value="chats">
|
||||||
<!-- Messages will be loaded here dynamically -->
|
<mdui-list>
|
||||||
|
<mdui-list-item headline="Общий чат" description="Вы: Последнее сообщение" id="chat-list-chat-1">
|
||||||
|
<img src="./src/images/default-avatar.png" alt="" slot="icon" />
|
||||||
|
</mdui-list-item>
|
||||||
|
<mdui-list-item headline="Общий чат 2" description="Вы: Последнее сообщение" id="chat-list-chat-2">
|
||||||
|
<img src="./src/images/default-avatar.png" alt="" slot="icon" />
|
||||||
|
</mdui-list-item>
|
||||||
|
</mdui-list>
|
||||||
|
</mdui-tab-panel>
|
||||||
|
<mdui-tab-panel slot="panel" value="channels">Скоро будет...</mdui-tab-panel>
|
||||||
|
<mdui-tab-panel slot="panel" value="contacts">Скоро будет...</mdui-tab-panel>
|
||||||
|
</mdui-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
<mdui-bottom-app-bar>
|
||||||
|
<mdui-button-icon icon="settings--filled" id="settings-open"></mdui-button-icon>
|
||||||
|
<mdui-button-icon icon="group_add--filled"></mdui-button-icon>
|
||||||
|
<div style="flex-grow: 1"></div>
|
||||||
|
<mdui-fab icon="edit--filled"></mdui-fab>
|
||||||
|
</mdui-bottom-app-bar>
|
||||||
|
</div>
|
||||||
|
<div class="chat-container">
|
||||||
|
<div class="chat-main" id="chat-inner">
|
||||||
|
<div class="chat-header">
|
||||||
|
<img src="src/images/default-avatar.png" alt="Avatar" class="chat-header-avatar">
|
||||||
|
<div class="chat-header-info">
|
||||||
|
<div class="info-chat">
|
||||||
|
<h4 id="chat-name">Общий чат</h4>
|
||||||
|
<p>
|
||||||
|
<span class="online-status"></span>
|
||||||
|
Онлайн
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a href="#" id="hide-chat">Свернуть чат</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="chat-input-wrapper">
|
<div class="chat-messages" id="chat-messages">
|
||||||
<div class="chat-input">
|
<!-- Messages will be loaded here dynamically -->
|
||||||
<form class="input-group" id="message-form">
|
</div>
|
||||||
<input type="text" class="message-input" id="message-input" placeholder="Напишите сообщение..." autocomplete="off">
|
|
||||||
<button type="submit" class="send-btn">
|
<div class="chat-input-wrapper">
|
||||||
<span class="material-symbols filled">send</span>
|
<div class="chat-input">
|
||||||
</button>
|
<form class="input-group" id="message-form">
|
||||||
</form>
|
<input type="text" class="message-input" id="message-input" placeholder="Напишите сообщение..." autocomplete="off">
|
||||||
|
<button type="submit" class="send-btn">
|
||||||
|
<span class="material-symbols filled">send</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export function showLogin(): void {
|
|||||||
document.getElementById('register-form')!.style.display = 'none';
|
document.getElementById('register-form')!.style.display = 'none';
|
||||||
document.getElementById('chat-interface')!.style.display = 'none';
|
document.getElementById('chat-interface')!.style.display = 'none';
|
||||||
clearAlerts();
|
clearAlerts();
|
||||||
|
document.getElementById("electron-title-bar")!.classList.add("color-surface");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,6 +69,7 @@ export function showRegister(): void {
|
|||||||
document.getElementById('register-form')!.style.display = 'flex';
|
document.getElementById('register-form')!.style.display = 'flex';
|
||||||
document.getElementById('chat-interface')!.style.display = 'none';
|
document.getElementById('chat-interface')!.style.display = 'none';
|
||||||
clearAlerts();
|
clearAlerts();
|
||||||
|
document.getElementById("electron-title-bar")!.classList.add("color-surface");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,6 +83,7 @@ export function showChat(): void {
|
|||||||
document.getElementById('register-form')!.style.display = 'none';
|
document.getElementById('register-form')!.style.display = 'none';
|
||||||
document.getElementById('chat-interface')!.style.display = 'block';
|
document.getElementById('chat-interface')!.style.display = 'block';
|
||||||
loadMessages();
|
loadMessages();
|
||||||
|
document.getElementById("electron-title-bar")!.classList.remove("color-surface");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
background-color: $color-dark-surface;
|
background-color: $color-dark-surface;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
@use "common/material" as *;
|
@use "common/material" as *;
|
||||||
|
|
||||||
#chat-interface {
|
#chat-interface {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: $color-dark-surface-container;
|
background-color: $color-dark-surface-container;
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
@use "common/material" as *;
|
||||||
|
|
||||||
|
#electron-title-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.electron {
|
||||||
|
#electron-title-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: $color-dark-surface-container;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 10;
|
||||||
|
transition: background-color 0.5s ease;
|
||||||
|
|
||||||
|
&.color-surface {
|
||||||
|
background-color: $color-dark-surface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#window-title {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.platform-darwin .macos-padding {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .window-controls {
|
||||||
|
// -webkit-app-region: no-drag;
|
||||||
|
|
||||||
|
// .hidden {
|
||||||
|
// display: none;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile {
|
#profile {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
@use "common/components";
|
@use "common/components";
|
||||||
@use "common/colors" as *;
|
@use "common/colors" as *;
|
||||||
@use "common/material" as *;
|
@use "common/material" as *;
|
||||||
|
@use "electron";
|
||||||
|
|
||||||
@use "lib/fonts/montserrat";
|
@use "lib/fonts/montserrat";
|
||||||
@use "lib/fonts/material-symbols";
|
@use "lib/fonts/material-symbols";
|
||||||
@@ -24,6 +25,13 @@ body {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
#main-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mdui-dialog {
|
mdui-dialog {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import "../electron.d.ts";
|
||||||
|
import { PRODUCT_NAME } from "./config";
|
||||||
|
|
||||||
|
if (window.electronInterface !== undefined) {
|
||||||
|
console.log("Running in Electron");
|
||||||
|
document.documentElement.classList.add("electron", `platform-${window.electronInterface.platform}`);
|
||||||
|
document.getElementById("window-title")!.textContent = PRODUCT_NAME;
|
||||||
|
} else {
|
||||||
|
console.log("Running in normal browser");
|
||||||
|
}
|
||||||
@@ -16,3 +16,4 @@ import "./init";
|
|||||||
import "./profile";
|
import "./profile";
|
||||||
import "./message-context-menu";
|
import "./message-context-menu";
|
||||||
import "./user-profile-dialog";
|
import "./user-profile-dialog";
|
||||||
|
import "./electron";
|
||||||
+35
-18
@@ -1,22 +1,39 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig, PluginOption } from 'vite';
|
||||||
import { createHtmlPlugin } from 'vite-plugin-html'
|
import { createHtmlPlugin } from 'vite-plugin-html';
|
||||||
import autoprefixer from 'autoprefixer'
|
import autoprefixer from 'autoprefixer';
|
||||||
|
import electron from 'vite-plugin-electron/simple';
|
||||||
|
|
||||||
|
const plugins: PluginOption[] = [
|
||||||
|
createHtmlPlugin({
|
||||||
|
minify: {
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeComments: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
removeScriptTypeAttributes: true,
|
||||||
|
removeStyleLinkTypeAttributes: true,
|
||||||
|
useShortDoctype: true,
|
||||||
|
minifyCSS: true,
|
||||||
|
minifyJS: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
if (process.env.VITE_ELECTRON) {
|
||||||
|
plugins.push(
|
||||||
|
electron({
|
||||||
|
main: {
|
||||||
|
entry: "electron/main.ts",
|
||||||
|
},
|
||||||
|
preload: {
|
||||||
|
input: "frontend/electron/preload.ts"
|
||||||
|
},
|
||||||
|
renderer: {},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: plugins,
|
||||||
createHtmlPlugin({
|
|
||||||
minify: {
|
|
||||||
collapseWhitespace: true,
|
|
||||||
removeComments: true,
|
|
||||||
removeRedundantAttributes: true,
|
|
||||||
removeScriptTypeAttributes: true,
|
|
||||||
removeStyleLinkTypeAttributes: true,
|
|
||||||
useShortDoctype: true,
|
|
||||||
minifyCSS: true,
|
|
||||||
minifyJS: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 8301,
|
port: 8301,
|
||||||
@@ -50,4 +67,4 @@ export default defineConfig({
|
|||||||
cssMinify: true,
|
cssMinify: true,
|
||||||
assetsInlineLimit: 0
|
assetsInlineLimit: 0
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
+11
-2
@@ -3,6 +3,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "frontend/dist-electron/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"backend:run": "cd backend && dotenv -e ../deployment/.env -- ../.venv/bin/fastapi dev --port 8300 main.py",
|
"backend:run": "cd backend && dotenv -e ../deployment/.env -- ../.venv/bin/fastapi dev --port 8300 main.py",
|
||||||
"backend:dependencies": "python3 -m venv .venv && ./.venv/bin/pip3 install -r backend/requirements.txt",
|
"backend:dependencies": "python3 -m venv .venv && ./.venv/bin/pip3 install -r backend/requirements.txt",
|
||||||
@@ -10,25 +11,33 @@
|
|||||||
"backend:clean": "rm -rf backend/data",
|
"backend:clean": "rm -rf backend/data",
|
||||||
"frontend:dev": "vite frontend",
|
"frontend:dev": "vite frontend",
|
||||||
"frontend:build": "tsc --project frontend && vite build frontend",
|
"frontend:build": "tsc --project frontend && vite build frontend",
|
||||||
|
"frontend:electron:dev": "VITE_ELECTRON=true npm run frontend:dev",
|
||||||
|
"frontend:electron:dependencies": "cd frontend/electron/forge && npm install",
|
||||||
|
"frontend:electron:build": "VITE_ELECTRON=true npm run frontend:build && cp -r frontend/dist frontend/dist-electron frontend/electron/forge && cd frontend/electron/forge && npm run make",
|
||||||
"frontend:preview": "vite preview frontend",
|
"frontend:preview": "vite preview frontend",
|
||||||
"frontend:dependencies": "npm install --ignore-scripts",
|
"frontend:dependencies": "npm install --ignore-scripts",
|
||||||
"frontend:clean": "rm -rf frontend/dist",
|
"frontend:clean": "rm -rf frontend/dist",
|
||||||
"dev": "concurrently 'npm run frontend:dev' 'npm run backend:run'",
|
"dev": "concurrently 'npm run frontend:dev' 'npm run backend:run'",
|
||||||
"build": "npm run frontend:build",
|
"dev:electron": "concurrently 'npm run frontend:electron:dev' 'npm run backend:run'",
|
||||||
|
"build:electron": "npm run frontend:electron:build",
|
||||||
|
"build": "npm run frontend:build && npm run build:electron",
|
||||||
"preview": "cd deployment && docker compose up --build --watch",
|
"preview": "cd deployment && docker compose up --build --watch",
|
||||||
"preview:clean": "cd deployment && docker compose down -v",
|
"preview:clean": "cd deployment && docker compose down -v",
|
||||||
"clean": "npm run backend:clean && npm run frontend:clean && npm run preview:clean",
|
"clean": "npm run backend:clean && npm run frontend:clean && npm run preview:clean",
|
||||||
"install": "npm run backend:dependencies && cp deployment/.env.example deployment/.env"
|
"install": "npm run backend:dependencies && npm run frontend:electron:dependencies && cp deployment/.env.example deployment/.env"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"concurrently": "^9.2.0",
|
"concurrently": "^9.2.0",
|
||||||
|
"electron": "^37.3.1",
|
||||||
"dotenv-cli": "^10.0.0",
|
"dotenv-cli": "^10.0.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"sass-embedded": "^1.90.0",
|
"sass-embedded": "^1.90.0",
|
||||||
"terser": "^5.43.1",
|
"terser": "^5.43.1",
|
||||||
"typescript": "~5.9.2",
|
"typescript": "~5.9.2",
|
||||||
"vite": "^7.1.2",
|
"vite": "^7.1.2",
|
||||||
|
"vite-plugin-electron": "^0.29.0",
|
||||||
|
"vite-plugin-electron-renderer": "^0.14.6",
|
||||||
"vite-plugin-html": "^3.2.2"
|
"vite-plugin-html": "^3.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user