使用 Electron 框架的客户端。用 electron-vite 构建。
技术栈:
- React
- TypeScript
- Electron
- electron-vite
- electron-builder
$ npm ci这样会严格按 package-lock.json 安装,保证复现性、不会随意改锁文件,特别适合 CI/新机器
$ npm installWindows 系统等待太久可以提前先输入以下指令再下载
$ npm config set registry http://registry.npmjs.org/$ npm run devnpm run lint
npm run typecheck# For windows
$ npm run build:win
# For macOS
$ npm run build:mac
# For Linux
$ npm run build:linux./src/
├── main/
│ ├── api/
│ ├── comfyCli/
│ └── index.ts
├── preload/
│ ├── apiIpc.ts
│ ├── index.d.ts
│ └── index.ts
├── renderer/
│ ├── index.html
│ └── src/
└── types/
├── api/
└── utils/
- 其中 main 、 preload 、 renderer 对应 electron 的三种进程环境。见 https://www.electronjs.org/zh/docs/latest/tutorial/process-model
- common 对应 main 进程与 renderer 进程都会用到的逻辑。
- types 对应 main 进程与 renderer 进程都会用到的类型定义。
约定: api 指 main 进程与 renderer 进程交互用的逻辑。 main 进程作为 server 端实现逻辑,renderer 端作为 client 端进行调用。
- main/api 对应 main 进程的 api 实现逻辑。 main/api/serverIpc.ts 内负责处理如何通信。
- preload/apiIpc.ts 将 main/api 的 api 暴露给 renderer 进程。
- Ipc 指 main 进程与 renderer 进程间 api 调用的通信方法为 electron 内置 IPC 。
详细完整版:
AIEngineElectron/
└─ src/
├─ main/ # Electron 主进程(进程管理、IPC、后端服务封装)
│ ├─ api/
│ │ ├─ serverIpc.ts # 统一注册 IPC:Unary + Server-Streaming;绑定 svcState/svcComfy/svcDialog
│ │ ├─ svcComfyImpl.ts # ComfySvc 实现:排队提交、查询历史、WebSocket 事件透传、便利封装
│ │ ├─ svcDialogImpl.ts # Electron 原生对话框封装(Open/Save/Message)
│ │ └─ svcStateImpl.ts # 状态&环境:配置读写、ComfyUI 启停与连接、端口探测、列文件、命令运行、GPU/环境检测
│ ├─ comfy/
│ │ ├─ error.ts # Comfy 侧 HTTP 错误类型与判定(ComfyPostError)
│ │ ├─ fs.ts # Comfy 文件系统访问(模型、权重等路径扫描)
│ │ ├─ http.ts # ComfyUI HTTP 客户端封装:/prompt /history /upload /view /ws
│ │ ├─ logic.ts # 轮询等待历史/图片的逻辑(超时/轮询间隔)
│ │ └─ state.ts # 主进程 WS 长连管理与自动重连;事件中心广播(EventCenter<ComfyEvent>)
│ ├─ config/
│ │ ├─ buildEnv.ts # 构建/平台/嵌入路径映射(dev/prod;win/mac);嵌入式默认配置
│ │ └─ config.ts # 用户配置持久化(config.json);内存缓存 + 变更广播(EventCenter<Config>)
│ ├─ queue/
│ │ ├─ convert.ts # Comfy WS 事件中的 prompt_id ↔ 内部 task.id 转换;队列项映射
│ │ ├─ taskError.ts # 结果错误类型判定(ComfyHistory 的 error 变体)
│ │ └─ taskQueue.ts # 内存任务队列:提交 → 等待结果 → 完成/错误;和 ComfyHttpCli 对接
│ ├─ subprocess/
│ │ └─ subprocess.ts # 子进程管理:spawn/连接/日志/优雅关闭/强杀/清理全部
│ ├─ utils/
│ │ ├─ eventCenter.ts # 轻量事件中心(onEvent/onEnd;广播/清理)
│ │ ├─ fileUtils.ts # fs.exists 的 Promise 包装
│ │ └─ queueManager.ts # 通用拉取执行队列(setInterval 轮询 + execute 回调)
│ ├─ index.ts # Electron 启动入口:建窗、热重载、拦截外链、quit 信号、窗口生命周期
│ └─ lifeCycle.ts # 应用生命周期:beforeShow(initConfig/initQueue/initIPC);beforeQuit(清理子进程/队列)
│
├─ preload/ # (未贴出)给渲染进程暴露安全 API(window.api 等)
├─ renderer/ # React 界面(MUI 主题、路由、页面、Hooks)
│ └─ src/
│ ├─ assets/ # 静态资源(图片/字体/CSS)
│ ├─ components/
│ │ ├─ Layout.tsx # 顶层布局:左侧 Sidebar + 右侧内容(背景随主题)
│ │ ├─ MaxSizeLayout.tsx # Modal 子树尺寸监听(传回 onResize)
│ │ ├─ ModalLayout.tsx # 通用 Modal 容器(支持无按钮直开)
│ │ ├─ ProgressChip.tsx # 进度条样式 Chip(背景宽度按 progress 变化)
│ │ ├─ PureConfigNotSetCallout.tsx
│ │ │ # 配置未完成提醒(ComfyUI 路径/Python 路径未设 → 引导跳设置页)
│ │ ├─ Sidebar.tsx # 侧栏导航(根据配置动态禁用部分菜单;品牌区/阴影/选中态)
│ │ ├─ ThemeRoot.tsx # 注入 MUI 主题(getTheme + CssBaseline)
│ │ └─ Versions.tsx # 底部版本信息(静态 chip)
│ ├─ hooks/
│ │ ├─ ThemeModeProvider.tsx # 主题模式状态(localStorage 持久化+ <html data-theme>)
│ │ ├─ useComfyEvent.ts # 渲染进程端订阅 ComfyEvent(通过 svcComfy.connectWs → 回调注册/注销)
│ │ ├─ useConfig.ts # 配置上下文(svcState.watchConfig → Context)
│ │ ├─ useMessage.ts # 全局消息(notistack 包装)
│ │ └─ useThemeMode.ts # 主题上下文 Hook(取/切换 mode)
│ ├─ pages/
│ │ ├─ FileBrowserPage/
│ │ │ └─ ModelPage.tsx # (模型/文件浏览页,依赖 Comfy 目录)
│ │ ├─ MainPage/
│ │ │ └─ MainPage.tsx # 首页
│ │ ├─ QuickAppPage/
│ │ │ └─ QuickAppPage.tsx # 快应用(工作流快捷入口)
│ │ ├─ SettingsPage/
│ │ │ └─ SettingsPage.tsx # 设置(含环境/路径等)
│ │ ├─ ContactPage.tsx # 联系我们(后续可扩为文件夹)
│ │ └─ TerminalPage.tsx # 终端(依赖 Comfy/Python 已就绪)
│ ├─ utils/
│ │ └─ windowUtils.ts # 统一从 window.api 获取带类型的 Api
│ ├─ App.tsx # 路由定义注入 Layout,渲染 routes
│ ├─ env.d.ts # Vite 类型声明
│ ├─ main.tsx # 前端入口:主题/消息/配置/事件/Redux 等 Provider 装配
│ ├─ routes.ts # 路由表(侧栏显示/禁用规则/图标)
│ └─ theme.ts # MUI 主题生成(明暗、色板、排版、组件风格)
│
└─ shared/ # (未贴出)主/渲染共享类型与常量(Api 接口、comfy types、config 类型等)
有 pure 和 embedded 两种模式,由打包参数唯一决定,一旦构建用户不可修改。
- 打包时决定是否将内置 python 与 ComfyUI 打包到 app 中。
- 在程序中, Mode 只决定当用户不填入 python_cmd 与 comfyui_dir 时,程序的行为。
- embedded:使用内置 python 与 ComfyUI
- pure:阻止任何关联 ComfyUI 、调用 python 的行为(检查环境、打开 Comfy 目录、启动 ComfyUI 等)
Mode 有两处地方设置:
- 程序内,通过 electron-vite build 时传入
--mode参数,程序内读取import.meta.env.VITE_BUILD_MODE获取。 - 打包时,通过 cross-env 设置
PACKAGE_MODE环境变量,electron-builder.config.ts读取process.env.PACKAGE_MODE获取。
当 Mode 为 embedded 时,程序会使用内置的 python 与 ComfyUI。在打包或调试 embedded mode 之前,需要先准备 embedded 目录下的文件。
从 ComfyUI GitHub 仓库的 Release 中下载 ComfyUI_windows_portable_nvidia ,并解压到 embedded 目录下,得到 ./embedded/ComfyUI 与 ./embedded/python_embeded 目录。
./embedded
├── ComfyUI
├── python_embedded_macos
└── python_embeded
要打包 MacOS 版本的 embedded ,需要先执行完前一步,下载并解压 ComfyUI 。
然后到 Python 官网,https://www.python.org/downloads/macos/ , 下载 python-3.12.7-macos11.pkg ,将其重命名为 python-3.12.7-macos11.pkg 并放置在 ./embedded/python_embedded_macos 目录中。
执行 ./embedded/python_embedded_macos/setup_macos_python.sh 脚本,将 python 嵌入到 ./embedded/python_embedded_macos 目录中。