docs: update README with new Rust frontend and fix TypeScript version

- Updated README.md to reflect the addition of a native Rust frontend using egui
- Added setup instructions for the new rust-frontend directory
- Modified frontend package.json to downgrade TypeScript version from 5.3.3 to 4.9.5 due to conflicts
- Updated path references in documentation from ComfyUI-Rust/ to direct paths
- Reorganized project structure documentation to distinguish between original and new frontends
This commit is contained in:
2026-03-03 00:44:51 +01:00
parent aefdcb38de
commit cd7a54f38b
10 changed files with 428 additions and 7 deletions

View File

@@ -4,7 +4,8 @@
This project implements an AI image/video generation tool inspired by the node-based workflow editor of Stable Diffusion Web UI. Built as a modern web application using: This project implements an AI image/video generation tool inspired by the node-based workflow editor of Stable Diffusion Web UI. Built as a modern web application using:
- **Backend**: Pure Rust (Actix-web framework) - **Backend**: Pure Rust (Actix-web framework)
- **Frontend**: React + TypeScript with Node graph visualization - **Frontend**: React + TypeScript with Node graph visualization (currently having TypeScript version conflict)
- **New Frontend Alternative**: Native Rust frontend using egui
- **GPU Acceleration**: ROCm integration for AMD RX 9070 XT GPU - **GPU Acceleration**: ROCm integration for AMD RX 9070 XT GPU
### Key Features: ### Key Features:
@@ -50,7 +51,7 @@ rocminfo # Should show your GPU info including "gfx900" or similar architectu
### Backend Setup (Rust): ### Backend Setup (Rust):
```bash ```bash
cd ComfyUI-Rust/backend cd backend
cargo build --release # For production builds, use release mode for better performance on AMD GPUs cargo build --release # For production builds, use release mode for better performance on AMD GPUs
# Run the backend server: # Run the backend server:
@@ -60,10 +61,10 @@ Backend runs at: http://localhost:[PORT]
API endpoints available after starting. API endpoints available after starting.
``` ```
### Frontend Setup (React): ### Frontend Setup (React - currently has TypeScript conflict):
```bash ```bash
cd ComfyUI-Rust/frontend cd frontend
npm install # Install dependencies npm install # Install dependencies
Run dev mode with hot reload and AMD GPU preview support: Run dev mode with hot reload and AMD GPU preview support:
@@ -73,6 +74,16 @@ yarn start
RUST_AMD_ROCM_PATH=/usr/local/AMDROCmlib yarn run prod-build && npm run serve RUST_AMD_ROCM_PATH=/usr/local/AMDROCmlib yarn run prod-build && npm run serve
``` ```
### New Rust Frontend Setup (egui):
```bash
cd rust-frontend
cargo run # Run the egui-based frontend
# Build for release:
cargo build --release
```
## Project Structure Overview: ## Project Structure Overview:
### Rust Backend (`backend/src`): ### Rust Backend (`backend/src`):
@@ -96,7 +107,7 @@ src/
``` ```
### Frontend Web App (`frontend/src`): ### Original Frontend Web App (`frontend/src`):
```typescript/react ```typescript/react
src/ src/
├─ components/node-editor.tsx // Node-based workflow editor (graph canvas) ├─ components/node-editor.tsx // Node-based workflow editor (graph canvas)
@@ -109,6 +120,17 @@ src/
``` ```
### New Rust Frontend (`rust-frontend/src`):
```rust
src/
├── main.rs # Main application entry point
├── node_editor.rs # Node-based workflow editor implementation
├── node_panel.rs # Panel for selecting and managing nodes
├── preview_pane.rs # Preview pane for image results
└── api_client.rs # Backend API communication layer
```
## ROCm Integration Notes: ## ROCm Integration Notes:
### Key Components: ### Key Components:
@@ -116,7 +138,7 @@ src/
- Parallelism configured based on AMD GPU thread count (RX9070XT) - Parallelism configured based on AMD GPU thread count (RX9070XT)
```rust ```rust
// Example configuration from rust/backend/src/config.rs // Example configuration from backend/src/config.rs
pub struct Config { pub struct Config {
pub gpu_backend_config = RocmConfig { // ROCm detection for RX900 series GPUs pub gpu_backend_config = RocmConfig { // ROCm detection for RX900 series GPUs

View File

@@ -20,7 +20,7 @@
"@types/node": "^20.11.0", "@types/node": "^20.11.0",
"@types/react": "^18.2.45", "@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"typescript": "^5.3.3" "typescript": "^4.9.5"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

18
rust-frontend/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "comfyui-rust-frontend"
version = "0.1.0"
edition = "2021"
[dependencies]
eframe = "0.24"
egui = "0.24"
egui_extras = "0.24"
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
image = "0.24"
thiserror = "1.0"
[dependencies.comfyui-backend]
path = "../backend"

59
rust-frontend/README.md Normal file
View File

@@ -0,0 +1,59 @@
# ComfyUI Rust Frontend
A native Rust frontend for ComfyUI-like AI image generation tool using egui.
## Features
- Node-based workflow editor
- Real-time preview pane
- Integration with backend API
- ROCm support through Rust bindings
## Architecture
This frontend is built with:
- `eframe` and `egui` for the UI framework
- `reqwest` for HTTP communication with the backend
- `serde` for JSON serialization/deserialization
## Project Structure
```
src/
├── main.rs # Main application entry point
├── node_editor.rs # Node-based workflow editor implementation
├── node_panel.rs # Panel for selecting and managing nodes
├── preview_pane.rs # Preview pane for image results
└── api_client.rs # Backend API communication layer
```
## Running the Application
1. Make sure you have Rust installed (`rustup` or `curl https://sh.rustup.org/rustup.sh -sSf | sh`)
2. Start the backend server:
```bash
cd ../backend && cargo run
```
3. In another terminal, run the frontend:
```bash
cd rust-frontend && cargo run
```
## Building
To build for release:
```bash
cd rust-frontend && cargo build --release
```
The executable will be located at `target/release/comfyui-rust-frontend`.
## Integration with Backend
This frontend communicates with the backend through HTTP API endpoints. The API client handles:
- Fetching available node types from `/nodes`
- Executing workflows via POST to `/execute`
- Getting execution results and preview images
The backend must be running on `http://localhost:8080` by default, but this can be configured in `src/main.rs`.

View File

@@ -0,0 +1,74 @@
use reqwest;
use serde::{Deserialize, Serialize};
use std::error::Error;
#[derive(Debug, Clone)]
pub struct ApiClient {
base_url: String,
client: reqwest::Client,
}
impl ApiClient {
pub fn new(base_url: String) -> Self {
Self {
base_url,
client: reqwest::Client::new(),
}
}
// Example API call - in a real implementation, this would be connected to your backend endpoints
pub async fn get_nodes(&self) -> Result<Vec<NodeInfo>, Box<dyn Error>> {
let response = self.client
.get(&format!("{}/nodes", self.base_url))
.send()
.await?;
let nodes: Vec<NodeInfo> = response.json().await?;
Ok(nodes)
}
pub async fn execute_workflow(&self, workflow: &Workflow) -> Result<ExecutionResult, Box<dyn Error>> {
let response = self.client
.post(&format!("{}/execute", self.base_url))
.json(workflow)
.send()
.await?;
let result: ExecutionResult = response.json().await?;
Ok(result)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct NodeInfo {
pub id: String,
pub name: String,
pub description: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Workflow {
pub nodes: Vec<WorkflowNode>,
pub connections: Vec<Connection>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WorkflowNode {
pub id: String,
pub node_type: String,
pub parameters: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Connection {
pub from_node: String,
pub from_output: String,
pub to_node: String,
pub to_input: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExecutionResult {
pub status: String,
pub output: Option<serde_json::Value>,
}

12
rust-frontend/src/lib.rs Normal file
View File

@@ -0,0 +1,12 @@
//! A Rust frontend for ComfyUI using egui
pub mod node_editor;
pub mod node_panel;
pub mod preview_pane;
pub mod api_client;
// Re-export key types for easier access
pub use node_editor::{NodeEditor, Node};
pub use node_panel::NodePanel;
pub use preview_pane::PreviewPane;
pub use api_client::{ApiClient, Workflow, ExecutionResult};

69
rust-frontend/src/main.rs Normal file
View File

@@ -0,0 +1,69 @@
use eframe::egui;
use std::sync::Arc;
mod node_editor;
mod node_panel;
mod preview_pane;
mod api_client;
use node_editor::NodeEditor;
use node_panel::NodePanel;
use preview_pane::PreviewPane;
use api_client::ApiClient;
struct ComfyUIApp {
node_editor: NodeEditor,
node_panel: NodePanel,
preview_pane: PreviewPane,
api_client: ApiClient,
}
impl ComfyUIApp {
fn new(_cc: &eframe::CreationContext) -> Self {
Self {
node_editor: NodeEditor::new(),
node_panel: NodePanel::new(),
preview_pane: PreviewPane::new(),
api_client: ApiClient::new("http://localhost:8080".to_string()),
}
}
}
impl eframe::App for ComfyUIApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
// Main layout with three panes
ui.horizontal(|ui| {
// Left panel - Node selection
ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
self.node_panel.ui(ui);
});
// Center panel - Node editor
ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
self.node_editor.ui(ui, &self.api_client);
});
// Right panel - Preview pane
ui.with_layout(egui::Layout::top_down_justified(egui::Align::RIGHT), |ui| {
self.preview_pane.ui(ui, &self.api_client);
});
});
});
}
}
fn main() -> eframe::Result {
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title("ComfyUI Rust Frontend")
.with_inner_size([1200.0, 800.0]),
..Default::default()
};
eframe::run_native(
"ComfyUI Rust Frontend",
native_options,
Box::new(|cc| Box::new(ComfyUIApp::new(cc))),
)
}

