Skip to content

Commit

Permalink
added queue indicator
Browse files Browse the repository at this point in the history
added update notification
  • Loading branch information
diStyApps committed Nov 11, 2024
1 parent 5f18e89 commit 0de6a8b
Show file tree
Hide file tree
Showing 13 changed files with 801 additions and 287 deletions.
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {}
APP_CONFIGS: List[AppConfig] = []
APP_NAME: str = "Flow"
APP_VERSION: str = "0.2.1"
APP_VERSION: str = "0.2.2"
PURPLE = "\033[38;5;129m"
RESET = "\033[0m"
FLOWMSG = f"{PURPLE}Flow{RESET}"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "comfyui-disty-flow"
description = "Flow is a custom node designed to provide a more user-friendly interface for ComfyUI by acting as an alternative user interface for running workflows. It is not a replacement for workflow creation.\nFlow is currently in the early stages of development, so expect bugs and ongoing feature enhancements. With your support and feedback, Flow will settle into a steady stream."
version = "0.2.1"
version = "0.2.2"
license = {file = "LICENSE"}

[project.urls]
Expand Down
11 changes: 9 additions & 2 deletions web/core/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -595,14 +595,21 @@ input[type='number']::-webkit-outer-spin-button {
}

#loading-area {
display: none;
background-color: var(--color-background-secondary);
z-index: 100;
display: flex;
justify-content: center;
justify-content: space-evenly;
align-items: center;
}

#queue-display{
overflow-y: auto;
padding: 10px;
background-color: var(--color-background-secondary);
margin-left: 10px;
user-select: none;
}

