Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App Icons / System Tray #14

Merged
merged 14 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/publish_all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Publish All Platforms

on:
workflow_dispatch:

jobs:
build-windows:
secrets: inherit
uses: ./.github/workflows/publish_windows.yml
build-apple:
secrets: inherit
uses: ./.github/workflows/publish_macos.yml
build-linux:
secrets: inherit
uses: ./.github/workflows/publish_linux.yml
2 changes: 1 addition & 1 deletion .github/workflows/publish_linux.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# .github/workflows/publish_linux.yml
name: Publish App
name: Release Linux app

on:
workflow_dispatch:
Expand Down
Binary file added assets/UI/Comfy_Logo.icns
Binary file not shown.
Binary file added assets/UI/Comfy_Logo.ico
Binary file not shown.
Binary file added assets/UI/Comfy_Logo_x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/UI/Comfy_Logo_x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/UI/Comfy_Logo_x16_BW.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/UI/Comfy_Logo_x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/UI/Comfy_Logo_x32_BW.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/UI/Comfy_Logo_x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion assets/UI/run_cpu.bat
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
.\resources\UI\python_embedded\python.exe -s .//resources//UI//ComfyUI//main.py --cpu --windows-standalone-build
.\resources\python\python.exe -s .//resources//ComfyUI//main.py --cpu --windows-standalone-build
pause
5 changes: 4 additions & 1 deletion forge.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ForgeConfig } from '@electron-forge/shared-types';
import { MakerSquirrel } from '@electron-forge/maker-squirrel';
import { MakerZIP } from '@electron-forge/maker-zip';
import { MakerDMG } from '@electron-forge/maker-dmg';
import { MakerDeb } from '@electron-forge/maker-deb';
import { MakerRpm } from '@electron-forge/maker-rpm';
import { VitePlugin } from '@electron-forge/plugin-vite';
Expand Down Expand Up @@ -31,8 +32,9 @@ const config: ForgeConfig = {
teamId: process.env.APPLE_TEAM_ID
},
},
extraResource: ['./assets'],
extraResource: ['./assets/UI', './assets/ComfyUI', './assets/python.tgz'],

