Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cb2af3dc3 | ||
|
|
9d999f92a9 | ||
|
|
01e8668d1a | ||
|
|
8e58d962a7 | ||
|
|
c59c308d32 | ||
|
|
4560f2cca9 | ||
|
|
872752b820 | ||
|
|
97bb2b69c5 | ||
|
|
08fe87c8af | ||
|
|
d43e5fcefc | ||
|
|
65492a108c |
+25
-4
@@ -361,13 +361,30 @@ async def send_json_override(self, event, data, sid=None):
|
||||
if event == 'executing' and data.get('node') is not None:
|
||||
node = data.get('node')
|
||||
|
||||
if 'prompt_id' in prompt_metadata:
|
||||
if prompt_id in prompt_metadata:
|
||||
if 'progress' not in prompt_metadata[prompt_id]:
|
||||
prompt_metadata[prompt_id]["progress"] = set()
|
||||
|
||||
prompt_metadata[prompt_id]["progress"].add(node)
|
||||
calculated_progress = len(prompt_metadata[prompt_id]["progress"]) / len(prompt_metadata[prompt_id]['workflow_api'])
|
||||
# print("calculated_progress", calculated_progress)
|
||||
|
||||
if 'last_updated_node' in prompt_metadata[prompt_id] and prompt_metadata[prompt_id]['last_updated_node'] == node:
|
||||
return
|
||||
prompt_metadata[prompt_id]['last_updated_node'] = node
|
||||
class_type = prompt_metadata[prompt_id]['workflow_api'][node]['class_type']
|
||||
print("updating run live status", class_type)
|
||||
await update_run_live_status(prompt_id, "Executing " + class_type)
|
||||
await update_run_live_status(prompt_id, "Executing " + class_type, calculated_progress)
|
||||
|
||||
if event == 'execution_cached' and data.get('nodes') is not None:
|
||||
if prompt_id in prompt_metadata:
|
||||
if 'progress' not in prompt_metadata[prompt_id]:
|
||||
prompt_metadata[prompt_id]["progress"] = set()
|
||||
|
||||
if 'nodes' in data:
|
||||
for node in data.get('nodes', []):
|
||||
prompt_metadata[prompt_id]["progress"].add(node)
|
||||
# prompt_metadata[prompt_id]["progress"].update(data.get('nodes'))
|
||||
|
||||
if event == 'execution_error':
|
||||
# Careful this might not be fully awaited.
|
||||
@@ -391,18 +408,22 @@ class Status(Enum):
|
||||
# Global variable to keep track of the last read line number
|
||||
last_read_line_number = 0
|
||||
|
||||
async def update_run_live_status(prompt_id, live_status):
|
||||
async def update_run_live_status(prompt_id, live_status, calculated_progress: float):
|
||||
if prompt_id not in prompt_metadata:
|
||||
return
|
||||
|
||||
print("progress", calculated_progress)
|
||||
|
||||
status_endpoint = prompt_metadata[prompt_id]['status_endpoint']
|
||||
body = {
|
||||
"run_id": prompt_id,
|
||||
"live_status": live_status,
|
||||
"progress": calculated_progress
|
||||
}
|
||||
# requests.post(status_endpoint, json=body)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
await session.post(status_endpoint, json=body)
|
||||
async with session.post(status_endpoint, json=body) as response:
|
||||
pass
|
||||
|
||||
|
||||
def update_run(prompt_id, status: Status):
|
||||
|
||||
+8
-4
@@ -1,7 +1,7 @@
|
||||
import { app } from "./app.js";
|
||||
import { api } from "./api.js";
|
||||
import { ComfyWidgets, LGraphNode } from "./widgets.js";
|
||||
import { generateDependencyGraph } from "https://esm.sh/[email protected]4";
|
||||
import { generateDependencyGraph } from "https://esm.sh/[email protected]9";
|
||||
|
||||
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
|
||||
/** @type {ComfyExtension} */
|
||||
@@ -182,7 +182,7 @@ function showError(title, message) {
|
||||
function createDynamicUIHtml(data) {
|
||||
console.log(data);
|
||||
let html =
|
||||
'<div style="max-width: 1024px; margin: 14px auto; display: flex; flex-direction: column; gap: 24px;">';
|
||||
'<div style="min-width: 600px; max-width: 1024px; margin: 14px auto; display: flex; flex-direction: column; gap: 24px;">';
|
||||
const bgcolor = "var(--comfy-input-bg)";
|
||||
const evenBg = "var(--border-color)";
|
||||
const textColor = "var(--input-text)";
|
||||
@@ -284,8 +284,12 @@ function addButton() {
|
||||
}
|
||||
|
||||
const ok = await confirmDialog.confirm(
|
||||
"Confirm deployment -> " + displayName,
|
||||
`A new version will be deployed, are you conform? <br><br><input id="include-deps" type="checkbox" checked>Include dependence</input>`,
|
||||
"Confirm deployment -> " +
|
||||
displayName +
|
||||
"<br><br><small><div style='font-weight: normal;'>" +
|
||||
endpoint +
|
||||
"<div><br>",
|
||||
`A new version will be deployed, are you confirm? <br><br><input id="include-deps" type="checkbox" checked>Include dependence</input>`,
|
||||
);
|
||||
if (!ok) return;
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
@@ -48,12 +48,39 @@ const exampleWorkflows: exampleWorkflow[] = [
|
||||
},
|
||||
{
|
||||
title: "Upscale and Add Detail SDXL",
|
||||
description: "Upscale and Add Details to your creations",
|
||||
description: "Upscale and Add Details to your creations.",
|
||||
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-upscale-and-add-detail-sdxl',
|
||||
image: {
|
||||
src: '/example-workflows/upscale.webp',
|
||||
alt: 'Upscale and Add Detail SDXL',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Txt2Img SDXL Turbo",
|
||||
description: "Try SDXL turbo and generate images since 1 step in seconds.",
|
||||
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-txt2img-sdxl-turbo',
|
||||
image: {
|
||||
src: '/example-workflows/txt2img-sdxl-turbo.webp',
|
||||
alt: 'Txt2Img SDXL Turbo',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Img2Img SDXL Controlnet",
|
||||
description: "This workflow uses canny. Generate lines of you original image and create variations.",
|
||||
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-img2-img-sdxl-controlnet',
|
||||
image: {
|
||||
src: '/example-workflows/txt2img-controlnet.webp',
|
||||
alt: 'Img2Img SDXL Controlnet',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Automatic Inpainting (SEG)",
|
||||
description: "Type what do you want to select and change that area with your prompt.",
|
||||
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-automatic-inpainting-clip-seg',
|
||||
image: {
|
||||
src: '/example-workflows/automatic-inpainting-seg.webp',
|
||||
alt: 'Img2Img SDXL Controlnet',
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -67,7 +94,7 @@ async function Examples() {
|
||||
</h1>
|
||||
<p className="max-w-[560px] text-center text-lg text-muted-foreground">Text to Image, Image to Image, IPAdapter, and more. Here are some examples that you can use to deploy your workflow.</p>
|
||||
</section>
|
||||
<section className="flex justify-center flex-wrap gap-4">
|
||||
<section className="flex justify-center flex-wrap gap-5">
|
||||
{exampleWorkflows.map(workflow => {
|
||||
return <Card className="w-[350px]">
|
||||
<CardHeader>
|
||||
|
||||
@@ -80,7 +80,9 @@
|
||||
/* @apply rounded-lg p-2 overflow-x-scroll */
|
||||
@apply p-2 max-w-full overflow-auto w-full
|
||||
}
|
||||
|
||||
.vsc-controller{
|
||||
position: absolute;
|
||||
}
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
|
||||
@@ -60,6 +60,7 @@ import { callServerPromise } from "./callServerPromise";
|
||||
import fetcher from "./fetcher";
|
||||
import { ButtonAction } from "@/components/ButtonActionLoader";
|
||||
import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
|
||||
import { VisualizeImagesGrid } from "@/components/VisualizeImagesGrid";
|
||||
|
||||
export function VersionSelect({
|
||||
workflow,
|
||||
@@ -169,19 +170,21 @@ export function useSelectedMachine(
|
||||
}
|
||||
|
||||
type PublicRunStore = {
|
||||
image: string;
|
||||
image: {
|
||||
url: string;
|
||||
}[] | null;
|
||||
loading: boolean;
|
||||
runId: string;
|
||||
status: string;
|
||||
|
||||
setImage: (image: string) => void;
|
||||
setImage: (image: { url: string; }[]) => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
setRunId: (runId: string) => void;
|
||||
setStatus: (status: string) => void;
|
||||
};
|
||||
|
||||
export const publicRunStore = create<PublicRunStore>((set) => ({
|
||||
image: "",
|
||||
image: null,
|
||||
loading: false,
|
||||
runId: "",
|
||||
status: "",
|
||||
@@ -205,7 +208,10 @@ export function PublicRunOutputs(props: {
|
||||
console.log(res?.status);
|
||||
if (res) setStatus(res.status);
|
||||
if (res && res.status === "success") {
|
||||
setImage(res.outputs[0]?.data.images[0].url);
|
||||
const imageURLs = res.outputs[0]?.data.images.map((item: { url: string; }) => {
|
||||
return { url: item.url };
|
||||
});
|
||||
setImage(imageURLs);
|
||||
setLoading(false);
|
||||
clearInterval(interval);
|
||||
}
|
||||
@@ -214,30 +220,25 @@ export function PublicRunOutputs(props: {
|
||||
return () => clearInterval(interval);
|
||||
}, [runId]);
|
||||
|
||||
return (
|
||||
<div className="border border-gray-200 w-full square h-[400px] rounded-lg relative">
|
||||
{!loading && !image && props.preview && props.preview.length > 0 && (
|
||||
<>
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={props.preview[0]?.url}
|
||||
alt="Generated image"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!loading && image && (
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={image}
|
||||
alt="Generated image"
|
||||
/>
|
||||
)}
|
||||
{loading && (
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="border border-gray-200 w-full h-[400px] square rounded-lg relative p-4 ">
|
||||
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
|
||||
{status} <LoadingIcon />
|
||||
</div>
|
||||
<Skeleton className="w-full h-full" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="border border-gray-200 w-full min-h-[400px] square rounded-lg relative p-4 ">
|
||||
{!image && props.preview && props.preview.length > 0 &&
|
||||
<VisualizeImagesGrid images={props.preview} />
|
||||
}
|
||||
{image && (
|
||||
<VisualizeImagesGrid images={image} />
|
||||
)}
|
||||
{loading && <Skeleton className="w-full h-full" />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -328,7 +329,7 @@ export function RunWorkflowButton({
|
||||
className="px-1"
|
||||
>
|
||||
<div className="flex justify-end">
|
||||
<AutoFormSubmit>
|
||||
<AutoFormSubmit disabled={isLoading}>
|
||||
Run
|
||||
{isLoading ? <LoadingIcon /> : <Play size={14} />}
|
||||
</AutoFormSubmit>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
type imagesType = {
|
||||
url: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
type VisualizeImagesGridProps = {
|
||||
images: imagesType[];
|
||||
layout?: 'justify-between' | 'justify-center' | 'justify-start' | 'justify-end';
|
||||
};
|
||||
export function VisualizeImagesGrid({ images, layout }: VisualizeImagesGridProps) {
|
||||
|
||||
return (
|
||||
<div className={`flex gap-4 flex-wrap ${layout || 'justify-center'}`}>
|
||||
<>
|
||||
{images && images.length > 0 &&
|
||||
images.map(item => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (item?.url.endsWith(".mp4") || item?.url.endsWith(".webm")) {
|
||||
return (
|
||||
<video key={item?.url} controls autoPlay className="rounded-xl" style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}>
|
||||
<source src={item?.url} type="video/mp4" />
|
||||
<source src={item?.url} type="video/webm" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
);
|
||||
}
|
||||
return <img
|
||||
key={item?.url}
|
||||
className="object-contain overflow-hidden rounded-xl"
|
||||
src={item?.url}
|
||||
alt="Generated image"
|
||||
style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}
|
||||
/>;
|
||||
})
|
||||
}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
@@ -90,12 +90,12 @@ export const createRun = withServerPromise(
|
||||
Object.entries(workflow_api).forEach(([_, node]) => {
|
||||
if (node.inputs["input_id"] === key) {
|
||||
node.inputs["input_id"] = inputs[key];
|
||||
// Fix for external text default value
|
||||
if (node.class_type == "ComfyUIDeployExternalText") {
|
||||
node.inputs["default_value"] = inputs[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Fix for external text default value
|
||||
if (node.class_type == "ComfyUIDeployExternalText") {
|
||||
node.inputs["default_value"] = inputs[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user