Compare commits

..
10 changed files with 40 additions and 137 deletions
+4 -25
View File
@@ -361,30 +361,13 @@ async def send_json_override(self, event, data, sid=None):
if event == 'executing' and data.get('node') is not None: if event == 'executing' and data.get('node') is not None:
node = data.get('node') 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: if 'last_updated_node' in prompt_metadata[prompt_id] and prompt_metadata[prompt_id]['last_updated_node'] == node:
return return
prompt_metadata[prompt_id]['last_updated_node'] = node prompt_metadata[prompt_id]['last_updated_node'] = node
class_type = prompt_metadata[prompt_id]['workflow_api'][node]['class_type'] class_type = prompt_metadata[prompt_id]['workflow_api'][node]['class_type']
print("updating run live status", class_type) print("updating run live status", class_type)
await update_run_live_status(prompt_id, "Executing " + class_type, calculated_progress) await update_run_live_status(prompt_id, "Executing " + class_type)
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': if event == 'execution_error':
# Careful this might not be fully awaited. # Careful this might not be fully awaited.
@@ -408,22 +391,18 @@ class Status(Enum):
# Global variable to keep track of the last read line number # Global variable to keep track of the last read line number
last_read_line_number = 0 last_read_line_number = 0
async def update_run_live_status(prompt_id, live_status, calculated_progress: float): async def update_run_live_status(prompt_id, live_status):
if prompt_id not in prompt_metadata: if prompt_id not in prompt_metadata:
return return
print("progress", calculated_progress)
status_endpoint = prompt_metadata[prompt_id]['status_endpoint'] status_endpoint = prompt_metadata[prompt_id]['status_endpoint']
body = { body = {
"run_id": prompt_id, "run_id": prompt_id,
"live_status": live_status, "live_status": live_status,
"progress": calculated_progress
} }
# requests.post(status_endpoint, json=body) # requests.post(status_endpoint, json=body)
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.post(status_endpoint, json=body) as response: await session.post(status_endpoint, json=body)
pass
def update_run(prompt_id, status: Status): def update_run(prompt_id, status: Status):
+4 -8
View File
@@ -1,7 +1,7 @@
import { app } from "./app.js"; import { app } from "./app.js";
import { api } from "./api.js"; import { api } from "./api.js";
import { ComfyWidgets, LGraphNode } from "./widgets.js"; import { ComfyWidgets, LGraphNode } from "./widgets.js";
import { generateDependencyGraph } from "https://esm.sh/[email protected]9"; import { generateDependencyGraph } from "https://esm.sh/[email protected]4";
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/ /** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
/** @type {ComfyExtension} */ /** @type {ComfyExtension} */
@@ -182,7 +182,7 @@ function showError(title, message) {
function createDynamicUIHtml(data) { function createDynamicUIHtml(data) {
console.log(data); console.log(data);
let html = let html =
'<div style="min-width: 600px; max-width: 1024px; margin: 14px auto; display: flex; flex-direction: column; gap: 24px;">'; '<div style="max-width: 1024px; margin: 14px auto; display: flex; flex-direction: column; gap: 24px;">';
const bgcolor = "var(--comfy-input-bg)"; const bgcolor = "var(--comfy-input-bg)";
const evenBg = "var(--border-color)"; const evenBg = "var(--border-color)";
const textColor = "var(--input-text)"; const textColor = "var(--input-text)";
@@ -284,12 +284,8 @@ function addButton() {
} }
const ok = await confirmDialog.confirm( const ok = await confirmDialog.confirm(
"Confirm deployment -> " + "Confirm deployment -> " + displayName,
displayName + `A new version will be deployed, are you conform? <br><br><input id="include-deps" type="checkbox" checked>Include dependence</input>`,
"<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; if (!ok) return;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

+2 -29
View File
@@ -48,39 +48,12 @@ const exampleWorkflows: exampleWorkflow[] = [
}, },
{ {
title: "Upscale and Add Detail SDXL", 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', previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-upscale-and-add-detail-sdxl',
image: { image: {
src: '/example-workflows/upscale.webp', src: '/example-workflows/upscale.webp',
alt: 'Upscale and Add Detail SDXL', 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',
}
} }
]; ];
@@ -94,7 +67,7 @@ async function Examples() {
</h1> </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> <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>
<section className="flex justify-center flex-wrap gap-5"> <section className="flex justify-center flex-wrap gap-4">
{exampleWorkflows.map(workflow => { {exampleWorkflows.map(workflow => {
return <Card className="w-[350px]"> return <Card className="w-[350px]">
<CardHeader> <CardHeader>
+1 -3
View File
@@ -80,9 +80,7 @@
/* @apply rounded-lg p-2 overflow-x-scroll */ /* @apply rounded-lg p-2 overflow-x-scroll */
@apply p-2 max-w-full overflow-auto w-full @apply p-2 max-w-full overflow-auto w-full
} }
.vsc-controller{
position: absolute;
}
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border;
+24 -25
View File
@@ -60,7 +60,6 @@ import { callServerPromise } from "./callServerPromise";
import fetcher from "./fetcher"; import fetcher from "./fetcher";
import { ButtonAction } from "@/components/ButtonActionLoader"; import { ButtonAction } from "@/components/ButtonActionLoader";
import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine"; import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
import { VisualizeImagesGrid } from "@/components/VisualizeImagesGrid";
export function VersionSelect({ export function VersionSelect({
workflow, workflow,
@@ -170,21 +169,19 @@ export function useSelectedMachine(
} }
type PublicRunStore = { type PublicRunStore = {
image: { image: string;
url: string;
}[] | null;
loading: boolean; loading: boolean;
runId: string; runId: string;
status: string; status: string;
setImage: (image: { url: string; }[]) => void; setImage: (image: string) => void;
setLoading: (loading: boolean) => void; setLoading: (loading: boolean) => void;
setRunId: (runId: string) => void; setRunId: (runId: string) => void;
setStatus: (status: string) => void; setStatus: (status: string) => void;
}; };
export const publicRunStore = create<PublicRunStore>((set) => ({ export const publicRunStore = create<PublicRunStore>((set) => ({
image: null, image: "",
loading: false, loading: false,
runId: "", runId: "",
status: "", status: "",
@@ -208,10 +205,7 @@ export function PublicRunOutputs(props: {
console.log(res?.status); console.log(res?.status);
if (res) setStatus(res.status); if (res) setStatus(res.status);
if (res && res.status === "success") { if (res && res.status === "success") {
const imageURLs = res.outputs[0]?.data.images.map((item: { url: string; }) => { setImage(res.outputs[0]?.data.images[0].url);
return { url: item.url };
});
setImage(imageURLs);
setLoading(false); setLoading(false);
clearInterval(interval); clearInterval(interval);
} }
@@ -220,25 +214,30 @@ export function PublicRunOutputs(props: {
return () => clearInterval(interval); return () => clearInterval(interval);
}, [runId]); }, [runId]);
if (loading) {
return ( return (
<div className="border border-gray-200 w-full h-[400px] square rounded-lg relative p-4 "> <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 && (
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2"> <div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
{status} <LoadingIcon /> {status} <LoadingIcon />
</div> </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> </div>
); );
} }
@@ -329,7 +328,7 @@ export function RunWorkflowButton({
className="px-1" className="px-1"
> >
<div className="flex justify-end"> <div className="flex justify-end">
<AutoFormSubmit disabled={isLoading}> <AutoFormSubmit>
Run Run
{isLoading ? <LoadingIcon /> : <Play size={14} />} {isLoading ? <LoadingIcon /> : <Play size={14} />}
</AutoFormSubmit> </AutoFormSubmit>
@@ -1,42 +0,0 @@
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>
);
}
+2 -2
View File
@@ -90,12 +90,12 @@ export const createRun = withServerPromise(
Object.entries(workflow_api).forEach(([_, node]) => { Object.entries(workflow_api).forEach(([_, node]) => {
if (node.inputs["input_id"] === key) { if (node.inputs["input_id"] === key) {
node.inputs["input_id"] = inputs[key]; node.inputs["input_id"] = inputs[key];
}
// Fix for external text default value // Fix for external text default value
if (node.class_type == "ComfyUIDeployExternalText") { if (node.class_type == "ComfyUIDeployExternalText") {
node.inputs["default_value"] = inputs[key]; node.inputs["default_value"] = inputs[key];
} }
}
}); });
} }
} }