icon: process.platform === 'linux' ? 'assets/UI/Comfy_Logo_x128.png' : 'assets/UI/Comfy_Logo',
},
rebuildConfig: {},
hooks: {
Expand All @@ -45,6 +47,7 @@ const config: ForgeConfig = {
},
},
makers: [
new MakerSquirrel({frameworkVersion:'net481'}, ['win32']),
new MakerZIP({}, ['darwin', 'win32']),
// the forge build produces a "ComfyUI" bin, but the rpm/deb makers expect a "comfyui-electron" bin (matching the "name" in package.json). We override this below
new MakerRpm({
Expand Down
6 changes: 6 additions & 0 deletions forge.env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ declare global {
// Used for hot reload after preload scripts.
viteDevServers: Record<string, import('vite').ViteDevServer>;
}
interface ProcessEnv {
PUBLISH? : string | boolean,
APPLE_ID: string,
APPLE_PASSWORD: string,
APPLE_TEAM_ID: string,
}
}

type VitePluginConfig = ConstructorParameters<typeof import('@electron-forge/plugin-vite').VitePlugin>[0];
Expand Down
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
<title>ComfyUI</title>

</head>
<body>
<h1>💖 Hello World!</h1>
<p>Welcome to your Electron application.</p>
<h1>Python Server is Loading</h1>
<p></p>
<script type="module" src="/src/renderer.ts"></script>
</body>
</html>
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "comfyui-electron",
"productName": "ComfyUI",
"version": "0.0.3",
"version": "0.0.5",
"description": "The best modular GUI to run AI diffusion models.",
"main": ".vite/build/main.js",
"packageManager": "[email protected]",
Expand All @@ -26,6 +26,7 @@
"devDependencies": {
"@electron-forge/cli": "^7.4.0",
"@electron-forge/maker-deb": "^7.4.0",
"@electron-forge/maker-dmg": "^7.4.0",
"@electron-forge/maker-rpm": "^7.4.0",
"@electron-forge/maker-squirrel": "^7.4.0",
"@electron-forge/maker-zip": "^7.4.0",
Expand Down
108 changes: 92 additions & 16 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { spawn, ChildProcess } from 'node:child_process';
import * as fs from 'node:fs/promises';
import net from 'node:net';
import path from 'node:path';
import { SetupTray } from './tray';

import dotenv from "dotenv";
import { app, BrowserWindow } from 'electron';
import { app, BrowserWindow, webContents } from 'electron';
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
import('electron-squirrel-startup').then(ess => {
const {default: check} = ess;
Expand Down Expand Up @@ -36,12 +37,39 @@ const createWindow = () => {
});

// Load the UI from the Python server's URL
mainWindow.loadURL('http://localhost:8188/');

//mainWindow.loadURL('http://localhost:8188/');
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));

// Set up the System Tray Icon for all platforms
// Returns a tray so you can set a global var to access.
SetupTray(mainWindow);

// Overrides the behavior of closing the window to allow for
// the python server to continue to run in the background
mainWindow.on('close' , (e:Electron.Event) => {
e.preventDefault();
mainWindow.hide();
// Mac Only Behavior
if (process.platform === 'darwin') {
app.dock.hide();
}
})
// Open the DevTools.
mainWindow.webContents.openDevTools();
// mainWindow.webContents.openDevTools();
};

// Server Heartbeat Listener Variables
let serverHeartBeatReference: NodeJS.Timeout = null;
const serverHeartBeatInterval: number = 15 * 1000; //15 Seconds
async function serverHeartBeat() {
const isReady = await isPortInUse(host, port);
if (isReady) {
// Getting webcontents[0] is not reliable if app started with dev window
webContents.getAllWebContents()[0].send("python-server-status", "active");
} else {
webContents.getAllWebContents()[0].send("python-server-status", "false");
}
}

const isPortInUse = (host: string, port: number): Promise<boolean> => {
return new Promise((resolve) => {
Expand All @@ -64,13 +92,25 @@ const isPortInUse = (host: string, port: number): Promise<boolean> => {
});
};

// Launch Python Server Variables
const maxFailWait: number = 10 * 2000; // 10seconds
let currentWaitTime: number = 0;
let spawnServerTimeout: NodeJS.Timeout = null;

const launchPythonServer = async (args: {userResourcesPath: string, appResourcesPath: string}) => {
const {userResourcesPath, appResourcesPath} = args;

const isServerRunning = await isPortInUse(host, port);
if (isServerRunning) {
console.log('Python server is already running');
// Server has been started outside the app, so attach to it.
setTimeout(() => {
// Not sure if needed but wait a few moments before sending the connect message up.
webContents.getAllWebContents()[0].send("python-server-status", "active");
}, 5000);
clearInterval(serverHeartBeatReference);
webContents.getAllWebContents()[0].loadURL('http://localhost:8188/');
serverHeartBeatReference = setInterval(serverHeartBeat, serverHeartBeatInterval);
return Promise.resolve();
}

Expand Down Expand Up @@ -133,9 +173,21 @@ const launchPythonServer = async (args: {userResourcesPath: string, appResources
const checkInterval = 1000; // Check every 1 second

const checkServerReady = async () => {
currentWaitTime += 1000;
if (currentWaitTime > maxFailWait) {
//Something has gone wrong and we need to backout.
clearTimeout(spawnServerTimeout);
reject("Python Server Failed To Start");
}
const isReady = await isPortInUse(host, port);
if (isReady) {
console.log('Python server is ready');
// Start the Heartbeat listener, send connected message to Renderer and resolve promise.
serverHeartBeatReference = setInterval(serverHeartBeat, serverHeartBeatInterval);
webContents.getAllWebContents()[0].send("python-server-status", "active");
//For now just replace the source of the main window to the python server
webContents.getAllWebContents()[0].loadURL('http://localhost:8188/');
clearTimeout(spawnServerTimeout);
resolve();
} else {
console.log('Ping failed. Retrying...');
Expand Down Expand Up @@ -175,32 +227,56 @@ app.on('ready', async () => {
} catch {
// if user-specific resources dir already exists, that is fine
}

try {
await launchPythonServer({userResourcesPath, appResourcesPath});
try {
createWindow();
await launchPythonServer({userResourcesPath, appResourcesPath});
} catch (error) {

console.error(error);
}
});

const killPythonServer = () => {
if (pythonProcess) {
pythonProcess.kill();
pythonProcess = null;
}
console.log('Python server:', pythonProcess);
return new Promise<void>(async(resolve, reject) => {
if (pythonProcess) {
try {
pythonProcess.kill();
setTimeout(() => {
resolve(); // Force the issue after 5seconds
}, 5000);
// Make sure exit code was set so we can close gracefully
while (pythonProcess.exitCode == null)
{}
resolve();
}
catch(error)
{
console.error(error);
reject(error);
}
}
else
{
resolve();
}
})
};

app.on('will-quit', () => {
killPythonServer();
});
app.on('before-quit', async () => {
await killPythonServer();
app.exit();
})

app.on('quit', () => {
app.exit();
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
//app.quit();
}
});

Expand Down
54 changes: 54 additions & 0 deletions src/tray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Tray, Menu, BrowserWindow, app } from "electron";
import path from 'path';

export function SetupTray(mainView: BrowserWindow): Tray {

// Set icon for the tray
// I think there is a way to packaged the icon in so you don't need to reference resourcesPath
const trayImage = path.join(process.resourcesPath, 'UI', process.platform === 'darwin' ? 'Comfy_Logo_x16_BW.png' : 'Comfy_Logo_x32.png');
let tray = new Tray(trayImage);

tray.setTitle('ComfyUI'); // Only Macos, can be blank to JUST show icon
tray.setToolTip('ComfyUI - Server is running');

// For Mac you can have a separate icon when you press.
// The current design language for Mac Eco System is White or Black icon then when you click it is in color
if (process.platform === "darwin")
{
tray.setPressedImage(path.join(process.resourcesPath, 'UI','Comfy_Logo_x16.png'));
}

const contextMenu = Menu.buildFromTemplate([
{
label: 'Show Comfy Window',
click: function () {
mainView.show();
// Mac Only
if (process.platform === 'darwin') {
app.dock.show();
}
},
},
{
label: 'Quit Comfy',
click() {
app.quit();
},
},
{
label: 'Hide',
click() {

mainView.hide();
// Mac Only
if (process.platform === 'darwin') {
app.dock.hide();
}
}
}]);

tray.setContextMenu(contextMenu);

// If we want to make it more dynamic return tray so we can access it later
return tray;
}
Loading
Loading