Skip to content

Commit 98029c2

Browse files
committed
Refactor install.
1 parent 94ce295 commit 98029c2

File tree

4 files changed

+121
-40
lines changed

4 files changed

+121
-40
lines changed

src/config/comfyConfigManager.ts

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import log from 'electron-log/main';
44

55
export type DirectoryStructure = (string | DirectoryStructure)[];
66

7+
/**
8+
* Responsible for creating the ComfyUI directory structure.
9+
*/
710
export class ComfyConfigManager {
811
private static readonly DEFAULT_DIRECTORIES: DirectoryStructure = [
912
'custom_nodes',

src/constants.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export const IPC_CHANNELS = {
99
OPEN_DIALOG: 'open-dialog',
1010
FIRST_TIME_SETUP_COMPLETE: 'first-time-setup-complete',
1111
DEFAULT_INSTALL_LOCATION: 'default-install-location',
12+
// Installation state
13+
INSTALLATION_STATE_CHANGED: 'installation-state-changed',
14+
GET_INSTALLATION_STATE: 'get-installation-state',
15+
// End Installation state
1216
DOWNLOAD_PROGRESS: 'download-progress',
1317
START_DOWNLOAD: 'start-download',
1418
PAUSE_DOWNLOAD: 'pause-download',

src/install/install.ts

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { app, ipcMain } from 'electron';
2+
import log from 'electron-log/main';
3+
import { IPC_CHANNELS } from '../constants';
4+
import { createModelConfigFiles, getModelConfigPath } from '../config/extra_model_config';
5+
import fs from 'fs';
6+
import { AppWindow } from '../main-process/appWindow';
7+
import { ComfyConfigManager } from '../config/comfyConfigManager';
8+
9+
type InstallationState =
10+
| { status: 'NOT_INSTALLED' }
11+
| { status: 'ASKING_FOR_DIRECTORY'; defaultLocation: string; validationErrorMessage?: string }
12+
| {
13+
status: 'INSTALLING';
14+
location: string;
15+
}
16+
| {
17+
status: 'READY';
18+
location: string; // Must exist!
19+
}
20+
| {
21+
status: 'ERROR';
22+
error: Error; // Must exist!
23+
};
24+
25+
/**
26+
* This class is responsible for handling the installation of ComfyUI.
27+
* It registers an IPC handler for the current installation state.
28+
* It also sends any changes to the state to the renderer process.
29+
*/
30+
export class ComfyUIInstall {
31+
private static instance: ComfyUIInstall;
32+
private installLocation: string;
33+
private defaultInstallLocation: string;
34+
private state: InstallationState = { status: 'NOT_INSTALLED' };
35+
private appWindow: AppWindow;
36+
37+
private constructor(appWindow: AppWindow) {
38+
const defaultInstallLocation = app.getPath('documents');
39+
ipcMain.handle(IPC_CHANNELS.DEFAULT_INSTALL_LOCATION, () => defaultInstallLocation); // TODO: Remove after migration.
40+
ipcMain.handle(IPC_CHANNELS.GET_INSTALLATION_STATE, () => this.state);
41+
this.installLocation = defaultInstallLocation;
42+
this.defaultInstallLocation = defaultInstallLocation;
43+
this.appWindow = appWindow;
44+
}
45+
46+
static get(appWindow: AppWindow): ComfyUIInstall {
47+
if (!ComfyUIInstall.instance) {
48+
ComfyUIInstall.instance = new ComfyUIInstall(appWindow);
49+
}
50+
return ComfyUIInstall.instance;
51+
}
52+
53+
setState(state: InstallationState): void {
54+
log.info('Setting installation state:', state);
55+
this.state = state;
56+
this.appWindow.send(IPC_CHANNELS.INSTALLATION_STATE_CHANGED, state);
57+
}
58+
59+
public async install(): Promise<void> {
60+
const firstTimeSetup = await this.isFirstTimeSetup();
61+
log.info('First time setup:', firstTimeSetup);
62+
if (!firstTimeSetup) {
63+
this.appWindow.send(IPC_CHANNELS.FIRST_TIME_SETUP_COMPLETE, null); // TODO:Remove after migration.
64+
return;
65+
}
66+
this.setState({ status: 'ASKING_FOR_DIRECTORY', defaultLocation: this.defaultInstallLocation });
67+
this.appWindow.send(IPC_CHANNELS.SHOW_SELECT_DIRECTORY, null); // TODO:Remove after migration.
68+
while (this.state.status === 'ASKING_FOR_DIRECTORY') {
69+
const selectedDirectory = await this.selectInstallDirectory();
70+
const { valid, errorMessage } = await this.isValidComfyDirectory(selectedDirectory);
71+
if (!valid) {
72+
this.setState({ ...this.state, validationErrorMessage: errorMessage });
73+
return;
74+
} else {
75+
this.setState({ status: 'INSTALLING', location: selectedDirectory });
76+
this.installLocation = selectedDirectory;
77+
}
78+
}
79+
80+
const actualComfyDirectory = ComfyConfigManager.setUpComfyUI(this.installLocation);
81+
const modelConfigPath = getModelConfigPath();
82+
await createModelConfigFiles(modelConfigPath, actualComfyDirectory);
83+
84+
this.setState({ status: 'READY', location: this.installLocation });
85+
}
86+
87+
/**
88+
* Check if the user has completed the first time setup wizard.
89+
* This means the extra_models_config.yaml file exists in the user's data directory.
90+
*/
91+
private async isFirstTimeSetup(): Promise<boolean> {
92+
const extraModelsConfigPath = getModelConfigPath();
93+
return !fs.existsSync(extraModelsConfigPath);
94+
}
95+
96+
private async selectInstallDirectory(): Promise<string> {
97+
return new Promise((resolve, reject) => {
98+
ipcMain.on(IPC_CHANNELS.SELECTED_DIRECTORY, (_event, value: string) => {
99+
log.info('Directory selected:', value);
100+
resolve(value);
101+
});
102+
});
103+
}
104+
105+
async isValidComfyDirectory(selectedDirectory: string): Promise<{ valid: boolean; errorMessage?: string }> {
106+
//TODO: Implement
107+
return { valid: true };
108+
}
109+
}

src/main.ts

+5-40
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ import axios from 'axios';
44
import path from 'node:path';
55
import { SetupTray } from './tray';
66
import { IPC_CHANNELS, SENTRY_URL_ENDPOINT, ProgressStatus } from './constants';
7-
import { app, BrowserWindow, dialog, ipcMain, shell } from 'electron';
7+
import { app, dialog, ipcMain } from 'electron';
88
import log from 'electron-log/main';
99
import * as Sentry from '@sentry/electron/main';
1010
import * as net from 'net';
1111
import { graphics } from 'systeminformation';
12-
import { createModelConfigFiles, getModelConfigPath, readBasePathFromConfig } from './config/extra_model_config';
12+
import { getModelConfigPath } from './config/extra_model_config';
1313
import todesktop from '@todesktop/runtime';
1414
import { PythonEnvironment } from './pythonEnvironment';
1515
import { DownloadManager } from './models/DownloadManager';
1616
import { getModelsDirectory } from './utils';
1717
import { ComfySettings } from './config/comfySettings';
1818
import dotenv from 'dotenv';
1919
import { buildMenu } from './menu/menu';
20-
import { ComfyConfigManager } from './config/comfyConfigManager';
2120
import { AppWindow } from './main-process/appWindow';
2221
import { getAppResourcesPath, getBasePath, getPythonInstallPath } from './install/resourcePaths';
2322
import { PathHandlers } from './handlers/pathHandlers';
2423
import { AppInfoHandlers } from './handlers/appInfoHandlers';
24+
import { ComfyUIInstall } from './install/install';
2525

2626
dotenv.config();
2727

@@ -157,11 +157,8 @@ if (!gotTheLock) {
157157
});
158158
});
159159

