# Пошаговый гайд: как “поднять” существующий Angular-проект из `ui/` внутри нового Electron-проекта `desktop-angular/` (без копирования собранного кода) Ниже — практичный сценарий: вы создаёте папку `desktop-angular/` с каркасом Electron, подключаете туда исходники Angular (те, что уже лежат в `ui/`), настраиваете общий запуск в dev-режиме и сборку prod-пакета. Никаких переносов уже собранных файлов, всё работает от исходников Angular. ## Что получится в итоге - Dev-режим: одновременно запускается `ng serve` из `ui/` и Electron, который открывает `http://localhost:4200`. - Prod-режим: сначала билдится Angular в `ui/dist/...`, затем Electron упаковывает приложение и грузит `index.html` из собранного Angular. --- ## 1) Создаём новый проект `desktop-angular/` для Electron - В корне репозитория создайте папку `desktop-angular/`: ```bash mkdir /home/su/projects/articles/embed-gui-article/desktop-angular cd /home/su/projects/articles/embed-gui-article/desktop-angular npm init -y ``` - Устанавливаем зависимости Electron и утилиты для дев-сценария: ```bash npm i -D electron electron-builder concurrently wait-on cross-env ``` Рекомендуемая структура (минимум): - `desktop-angular/package.json` — скрипты для dev/prod. - `desktop-angular/src/main/main.js` — основной процесс Electron. - `desktop-angular/src/preload/preload.js` — безопасный мост. - (Без `renderer/` — рендерером будет ваш Angular из `ui/`.) Создайте папки и файлы: ```bash mkdir -p src/main src/preload touch src/main/main.js src/preload/preload.js ``` --- ## 2) Подключаемся к уже существующему Angular-проекту в `ui/` Предполагаем, что Angular уже установлен и запускается из `/home/su/projects/articles/embed-gui-article/ui/` стандартными командами: - Dev: `npm start` (или `ng serve`) - Build: `npm run build` Если нет `npm start`, добавьте его в `ui/package.json`: ```json { "scripts": { "start": "ng serve --port 4200 --disable-host-check", "build": "ng build" } } ``` --- ## 3) Код `main.js` для Electron (dev: URL, prod: файл из dist) Создайте простой `BrowserWindow`, который: - в dev грузит `http://localhost:4200` - в prod грузит файл `../ui/dist/project-front/browser/index.html` Обратите внимание на относительные пути: мы опираемся на реальную структуру вашего репо и текущие выходные пути Angular (`ui/dist/project-front/browser` у вас уже есть). ```javascript // desktop-angular/src/main/main.js const { app, BrowserWindow } = require('electron'); const path = require('path'); const isDev = process.env.NODE_ENV !== 'production'; function createWindow() { const win = new BrowserWindow({ width: 1200, height: 800, show: false, webPreferences: { preload: path.join(__dirname, '../preload/preload.js'), contextIsolation: true, nodeIntegration: false, sandbox: true } }); win.on('ready-to-show', () => win.show()); if (isDev) { win.loadURL('http://localhost:4200'); // win.webContents.openDevTools(); // включите по желанию } else { // В PROD грузим из собранного Angular const indexPath = path.resolve( __dirname, '../../../ui/dist/project-front/browser/index.html' ); win.loadFile(indexPath); } } app.whenReady().then(() => { createWindow(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); ``` --- ## 4) Код `preload.js` (минимальная безопасная заготовка) ```javascript // desktop-angular/src/preload/preload.js const { contextBridge } = require('electron'); contextBridge.exposeInMainWorld('env', { isElectron: true }); ``` В Angular вы сможете проверять наличие `window.env?.isElectron`. --- ## 5) Скрипты в `desktop-angular/package.json` Добавим удобные команды: - `dev`: параллельно запускает Angular dev-сервер в `ui/` и Electron (ждём порт 4200). - `build:ui`: сборка Angular. - `start`: старт Electron в prod-режиме (если нужно локально проверить загрузку собранного Angular без упаковки). - `dist`: упаковка Electron (electron-builder). ```json { "name": "desktop-angular", "version": "1.0.0", "private": true, "main": "src/main/main.js", "scripts": { "dev": "concurrently -k -n UI,ELECTRON -c green,cyan \"cd ../ui && npm start\" \"wait-on http://localhost:4200 && cross-env NODE_ENV=development electron .\"", "build:ui": "cd ../ui && npm run build", "start": "cross-env NODE_ENV=production electron .", "dist": "npm run build:ui && cross-env NODE_ENV=production electron-builder" }, "devDependencies": { "concurrently": "^9.0.0", "cross-env": "^7.0.3", "electron": "^31.0.0", "electron-builder": "^24.13.3", "wait-on": "^7.2.0" } } ``` --- ## 6) Конфигурация упаковки Electron (electron-builder) Добавьте секцию `build` в `desktop-angular/package.json`. Мы не копируем Angular внутрь `desktop-angular` — Electron будет брать собранный Angular прямо из `ui/dist/...` при упаковке (через `extraResources`). Это удобно и прозрачно. ```json { "build": { "appId": "com.yourcompany.knocker", "productName": "Knocker Desktop Angular", "files": [ "src/main/**/*", "src/preload/**/*", "package.json" ], "extraResources": [ { "from": "../ui/dist/project-front/browser", "to": "ui-dist", "filter": ["**/*"] } ], "linux": { "target": ["AppImage"], "category": "Utility", "artifactName": "Knocker-Desktop-Angular-${version}.${ext}" } } } ``` И соответствующее изменение в `main.js` на prod, если хотите грузить из `resources/ui-dist` у упакованного приложения: - В упакованном `.AppImage` ваши ресурсы оказываются в `process.resourcesPath`. - Тогда путь до `index.html` будет: `path.join(process.resourcesPath, 'ui-dist', 'index.html')`. Адаптированный prod-фрагмент: ```javascript // ... в main.js, в ветке !isDev: const indexPath = app.isPackaged ? path.join(process.resourcesPath, 'ui-dist', 'index.html') : path.resolve(__dirname, '../../../ui/dist/project-front/browser/index.html'); win.loadFile(indexPath); ``` Так вы покроете оба случая: локальный prod-запуск и реальный упакованный билд. --- ## 7) CORS, безопасность и доступ к файлам - В dev Angular грузится по `http://localhost:4200` — обычно проблем нет. - В prod Angular грузится с `file://` — убедитесь, что никакие запросы не завязаны на абсолютные HTTP-URL без необходимости. - По умолчанию оставляем `contextIsolation: true`, `nodeIntegration: false`, `sandbox: true`. Для доступа к нативному коду используйте IPC и `preload.js`. --- ## 8) Локальный запуск dev ```bash cd /home/su/projects/articles/embed-gui-article/desktop-angular npm run dev ``` - Скрипт поднимет `ng serve` из `ui/` и откроет Electron, который загрузит `http://localhost:4200`. --- ## 9) Локальная проверка prod без упаковки ```bash # Собираем Angular cd /home/su/projects/articles/embed-gui-article/ui npm run build # Запускаем Electron в prod-режиме cd /home/su/projects/articles/embed-gui-article/desktop-angular npm run start ``` --- ## 10) Упаковка приложения ```bash cd /home/su/projects/articles/embed-gui-article/desktop-angular npm run dist ``` - Angular соберётся. - Electron-упаковщик положит содержимое `ui/dist/project-front/browser` в `resources/ui-dist`. - Приложение загрузит `index.html` из `resources/ui-dist`. --- ## 11) Советы по интеграции Angular и Electron - Детект среды в Angular: ```typescript // пример: app.component.ts isElectron = typeof (window as any).env?.isElectron === 'boolean'; ``` - Статические пути: В Angular используйте относительные пути к ассетам, чтобы они работали и в `http://localhost:4200`, и в `file://` в prod. - IPC: если потребуется обмен с main-процессом, расширяйте `preload.js` и опишите чёткий API через `contextBridge`. --- ## 12) Итоговая структура (ключевые узлы) - `ui/` — как есть, исходники Angular. - `desktop-angular/` - `package.json` — скрипты `dev`, `build:ui`, `start`, `dist`, секция `build` (electron-builder). - `src/main/main.js` — создаёт окно, грузит URL в dev и файл в prod. - `src/preload/preload.js` — мост в рендерер. - Ничего из `ui/` внутрь `desktop-angular/` мы не копируем; работаем поверх исходников. --- ## Что делать, если вы хотите переиспользовать текущую папку `desktop/`? Можно; у вас уже есть `desktop/` с Electron. Тогда: - Либо перенести логику оттуда в `desktop-angular/` (описано выше). - Либо в существующем `desktop/` заменить рендерер на Angular из `ui/` по тем же принципам: dev — `loadURL('http://localhost:4200')`, prod — `loadFile(...)` на `ui/dist/...`.