Files
knock-gui/desktop-angular/HOW_TO.md

11 KiB
Raw Blame History

Пошаговый гайд: как “поднять” существующий 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/:
mkdir /home/su/projects/articles/embed-gui-article/desktop-angular
cd /home/su/projects/articles/embed-gui-article/desktop-angular
npm init -y
  • Устанавливаем зависимости Electron и утилиты для дев-сценария:
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/.)

Создайте папки и файлы:

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:

{
  "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 у вас уже есть).

// 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 (минимальная безопасная заготовка)

// 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).
{
  "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). Это удобно и прозрачно.

{
  "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-фрагмент:

// ... в 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

cd /home/su/projects/articles/embed-gui-article/desktop-angular
npm run dev
  • Скрипт поднимет ng serve из ui/ и откроет Electron, который загрузит http://localhost:4200.

9) Локальная проверка prod без упаковки

# Собираем 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) Упаковка приложения

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:
// пример: 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/....