160-
ipcMain.on(IPC_CHANNELS.OPEN_DEV_TOOLS, () => {
161-
appWindow.openDevTools();
162-
});
163-
164-
await handleFirstTimeSetup();
160+
const install = ComfyUIInstall.get(appWindow);
161+
install.install();
165162
const basePath = await getBasePath();
166163
const pythonInstallPath = await getPythonInstallPath();
167164
if (!basePath || !pythonInstallPath) {
@@ -549,38 +546,6 @@ function findAvailablePort(startPort: number, endPort: number): Promise<number>
549546
tryPort(startPort);
550547
});
551548
}
552-
/**
553-
* Check if the user has completed the first time setup wizard.
554-
* This means the extra_models_config.yaml file exists in the user's data directory.
555-
*/
556-
function isFirstTimeSetup(): boolean {
557-
const extraModelsConfigPath = getModelConfigPath();
558-
return !fs.existsSync(extraModelsConfigPath);
559-
}
560-
561-
async function selectedInstallDirectory(): Promise<string> {
562-
return new Promise((resolve, reject) => {
563-
ipcMain.on(IPC_CHANNELS.SELECTED_DIRECTORY, (_event, value) => {
564-
log.info('User selected to install ComfyUI in:', value);
565-
resolve(value);
566-
});
567-
});
568-
}
569-
570-
async function handleFirstTimeSetup() {
571-
const firstTimeSetup = isFirstTimeSetup();
572-
log.info('First time setup:', firstTimeSetup);
573-
if (firstTimeSetup) {
574-
appWindow.send(IPC_CHANNELS.SHOW_SELECT_DIRECTORY, null);
575-
const selectedDirectory = await selectedInstallDirectory();
576-
const actualComfyDirectory = ComfyConfigManager.setUpComfyUI(selectedDirectory);
577-
578-
const modelConfigPath = getModelConfigPath();
579-
await createModelConfigFiles(modelConfigPath, actualComfyDirectory);
580-
} else {
581-
appWindow.send(IPC_CHANNELS.FIRST_TIME_SETUP_COMPLETE, null);
582-
}
583-
}
584549

585550
/**
586551
* Rotate old log files by adding a timestamp to the end of the file.

0 commit comments

Comments
 (0)