footer {
background-color: var(--color-background-secondary);
color: var(--color-primary-text);
Expand Down
70 changes: 70 additions & 0 deletions web/core/js/common/components/LoraWorkflowManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

import WorkflowNodeAdder from './WorkflowNodeAdder.js';
import DropdownStepper from './DropdownStepper.js';

class LoraWorkflowManager {
constructor(workflow, flowConfig) {
this.workflowManager = new WorkflowNodeAdder(workflow);
this.flowConfig = flowConfig;
this.container = document.getElementById('side-workflow-controls');
this.addButton = null;
this.initializeUI();
}

initializeUI() {
this.addButton = document.createElement('button');
this.addButton.textContent = '+LoRA';
this.addButton.classList.add('add-lora-button');
this.addButton.style.marginBottom = '5px';
this.container.appendChild(this.addButton);
this.addButton.addEventListener('click', () => this.handleAddLora());
}

handleAddLora() {
try {
const newNodeId = this.workflowManager.addLora();

const dynamicConfig = this.createDynamicConfig(newNodeId);

const loraContainer = document.createElement('div');
loraContainer.id = dynamicConfig.id;
loraContainer.classList.add('dropdown-stepper-container');
this.container.appendChild(loraContainer);
new DropdownStepper(dynamicConfig, this.workflowManager.getWorkflow());

} catch (error) {
console.error('Error adding LoRA:', error);
}
}

createDynamicConfig(nodeId) {
return {
id: `LoraLoader_${nodeId}`,
label: "LoRA",
dropdown: {
url: "LoraLoaderModelOnly",
key: "lora_name",
nodePath: `${nodeId}.inputs.lora_name`
},
steppers: [
{
id: `lorastrength_${nodeId}`,
label: "Strength",
minValue: 0,
maxValue: 5,
step: 0.01,
defValue: 1,
precision: 2,
scaleFactor: 1,
container: `lorastrength_container_${nodeId}`,
nodePath: `${nodeId}.inputs.strength_model`
}
]
};
}

getWorkflow() {
return this.workflowManager.getWorkflow();
}
}
export default LoraWorkflowManager;
93 changes: 93 additions & 0 deletions web/core/js/common/components/WorkflowNodeAdder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
class WorkflowNodeAdder {
constructor(workflow) {
if (typeof workflow !== 'object' || workflow === null || Array.isArray(workflow)) {
throw new TypeError('Workflow must be a non-null object');
}
this.workflow = { ...workflow };
this.existingIds = new Set(Object.keys(this.workflow).map(id => parseInt(id, 10)));
this.highestId = this._getHighestNodeId();
this.loraCount = this._countExistingLoras();
}

addLora() {
const newLoraId = this._getNextNodeId();
const loraNode = this._createLoraNode(newLoraId);

const existingLoras = this._findLoraNodes();

if (existingLoras.length === 0) {
const modelLoaders = this._findModelLoaders();
if (modelLoaders.length === 0) {
throw new Error('No model loader found in the workflow to attach LoRA');
}

modelLoaders.forEach(loader => {
const originalModelInput = loader.inputs.model;
loader.inputs.model = [newLoraId.toString(), 0];
loraNode.inputs.model = originalModelInput;
});
} else {
const lastLora = existingLoras[existingLoras.length - 1];
const originalModelInput = lastLora.inputs.model;
lastLora.inputs.model = [newLoraId.toString(), 0];
loraNode.inputs.model = originalModelInput;
}

this.workflow[newLoraId.toString()] = loraNode;
this.existingIds.add(newLoraId);
this.highestId = newLoraId;
this.loraCount += 1;

return newLoraId;
}

getWorkflow() {
return this.workflow;
}

_createLoraNode(id) {
return {
inputs: {
lora_name: "lora.safetensors",
strength_model: 1,
model: []
},
class_type: "LoraLoaderModelOnly",
_meta: {
title: "LoraLoaderModelOnly"
}
};
}

_findLoraNodes() {
return Object.entries(this.workflow)
.filter(([_, node]) => node.class_type === "LoraLoaderModelOnly")
.map(([id, node]) => ({ id: parseInt(id, 10), ...node }));
}

_findModelLoaders() {
const modelLoaders = [];

Object.entries(this.workflow).forEach(([id, node]) => {
if (node.inputs && Array.isArray(node.inputs.model) && node.inputs.model.length === 2) {
modelLoaders.push({ id: parseInt(id, 10), ...node });
}
});

return modelLoaders;
}

_getNextNodeId() {
return this.highestId + 1;
}

_getHighestNodeId() {
return Math.max(...this.existingIds, 0);
}

_countExistingLoras() {
return this._findLoraNodes().length;
}
}

export default WorkflowNodeAdder;
21 changes: 10 additions & 11 deletions web/core/js/common/components/messageHandler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import { WebSocketHandler } from './webSocketHandler.js';
import { updateProgress, displayImagesInDiv } from './imagedisplay.js';
import { hideSpinner } from './utils.js';
Expand Down Expand Up @@ -35,6 +34,9 @@ class JSONMessageProcessor extends IMessageProcessor {
this.messageHandler.handleStatus();
break;
case 'executing':
case 'execution_start':
case 'execution_cached':
case 'execution_success':
break;
case 'execution_error':
hideSpinner();
Expand Down Expand Up @@ -85,11 +87,7 @@ class BlobMessageProcessor extends IMessageProcessor {

async process(blob) {
try {
// console.log('Processing Blob message:', blob);

if (!blob.type) {
// console.warn('Blob type is empty. Attempting to detect MIME type.');

const headerSize = 8;
if (blob.size <= headerSize) {
console.error('Blob size is too small to contain valid image data.');
Expand All @@ -98,11 +96,8 @@ class BlobMessageProcessor extends IMessageProcessor {
}

const slicedBlob = blob.slice(headerSize);
// console.log('Sliced Blob size:', slicedBlob.size);

const detectedType = await detectMimeType(slicedBlob);
if (detectedType) {
// console.log('Detected MIME type:', detectedType);
this.messageHandler.handleMedia(URL.createObjectURL(slicedBlob), detectedType, false);
} else {
console.error('Could not detect MIME type of Blob.');
Expand All @@ -113,7 +108,6 @@ class BlobMessageProcessor extends IMessageProcessor {

if (blob.type.startsWith('image/') || blob.type.startsWith('video/')) {
const objectURL = URL.createObjectURL(blob);
// console.log('Created Object URL:', objectURL);
this.messageHandler.handleMedia(objectURL, blob.type, true);
} else {
console.error('Unsupported Blob type:', blob.type);
Expand Down Expand Up @@ -171,6 +165,10 @@ export class MessageHandler {
}
}
hideSpinner();

// console.log('Execution completed:', data);
const event = new CustomEvent('jobCompleted');
window.dispatchEvent(event);
}

processImages(images) {
Expand Down Expand Up @@ -211,15 +209,16 @@ export class MessageHandler {
}

handleMedia(mediaUrl, mediaType, addToHistory = true) {
// console.log('Handling media:', mediaUrl, 'Type:', mediaType, 'Add to history:', addToHistory);
displayImagesInDiv([mediaUrl], addToHistory);
}

handleInterrupted() {
hideSpinner();
console.log('Execution Interrupted');
}

const event = new CustomEvent('jobInterrupted');
window.dispatchEvent(event);
}

handleStatus() {
updateProgress();
Expand Down
2 changes: 1 addition & 1 deletion web/core/js/common/components/progressbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const progressBarHTML = `
<div id="loading-area">
<div id="spinner"></div>
<progress id="main-progress" class="progress" value="0" max="100"></progress>
<progress id="main-progress" class="progress" value="0" max="10000"></progress>
<div id="queue-display"></div>
<div id="buttonsgen">
<button id="generateButton" class="menuButtons">GENERATE</button>
Expand Down
7 changes: 7 additions & 0 deletions web/core/js/common/scripts/state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const state = {
jobQueue: [],
currentJobId: 0,
isProcessing: false,
};

export default state;
38 changes: 38 additions & 0 deletions web/core/js/common/scripts/stateManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import state from './state.js';

class StateManager {
static addJob(job) {
state.jobQueue.push(job);
}

static getNextJob() {
return state.jobQueue[0];
}

static removeJob() {
state.jobQueue.shift();
}

static incrementJobId() {
state.currentJobId += 1;
return state.currentJobId;
}

static setProcessing(value) {
state.isProcessing = value;
}

static isProcessing() {
return state.isProcessing;
}

static getJobQueue() {
return state.jobQueue;
}

static getCurrentJobId() {
return state.currentJobId;
}
}

export default StateManager;
Loading

0 comments on commit 0de6a8b

Please sign in to comment.