View File

@@ -0,0 +1,67 @@
use eframe::egui;
use crate::api_client::ApiClient;
pub struct NodeEditor {
nodes: Vec<Node>,
}
#[derive(Debug, Clone)]
pub struct Node {
pub id: String,
pub name: String,
pub x: f32,
pub y: f32,
pub inputs: Vec<Input>,
pub outputs: Vec<Output>,
}
#[derive(Debug, Clone)]
pub struct Input {
pub id: String,
pub name: String,
pub node_id: String,
}
#[derive(Debug, Clone)]
pub struct Output {
pub id: String,
pub name: String,
pub node_id: String,
}
impl NodeEditor {
pub fn new() -> Self {
Self {
nodes: vec![],
}
}
pub fn ui(&mut self, ui: &mut egui::Ui, api_client: &ApiClient) {
ui.heading("Node Editor");
// Create a scroll area for the node editor
egui::ScrollArea::vertical().show(ui, |ui| {
// Placeholder for actual node rendering logic
ui.label("This is where the node-based workflow would be rendered.");
ui.label("Nodes can be dragged and connected here.");
// Simple example of a node
if ui.button("Add Node").clicked() {
let new_node = Node {
id: format!("node_{}", self.nodes.len()),
name: "New Node".to_string(),
x: 100.0 + (self.nodes.len() as f32 * 50.0),
y: 100.0,
inputs: vec![],
outputs: vec![],
};
self.nodes.push(new_node);
}
// Display existing nodes
for node in &self.nodes {
ui.label(format!("Node: {} at ({}, {})", node.name, node.x, node.y));
}
});
}
}

