diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 167ca31..92af7ea 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,72 +17,39 @@ permissions:
contents: read
jobs:
- build:
- name: Build (${{ matrix.os }})
- runs-on: ${{ matrix.os }}
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu-latest, macos-latest, windows-latest]
+ build-linux:
+ name: Build Linux
+ runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- - name: Force npm to use Bash
- if: runner.os == 'Windows'
- run: npm config set script-shell "C:\Program Files\Git\bin\bash.exe"
-
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
- - name: Cache npm (Linux/macOS)
- if: runner.os != 'Windows'
+ - name: Cache dependencies
uses: actions/cache@v4
with:
- path: ~/.npm
- key: npm-${{ runner.os }}-node-24-${{ hashFiles('package.json') }}
+ path: node_modules
+ key: npm-ubuntu-latest-node-24-${{ hashFiles('package.json') }}
restore-keys: |
- npm-${{ runner.os }}-node-24-
+ npm-ubuntu-latest-node-24-
- - name: Cache npm (Windows)
- if: runner.os == 'Windows'
- uses: actions/cache@v4
- with:
- path: ~\AppData\Local\npm-cache
- key: npm-${{ runner.os }}-node-24-${{ hashFiles('package.json') }}
- restore-keys: |
- npm-${{ runner.os }}-node-24-
-
- - name: Install root deps (no scripts)
+ - name: Install dependencies
run: npm install --ignore-scripts --no-audit --no-fund
- - name: Install Electron Forge deps
- run: npm run frontend:electron:dependencies
-
- - name: Cache Electron downloads (Linux/macOS)
- if: runner.os != 'Windows'
+ - name: Cache Electron downloads
uses: actions/cache@v4
with:
path: |
~/.cache/electron
~/.cache/electron-builder
- key: electron-${{ runner.os }}-${{ hashFiles('package.json', 'frontend/electron/forge/package.json') }}
+ key: electron-ubuntu-latest-${{ hashFiles('package.json', 'package.json') }}
restore-keys: |
- electron-${{ runner.os }}-
-
- - name: Cache Electron downloads (Windows)
- if: runner.os == 'Windows'
- uses: actions/cache@v4
- with:
- path: |
- ~\AppData\Local\electron\Cache
- ~\AppData\Local\electron-builder\Cache
- key: electron-${{ runner.os }}-${{ hashFiles('package.json', 'frontend/electron/forge/package.json') }}
- restore-keys: |
- electron-${{ runner.os }}-
+ electron-ubuntu-latest-
- name: Build Electron app
shell: bash
@@ -91,8 +58,102 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
- name: fromchat-${{ runner.os }}
+ name: FromChat-linux
path: |
- frontend/electron/forge/out/**
+ frontend/build/electron/forge/make/*/**
if-no-files-found: error
+ build-macos:
+ name: Build macOS
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '24'
+
+ - name: Cache dependencies
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: npm-macos-latest-node-24-${{ hashFiles('package.json') }}
+ restore-keys: |
+ npm-macos-latest-node-24-
+
+ - name: Install dependencies
+ run: npm install --ignore-scripts --no-audit --no-fund
+
+ # - name: Cache Electron downloads
+ # uses: actions/cache@v4
+ # with:
+ # path: |
+ # ~/.cache/electron
+ # ~/.cache/electron-builder
+ # key: electron-macos-latest-${{ hashFiles('package.json') }}
+ # restore-keys: |
+ # electron-macos-latest-
+
+ - name: Build Electron app
+ shell: bash
+ run: npm run build:electron
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: FromChat-macOS
+ path: |
+ frontend/build/electron/forge/make/zip/darwin/*/**.zip
+ if-no-files-found: error
+
+ build-windows:
+ name: Build Windows
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Force npm to use Bash
+ run: npm config set script-shell "C:\Program Files\Git\bin\bash.exe"
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '24'
+
+ - name: Cache dependencies
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: npm-windows-latest-node-24-${{ hashFiles('package.json') }}
+ restore-keys: |
+ npm-windows-latest-node-24-
+
+ - name: Install dependencies
+ run: npm install --ignore-scripts --no-audit --no-fund
+
+ - name: Cache Electron downloads
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~\AppData\Local\electron\Cache
+ ~\AppData\Local\electron-builder\Cache
+ key: electron-windows-latest-${{ hashFiles('package.json') }}
+ restore-keys: |
+ electron-windows-latest-
+
+ - name: Build Electron app
+ shell: bash
+ run: npm run build:electron
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: FromChat-windows
+ path: |
+ frontend/build/electron/forge/make/zip/win32/x64
+ if-no-files-found: error
\ No newline at end of file
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 6277bd0..d183da5 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -13,7 +13,6 @@ on:
- ".nvmrc"
- ".github/workflows/deploy.yml"
- "!frontend/electron/**"
- - "!frontend/src/electron/**"
- "!**.d.ts"
workflow_dispatch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index fa1af56..16545c5 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -2,5 +2,8 @@
"files.exclude": {
"**/__pycache__": true,
"**/package-lock.json": true
- }
+ },
+ "github-actions.workflows.pinned.workflows": [],
+ "github-actions.workflows.pinned.workflows.ignore": true,
+ "github-actions.workflows.pinned.workflows.ignoreContextAccess": true
}
\ No newline at end of file
diff --git a/deployment/frontend/Dockerfile b/deployment/frontend/Dockerfile
index 8f00220..6f37ca0 100644
--- a/deployment/frontend/Dockerfile
+++ b/deployment/frontend/Dockerfile
@@ -36,7 +36,7 @@ USER app
# 3.1. Frontend static files
WORKDIR /app
-COPY --from=frontend --chown=app /app/frontend/dist .
+COPY --from=frontend --chown=app /app/frontend/build/normal/dist .
# 3.2. Static file server
WORKDIR /server
diff --git a/frontend/electron/forge/package.json b/frontend/electron/forge/package.json
deleted file mode 100644
index 9ca842d..0000000
--- a/frontend/electron/forge/package.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "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"
- }
-}
diff --git a/frontend/electron/main.ts b/frontend/electron/main.ts
index cb6e23a..46c4322 100644
--- a/frontend/electron/main.ts
+++ b/frontend/electron/main.ts
@@ -4,7 +4,7 @@ import path from "node:path";
app.whenReady().then(() => {
const win = new BrowserWindow({
title: 'Main window',
- minWidth: 650,
+ minWidth: 800,
minHeight: 420,
webPreferences: {
preload: path.join(import.meta.dirname, "preload.mjs")
@@ -16,11 +16,11 @@ app.whenReady().then(() => {
y: 16 - 4
},
titleBarOverlay: process.platform !== "darwin"
- })
+ });
if (process.env.VITE_DEV_SERVER_URL) {
- win.loadURL(process.env.VITE_DEV_SERVER_URL)
+ win.loadURL(process.env.VITE_DEV_SERVER_URL);
} else {
- win.loadFile('dist/index.html');
+ win.loadFile('frontend/build/electron/dist/index.html');
}
});
\ No newline at end of file
diff --git a/frontend/electron/forge/forge.config.ts b/frontend/forge.config.ts
similarity index 80%
rename from frontend/electron/forge/forge.config.ts
rename to frontend/forge.config.ts
index ab5fefd..b96f865 100644
--- a/frontend/electron/forge/forge.config.ts
+++ b/frontend/forge.config.ts
@@ -1,29 +1,28 @@
import { FusesPlugin } from '@electron-forge/plugin-fuses';
import { FuseV1Options, FuseVersion } from '@electron/fuses';
-import type { ForgeConfig } from "@electron-forge/shared-types";
+import type { ForgeConfig } from '@electron-forge/shared-types';
-const config: ForgeConfig = {
+export default {
packagerConfig: {
- asar: true
+ asar: true,
},
+ outDir: "frontend/build/electron/forge",
rebuildConfig: {},
makers: [
- {
- name: '@electron-forge/maker-squirrel',
- config: {},
- },
{
name: '@electron-forge/maker-zip',
config: {},
- platforms: ['darwin'],
+ platforms: ['win32', 'darwin'],
},
{
name: '@electron-forge/maker-deb',
config: {},
+ platforms: ['linux'],
},
{
name: '@electron-forge/maker-rpm',
config: {},
+ platforms: ['linux'],
},
],
plugins: [
@@ -43,6 +42,4 @@ const config: ForgeConfig = {
[FuseV1Options.OnlyLoadAppFromAsar]: true,
}),
],
-};
-
-export default config;
\ No newline at end of file
+} satisfies ForgeConfig;
\ No newline at end of file
diff --git a/frontend/src/electron/electron.ts b/frontend/src/electron/electron.ts
index 4810cc0..0eb9ab1 100644
--- a/frontend/src/electron/electron.ts
+++ b/frontend/src/electron/electron.ts
@@ -5,13 +5,11 @@
* @version 1.0.0
*/
-import { PRODUCT_NAME } from "../core/config.ts";
-import { id } from "../utils/utils.ts";
+export const isElectron = import.meta.env.VITE_ELECTRON && window.electronInterface != undefined;
-if (window.electronInterface !== undefined) {
+if (isElectron) {
console.log("Running in Electron");
document.documentElement.classList.add("electron", `platform-${window.electronInterface.platform}`);
- id("window-title").textContent = PRODUCT_NAME;
} else {
console.log("Running in normal browser");
}
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 5dd0530..b923780 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -10,6 +10,7 @@ import "mdui/mdui.css";
import "./utils/material";
import "./core/init";
+import "./electron/electron";
import { createRoot } from 'react-dom/client';
import App from './ui/App';
import { StrictMode } from 'react';
diff --git a/frontend/src/resources/css/_electron.scss b/frontend/src/resources/css/_electron.scss
index a5db111..69aa4ed 100644
--- a/frontend/src/resources/css/_electron.scss
+++ b/frontend/src/resources/css/_electron.scss
@@ -9,13 +9,14 @@ html.electron {
display: flex;
flex-direction: row;
gap: 8px;
- height: 40px;
+ min-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;
+ flex-shrink: 0;
&.color-surface {
background-color: $color-dark-surface;
@@ -29,15 +30,12 @@ html.electron {
font-weight: 500;
}
- &.platform-darwin .macos-padding {
- width: 70px;
+ #main-wrapper {
+ flex: 1;
+ min-height: 0;
}
- // .window-controls {
- // -webkit-app-region: no-drag;
-
- // .hidden {
- // display: none;
- // }
- // }
+ &.platform-darwin .macos-padding {
+ width: 80px;
+ }
}
\ No newline at end of file
diff --git a/frontend/src/resources/css/style.scss b/frontend/src/resources/css/style.scss
index a63eebf..4cfe673 100644
--- a/frontend/src/resources/css/style.scss
+++ b/frontend/src/resources/css/style.scss
@@ -24,17 +24,20 @@ body {
background-color: $color-dark-surface;
color: $color-dark-on-surface;
line-height: 1.6;
+
+ #main-wrapper {
+ flex: 1;
+ position: relative;
+ min-height: 0;
+ }
+}
+
+body, #root {
height: 100vh;
position: relative;
margin: 0;
display: flex;
flex-direction: column;
-
- #main-wrapper {
- flex: 1;
- position: relative;
- height: 100vh;
- }
}
mdui-dialog {
diff --git a/frontend/src/ui/App.tsx b/frontend/src/ui/App.tsx
index 9ece2e2..2712a2e 100644
--- a/frontend/src/ui/App.tsx
+++ b/frontend/src/ui/App.tsx
@@ -1,4 +1,5 @@
import { MINIMUM_WIDTH } from "../core/config";
+import { isElectron } from "../electron/electron";
import { ElectronTitleBar } from "./components/Electron";
import useWindowSize from "./hooks/useWindowSize";
import ChatScreen from "./screen/ChatScreen";
@@ -17,7 +18,7 @@ export default function App() {
restoreUserFromStorage();
}, [restoreUserFromStorage]);
- if (width < MINIMUM_WIDTH) {
+ if (!isElectron && width < MINIMUM_WIDTH) {
return