View File

@@ -0,0 +1,47 @@
use eframe::egui;
pub struct NodePanel {
selected_node_type: Option<String>,
}
impl NodePanel {
pub fn new() -> Self {
Self {
selected_node_type: None,
}
}
pub fn ui(&mut self, ui: &mut egui::Ui) {
ui.set_min_width(200.0);
ui.heading("Node Panel");
ui.separator();
// Available node types
ui.label("Available Nodes:");
let node_types = vec![
"Image Loader",
"Text Input",
"Image Generator",
"Image Save",
"Conditional Node",
"Loop Node"
];
for node_type in node_types {
if ui.button(node_type).clicked() {
self.selected_node_type = Some(node_type.to_string());
}
}
ui.separator();
// Selected node info
if let Some(selected) = &self.selected_node_type {
ui.label(format!("Selected: {}", selected));
} else {
ui.label("No node selected");
}
}
}

View File

@@ -0,0 +1,53 @@
use eframe::egui;
use crate::api_client::ApiClient;
pub struct PreviewPane {
image_data: Option<Vec<u8>>,
image_name: String,
}
impl PreviewPane {
pub fn new() -> Self {
Self {
image_data: None,
image_name: "No image".to_string(),
}
}
pub fn ui(&mut self, ui: &mut egui::Ui, api_client: &ApiClient) {
ui.set_min_width(300.0);
ui.heading("Preview Pane");
ui.separator();
// Display current preview info
ui.label(format!("Image: {}", self.image_name));
if let Some(data) = &self.image_data {
// Try to display the image (simplified)
ui.label("Image would be displayed here");
// Placeholder for actual image display logic
ui.horizontal(|ui| {
if ui.button("Load Sample Image").clicked() {
self.image_name = "sample_output.png".to_string();
// In a real implementation, this would load actual image data
self.image_data = Some(vec![0; 1024]);
}
});
} else {
ui.label("No preview available");
if ui.button("Generate Preview").clicked() {
self.image_name = "generated_output.png".to_string();
// In a real implementation, this would fetch actual image data from backend
self.image_data = Some(vec![0; 1024]);
}
}
ui.separator();
// Status info
ui.label("Status: Ready");
}
}