Imagefilethings

This commit is contained in:
space-nuko
2023-05-13 22:13:06 -05:00
parent 05bcce5573
commit 152a1c6097
14 changed files with 169 additions and 272 deletions

2
klecks

Submodule klecks updated: a1cb8064a0...a36de3203f

View File

@@ -1,4 +1,4 @@
import { BuiltInSlotShape, LGraph, LGraphCanvas, LGraphNode, LiteGraph, NodeMode, type MouseEventExt, type Vector2, type Vector4, TitleMode, type ContextMenuItem, type IContextMenuItem, Subgraph } from "@litegraph-ts/core"; import { BuiltInSlotShape, LGraph, LGraphCanvas, LGraphNode, LiteGraph, NodeMode, type MouseEventExt, type Vector2, type Vector4, TitleMode, type ContextMenuItem, type IContextMenuItem, Subgraph, LLink } from "@litegraph-ts/core";
import type ComfyApp from "./components/ComfyApp"; import type ComfyApp from "./components/ComfyApp";
import queueState from "./stores/queueState"; import queueState from "./stores/queueState";
import { get } from "svelte/store"; import { get } from "svelte/store";
@@ -259,66 +259,59 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
const newNode = LiteGraph.createNode(node.type); const newNode = LiteGraph.createNode(node.type);
for (let index = 0; index < newNode.inputs.length; index++) { const createInputReroute = (slotIndex: number, link: LLink | null): ComfyReroute => {
const newInput = newNode.inputs[index];
const oldInput = node.inputs[index]
if (oldInput && newInput.type === oldInput.type) {
continue;
}
let link: LLink | null = null;
if (oldInput) {
link = node.getInputLink(index);
node.disconnectInput(index)
oldInput.type = newInput.type
oldInput.name = newInput.name
}
else {
node.addInput(newInput.name, newInput.type, newInput)
}
const reroute = LiteGraph.createNode(ComfyReroute); const reroute = LiteGraph.createNode(ComfyReroute);
reroute.properties.ignoreTypes = true; reroute.properties.ignoreTypes = true;
node.graph.add(reroute) node.graph.add(reroute)
const inputPos = node.getConnectionPos(true, index); const inputPos = node.getConnectionPos(true, slotIndex);
reroute.pos = [inputPos[0] - 140, inputPos[1] + LiteGraph.NODE_SLOT_HEIGHT / 2]; reroute.pos = [inputPos[0] - 140, inputPos[1] + LiteGraph.NODE_SLOT_HEIGHT / 2];
reroute.connect(0, node, index); node.graph.getNodeById(link.origin_id).connect(link.origin_slot, reroute, 0)
if (link != null) return reroute
node.graph.getNodeById(link.target_id).connect(link.target_slot, reroute, 0)
} }
for (let index = 0; index < newNode.outputs.length; index++) { for (let index = node.inputs.length - 1; index >= 0; index--) {
const newOutput = newNode.outputs[index]; let link: LLink | null = null;
const oldOutput = node.outputs[index]
if (oldOutput && newOutput.type === oldOutput.type) { link = node.getInputLink(index);
continue; node.disconnectInput(index)
if (link)
createInputReroute(index, link);
node.removeInput(index);
} }
let links = [] for (let index = 0; index < newNode.inputs.length; index++) {
const newInput = newNode.inputs[index]
if (oldOutput) { const input = node.addInput(newInput.name, newInput.type);
links = node.getOutputLinks(index)
node.disconnectOutput(index)
oldOutput.type = newOutput.type
oldOutput.name = newOutput.name
}
else {
node.addOutput(newOutput.name, newOutput.type, newOutput)
} }
const createOutputReroute = (index: number, links: LLink[], connect: boolean = true): ComfyReroute => {
const reroute = LiteGraph.createNode(ComfyReroute); const reroute = LiteGraph.createNode(ComfyReroute);
reroute.properties.ignoreTypes = true; reroute.properties.ignoreTypes = true;
node.graph.add(reroute) node.graph.add(reroute)
const rerouteSize = reroute.computeSize(); const rerouteSize = reroute.computeSize();
const outputPos = node.getConnectionPos(false, index); const outputPos = node.getConnectionPos(false, index);
reroute.pos = [outputPos[0] + rerouteSize[0] + 20, outputPos[1] + LiteGraph.NODE_SLOT_HEIGHT / 2]; reroute.pos = [outputPos[0] + rerouteSize[0] + 20, outputPos[1] + LiteGraph.NODE_SLOT_HEIGHT / 2];
node.connect(index, reroute, 0);
for (const link of links) { for (const link of links) {
reroute.connect(0, link.target_id, link.target_slot) reroute.connect(0, node.graph.getNodeById(link.target_id), link.target_slot)
} }
return reroute
}
for (let index = node.outputs.length - 1; index >= 0; index--) {
let links = node.getOutputLinks(index)
node.disconnectOutput(index)
if (links.length > 0)
createOutputReroute(index, links);
node.removeOutput(index);
}
for (let index = 0; index < newNode.outputs.length; index++) {
const newOutput = newNode.outputs[index]
const output = node.addOutput(newOutput.name, newOutput.type);
} }
} }

View File

@@ -258,9 +258,13 @@ export default class ComfyAPI {
}, },
body: postBody body: postBody
}) })
.then(res => res.json()) .then(async (res) => {
if (res.status != 200) {
throw await res.text()
}
return res.json()
})
.then(raw => { return { promptID: raw.prompt_id } }) .then(raw => { return { promptID: raw.prompt_id } })
.catch(res => { throw res.text() })
.catch(error => { return { error } }) .catch(error => { return { error } })
} }

View File

@@ -408,9 +408,8 @@ export default class ComfyApp {
setColor(type, "orange") setColor(type, "orange")
} }
setColor("IMAGE", "rebeccapurple") setColor("COMFYBOX_IMAGES", "rebeccapurple")
setColor("COMFYBOX_IMAGES", "lime") setColor("COMFYBOX_IMAGE", "fuchsia")
setColor("COMFYBOX_IMAGE", "green")
setColor(BuiltInSlotType.EVENT, "lightseagreen") setColor(BuiltInSlotType.EVENT, "lightseagreen")
setColor(BuiltInSlotType.ACTION, "lightseagreen") setColor(BuiltInSlotType.ACTION, "lightseagreen")
} }
@@ -787,15 +786,15 @@ export default class ComfyApp {
queueState.afterQueued(promptID, num, p.output, extraData) queueState.afterQueued(promptID, num, p.output, extraData)
error = response.error; error = response.error;
} catch (error) { } catch (err) {
error = error.toString(); error = err
} }
if (error != null) { if (error != null) {
const mes = error.response || error.toString() const mes = error.response || error.toString()
notify(`Error queuing prompt:\n${mes}`, { type: "error" }) notify(`Error queuing prompt:\n${mes}`, { type: "error" })
console.error(promptToGraphVis(p)) console.error(promptToGraphVis(p))
console.error("Error queuing prompt", mes, num, p) console.error("Error queuing prompt", error, num, p)
break; break;
} }

View File

@@ -1,11 +1,11 @@
import { LiteGraph, type SlotLayout } from "@litegraph-ts/core"; import { LiteGraph, type SlotLayout } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode"; import ComfyGraphNode from "./ComfyGraphNode";
import { comfyFileToAnnotatedFilepath, isComfyBoxImageMetadata } from "$lib/utils"; import { comfyFileToAnnotatedFilepath, isComfyBoxImageMetadata, parseWhateverIntoImageMetadata } from "$lib/utils";
export class ComfyImageToFilepath extends ComfyGraphNode { export default class ComfyImageToFilepathNode extends ComfyGraphNode {
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
inputs: [ inputs: [
{ name: "image", type: "COMFYBOX_IMAGE" }, { name: "image", type: "COMFYBOX_IMAGES,COMFYBOX_IMAGE" },
], ],
outputs: [ outputs: [
{ name: "filepath", type: "string" }, { name: "filepath", type: "string" },
@@ -14,19 +14,20 @@ export class ComfyImageToFilepath extends ComfyGraphNode {
override onExecute() { override onExecute() {
const data = this.getInputData(0) const data = this.getInputData(0)
if (data == null || !isComfyBoxImageMetadata(data)) { const meta = parseWhateverIntoImageMetadata(data);
if (meta == null || meta.length === 0) {
this.setOutputData(0, null) this.setOutputData(0, null)
return; return;
} }
const path = comfyFileToAnnotatedFilepath(data.comfyUIFile); const path = comfyFileToAnnotatedFilepath(meta[0].comfyUIFile);
this.setOutputData(0, path); this.setOutputData(0, path);
} }
} }
LiteGraph.registerNodeType({ LiteGraph.registerNodeType({
class: ComfyImageToFilepath, class: ComfyImageToFilepathNode,
title: "Comfy.ImageToFilepath", title: "Comfy.ImageToFilepath",
desc: "Converts ComfyBox image metadata to an annotated filepath like \"image.png[output]\" for use with ComfyUI.", desc: "Converts ComfyBox image metadata to an annotated filepath like \"image.png[output]\" for use with ComfyUI.",
type: "images/file_to_filepath" type: "image/file_to_filepath"
}) })

View File

@@ -0,0 +1,35 @@
import { LiteGraph, type SlotLayout } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
import { isComfyBoxImageMetadataArray } from "$lib/utils";
/*
* TODO: This is just a temporary workaround until litegraph can handle typed
* array arguments.
*/
export default class ComfyPickImageNode extends ComfyGraphNode {
static slotLayout: SlotLayout = {
inputs: [
{ name: "images", type: "COMFYBOX_IMAGES" },
],
outputs: [
{ name: "image", type: "COMFYBOX_IMAGE" },
]
}
override onExecute() {
const data = this.getInputData(0)
if (data == null || !isComfyBoxImageMetadataArray(data)) {
this.setOutputData(0, null)
return;
}
this.setOutputData(0, data[0]);
}
}
LiteGraph.registerNodeType({
class: ComfyPickImageNode,
title: "Comfy.PickImage",
desc: "Picks out the first image from an array of ComfyBox images.",
type: "image/pick_image"
})

View File

@@ -170,6 +170,10 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
parseValue(value: any): T { return value as T }; parseValue(value: any): T { return value as T };
getValue(): T {
return get(this.value);
}
setValue(value: any, noChangedEvent: boolean = false) { setValue(value: any, noChangedEvent: boolean = false) {
if (noChangedEvent) if (noChangedEvent)
this._noChangedEvent = true; this._noChangedEvent = true;
@@ -869,57 +873,6 @@ LiteGraph.registerNodeType({
type: "ui/radio" type: "ui/radio"
}) })
export interface ComfyImageUploadProperties extends ComfyWidgetProperties {
fileCount: "single" | "multiple" // gradio File component format
}
export class ComfyImageUploadNode extends ComfyWidgetNode<ComfyBoxImageMetadata[]> {
override properties: ComfyImageUploadProperties = {
defaultValue: [],
tags: [],
fileCount: "single",
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "store", type: BuiltInSlotType.ACTION }
],
outputs: [
{ name: "images", type: "COMFYBOX_IMAGES" }, // TODO support batches
{ name: "changed", type: BuiltInSlotType.EVENT },
]
}
override svelteComponentType = ImageUploadWidget;
override defaultValue = [];
override outputIndex = 0;
override changedIndex = 1;
override storeActionName = "store";
override saveUserState = false;
constructor(name?: string) {
super(name, [])
}
override parseValue(value: any): ComfyBoxImageMetadata[] {
return parseWhateverIntoImageMetadata(value) || []
}
override formatValue(value: ComfyImageLocation[]): string {
return `Images: ${value?.length || 0}`
}
}
LiteGraph.registerNodeType({
class: ComfyImageUploadNode,
title: "UI.ImageUpload",
desc: "Widget that lets you upload images into ComfyUI's input folder",
type: "ui/image_upload"
})
export type FileNameOrGalleryData = string | ComfyImageLocation;
export type MultiImageData = FileNameOrGalleryData[];
export interface ComfyImageEditorNodeProperties extends ComfyWidgetProperties { export interface ComfyImageEditorNodeProperties extends ComfyWidgetProperties {
} }
@@ -941,7 +894,7 @@ export class ComfyImageEditorNode extends ComfyWidgetNode<ComfyBoxImageMetadata[
override svelteComponentType = ImageEditorWidget; override svelteComponentType = ImageEditorWidget;
override defaultValue = []; override defaultValue = [];
override outputIndex = null; override outputIndex = 0;
override inputIndex = null; override inputIndex = null;
override changedIndex = 1; override changedIndex = 1;
override storeActionName = "store"; override storeActionName = "store";
@@ -963,6 +916,6 @@ export class ComfyImageEditorNode extends ComfyWidgetNode<ComfyBoxImageMetadata[
LiteGraph.registerNodeType({ LiteGraph.registerNodeType({
class: ComfyImageEditorNode, class: ComfyImageEditorNode,
title: "UI.ImageEditor", title: "UI.ImageEditor",
desc: "Widget that lets edit a multi-layered image", desc: "Widget that lets you upload and edit a multi-layered image. Can also act like a standalone image uploader.",
type: "ui/image_editor" type: "ui/image_editor"
}) })

View File

@@ -14,6 +14,7 @@ export {
export { default as ComfyPickFirstNode } from "./ComfyPickFirstNode" export { default as ComfyPickFirstNode } from "./ComfyPickFirstNode"
export { default as ComfyValueControl } from "./ComfyValueControl" export { default as ComfyValueControl } from "./ComfyValueControl"
export { default as ComfySelector } from "./ComfySelector" export { default as ComfySelector } from "./ComfySelector"
export { default as ComfyImageCacheNode } from "./ComfyImageCacheNode"
export { default as ComfyTriggerNewEventNode } from "./ComfyTriggerNewEventNode" export { default as ComfyTriggerNewEventNode } from "./ComfyTriggerNewEventNode"
export { default as ComfyConfigureQueuePromptButton } from "./ComfyConfigureQueuePromptButton" export { default as ComfyConfigureQueuePromptButton } from "./ComfyConfigureQueuePromptButton"
export { default as ComfyPickImageNode } from "./ComfyPickImageNode"
export { default as ComfyImageToFilepathNode } from "./ComfyImageToFilepathNode"

View File

@@ -5,6 +5,7 @@ import { type LGraphNode, type IWidget, type LGraph, NodeMode, type LGraphRemove
import { SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action'; import { SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action';
import type { ComfyWidgetNode } from '$lib/nodes'; import type { ComfyWidgetNode } from '$lib/nodes';
import type { NodeID } from '$lib/api'; import type { NodeID } from '$lib/api';
import { v4 as uuidv4 } from "uuid";
type DragItemEntry = { type DragItemEntry = {
/* /*
@@ -61,11 +62,6 @@ export type LayoutState = {
*/ */
allItemsByNode: Record<NodeID, DragItemEntry>, allItemsByNode: Record<NodeID, DragItemEntry>,
/*
* Next ID to use for instantiating a new drag item
*/
currentId: UUID,
/* /*
* Selected drag items. * Selected drag items.
*/ */
@@ -411,6 +407,18 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
defaultValue: "" defaultValue: ""
}, },
// Editor
{
name: "variant",
type: "enum",
location: "widget",
editable: true,
validNodeTypes: ["ui/image_editor"],
values: ["inlineEditor", "fileUpload"],
defaultValue: "inlineEditor",
refreshPanelOnChange: true
},
// Gallery // Gallery
{ {
name: "variant", name: "variant",
@@ -668,7 +676,6 @@ const store: Writable<LayoutState> = writable({
root: null, root: null,
allItems: {}, allItems: {},
allItemsByNode: {}, allItemsByNode: {},
currentId: 0,
currentSelection: [], currentSelection: [],
currentSelectionNodes: [], currentSelectionNodes: [],
isMenuOpen: false, isMenuOpen: false,
@@ -703,7 +710,7 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes>
const state = get(store); const state = get(store);
const dragItem: ContainerLayout = { const dragItem: ContainerLayout = {
type: "container", type: "container",
id: `${state.currentId++}`, id: uuidv4(),
attrsChanged: writable(0), attrsChanged: writable(0),
attrs: { attrs: {
...defaultWidgetAttributes, ...defaultWidgetAttributes,
@@ -726,7 +733,7 @@ function addWidget(parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partia
const widgetName = "Widget" const widgetName = "Widget"
const dragItem: WidgetLayout = { const dragItem: WidgetLayout = {
type: "widget", type: "widget",
id: `${state.currentId++}`, id: uuidv4(),
node: node, node: node,
attrsChanged: writable(0), attrsChanged: writable(0),
attrs: { attrs: {
@@ -939,7 +946,6 @@ function initDefaultLayout() {
root: null, root: null,
allItems: {}, allItems: {},
allItemsByNode: {}, allItemsByNode: {},
currentId: 0,
currentSelection: [], currentSelection: [],
currentSelectionNodes: [], currentSelectionNodes: [],
isMenuOpen: false, isMenuOpen: false,
@@ -964,7 +970,6 @@ function initDefaultLayout() {
export type SerializedLayoutState = { export type SerializedLayoutState = {
root: DragItemID | null, root: DragItemID | null,
allItems: Record<DragItemID, SerializedDragEntry>, allItems: Record<DragItemID, SerializedDragEntry>,
currentId: UUID,
attrs: LayoutAttributes attrs: LayoutAttributes
} }
@@ -1002,7 +1007,6 @@ function serialize(): SerializedLayoutState {
return { return {
root: state.root?.id, root: state.root?.id,
allItems, allItems,
currentId: state.currentId,
attrs: state.attrs attrs: state.attrs
} }
} }
@@ -1055,7 +1059,6 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
root, root,
allItems, allItems,
allItemsByNode, allItemsByNode,
currentId: data.currentId,
currentSelection: [], currentSelection: [],
currentSelectionNodes: [], currentSelectionNodes: [],
isMenuOpen: false, isMenuOpen: false,

View File

@@ -256,6 +256,10 @@ export function isComfyBoxImageMetadata(value: any): value is ComfyBoxImageMetad
return value && typeof value === "object" && (value as any).isComfyBoxImageMetadata; return value && typeof value === "object" && (value as any).isComfyBoxImageMetadata;
} }
export function isComfyBoxImageMetadataArray(value: any): value is ComfyBoxImageMetadata[] {
return Array.isArray(value) && value.every(isComfyBoxImageMetadata);
}
export function isComfyExecutionResult(value: any): value is ComfyExecutionResult { export function isComfyExecutionResult(value: any): value is ComfyExecutionResult {
return value && typeof value === "object" && Array.isArray(value.images) return value && typeof value === "object" && Array.isArray(value.images)
} }
@@ -306,7 +310,7 @@ export function parseWhateverIntoImageMetadata(param: any): ComfyBoxImageMetadat
if (isComfyBoxImageMetadata(param)) { if (isComfyBoxImageMetadata(param)) {
meta = [param]; meta = [param];
} }
if (Array.isArray(param) && !param.every(isComfyBoxImageMetadata)) { else if (Array.isArray(param) && param.every(isComfyBoxImageMetadata)) {
meta = param meta = param
} }
else if (isComfyExecutionResult(param)) { else if (isComfyExecutionResult(param)) {
@@ -316,6 +320,10 @@ export function parseWhateverIntoImageMetadata(param: any): ComfyBoxImageMetadat
return meta; return meta;
} }
export function comfyBoxImageToComfyFile(image: ComfyBoxImageMetadata): ComfyImageLocation {
return image.comfyUIFile
}
export function comfyBoxImageToComfyURL(image: ComfyBoxImageMetadata): string { export function comfyBoxImageToComfyURL(image: ComfyBoxImageMetadata): string {
return convertComfyOutputToComfyURL(image.comfyUIFile) return convertComfyOutputToComfyURL(image.comfyUIFile)
} }

View File

@@ -6,11 +6,10 @@
import { Button } from "@gradio/button"; import { Button } from "@gradio/button";
import type { ComfyImageEditorNode, ComfyImageLocation, MultiImageData } from "$lib/nodes/ComfyWidgetNodes"; import type { ComfyImageEditorNode, ComfyImageLocation, MultiImageData } from "$lib/nodes/ComfyWidgetNodes";
import { Embed as Klecks, KL, KlApp, klHistory, type KlAppOptionsEmbed } from "klecks"; import { Embed as Klecks, KL, KlApp, klHistory, type KlAppOptionsEmbed } from "klecks";
import type { FileData as GradioFileData } from "@gradio/upload";
import "klecks/style/style.scss"; import "klecks/style/style.scss";
import ImageUpload from "$lib/components/ImageUpload.svelte"; import ImageUpload from "$lib/components/ImageUpload.svelte";
import { uploadImageToComfyUI, type ComfyUploadImageAPIResponse, convertComfyOutputToComfyURL, type ComfyBoxImageMetadata, comfyFileToComfyBoxMetadata } from "$lib/utils"; import { uploadImageToComfyUI, type ComfyUploadImageAPIResponse, convertComfyOutputToComfyURL, type ComfyBoxImageMetadata, comfyFileToComfyBoxMetadata, comfyBoxImageToComfyURL, comfyBoxImageToComfyFile } from "$lib/utils";
import notify from "$lib/notify"; import notify from "$lib/notify";
export let widget: WidgetLayout | null = null; export let widget: WidgetLayout | null = null;
@@ -44,29 +43,6 @@
} }
}; };
const urlPattern = /^((http|https|ftp):\/\/)/;
$: updateUrls($nodeValue);
function updateUrls(value: MultiImageData) {
// leftUrl = ""
// rightUrl = ""
// console.warn("UPD", value)
//
// if (typeof value[0] === "string") {
// if (urlPattern.test(value[0]))
// leftUrl = value[0]
// else
// leftUrl = convertFilenameToComfyURL(value[0])
// }
// if (typeof value[1] === "string") {
// if (urlPattern.test(value[1]))
// rightUrl = value[1]
// else
// rightUrl = convertFilenameToComfyURL(value[1])
// }
}
let editorRoot: HTMLDivElement | null = null; let editorRoot: HTMLDivElement | null = null;
let showModal = false; let showModal = false;
let kl: Klecks | null = null; let kl: Klecks | null = null;
@@ -190,38 +166,73 @@
let uploadError = null; let uploadError = null;
function onUploading() { function onUploading() {
console.warn("UPLOADING!!!")
uploadError = null; uploadError = null;
status = "uploading" status = "uploading"
} }
function onUploaded(e: CustomEvent<ComfyImageLocation[]>) { function onUploaded(e: CustomEvent<ComfyImageLocation[]>) {
console.warn("UPLOADED!!!")
uploadError = null; uploadError = null;
status = "uploaded" status = "uploaded"
$nodeValue = e.detail; $nodeValue = e.detail.map(comfyFileToComfyBoxMetadata);
} }
function onClear() { function onClear() {
console.warn("CLEAR!!!")
uploadError = null; uploadError = null;
status = "none" status = "none"
} }
function onUploadError(e: CustomEvent<any>) { function onUploadError(e: CustomEvent<any>) {
console.warn("ERROR!!!")
status = "error" status = "error"
uploadError = e.detail uploadError = e.detail
notify(`Failed to upload image to ComfyUI: ${err}`, { type: "error", timeout: 10000 })
} }
function onChange(e: CustomEvent<ComfyImageLocation[]>) { function onChange(e: CustomEvent<ComfyImageLocation[]>) {
// $nodeValue = e.detail;
} }
$: canEdit = status === "none" || status === "uploaded"; let _value: ComfyImageLocation[] = []
$: if ($nodeValue)
_value = $nodeValue.map(comfyBoxImageToComfyFile)
else
_value = []
$: canEdit = status === "none" || status === "uploaded";
</script> </script>
<div class="wrapper comfy-image-editor"> <div class="wrapper comfy-image-editor">
{#if isMobile} {#if widget.attrs.variant === "fileUpload" || isMobile}
<span>TODO mask editor</span> <ImageUpload value={_value}
bind:imgWidth
bind:imgHeight
fileCount={"single"}
elem_classes={[]}
style={""}
label={widget.attrs.title}
on:uploading={onUploading}
on:uploaded={onUploaded}
on:upload_error={onUploadError}
on:clear={onClear}
on:change={onChange}
/>
{:else} {:else}
<div class="comfy-image-editor-panel">
<ImageUpload value={_value}
bind:imgWidth
bind:imgHeight
fileCount={"single"}
elem_classes={[]}
style={""}
label={widget.attrs.title}
on:uploading={onUploading}
on:uploaded={onUploaded}
on:upload_error={onUploadError}
on:clear={onClear}
on:change={onChange}
/>
<Modal bind:showModal closeOnClick={false} on:close={disposeEditor}> <Modal bind:showModal closeOnClick={false} on:close={disposeEditor}>
<div> <div>
<div id="klecks-loading-screen"> <div id="klecks-loading-screen">
@@ -230,20 +241,6 @@
<div class="image-editor-root" bind:this={editorRoot} /> <div class="image-editor-root" bind:this={editorRoot} />
</div> </div>
</Modal> </Modal>
<div class="comfy-image-editor-panel">
<ImageUpload value={$nodeValue}
bind:imgWidth
bind:imgHeight
fileCount={"single"}
elem_classes={[]}
style={""}
label={"Image"}
on:uploading={onUploading}
on:uploaded={onUploaded}
on:upload_error={onUploadError}
on:clear={onClear}
on:change={onChange}
/>
<Block> <Block>
<Button variant="primary" disabled={!canEdit} on:click={openImageEditor}> <Button variant="primary" disabled={!canEdit} on:click={openImageEditor}>
Edit Image Edit Image

View File

@@ -1,101 +0,0 @@
<script lang="ts">
import ImageUpload from "$lib/components/ImageUpload.svelte"
import type { WidgetLayout } from "$lib/stores/layoutState";
import type { Writable } from "svelte/store";
import type { ComfyGalleryNode, ComfyImageUploadNode, ComfyImageLocation, MultiImageData } from "$lib/nodes/ComfyWidgetNodes";
import notify from "$lib/notify";
import { comfyFileToComfyBoxMetadata, type ComfyBoxImageMetadata } from "$lib/utils";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyImageUploadNode | null = null;
let nodeValue: Writable<ComfyBoxImageMetadata[]> | null = null;
let propsChanged: Writable<number> | null = null;
let imgWidth: number = 1;
let imgHeight: number = 1;
$: widget && setNodeValue(widget);
$: if ($nodeValue && $nodeValue.length > 0) {
// TODO improve
if (imgWidth > 0 && imgHeight > 0) {
$nodeValue[0].width = imgWidth
$nodeValue[0].height = imgHeight
}
else {
$nodeValue[0].width = 0
$nodeValue[0].height = 0
}
}
function setNodeValue(widget: WidgetLayout) {
if (widget) {
node = widget.node as ComfyImageUploadNode
nodeValue = node.value;
propsChanged = node.propsChanged;
}
};
function onUploading() {
console.warn("ONUPLOAD!!!")
$nodeValue = []
}
function onChange(e: CustomEvent<ComfyImageLocation[]>) {
console.warn("ONCHANGE!!!", e.detail)
$nodeValue = (e.detail || []).map(comfyFileToComfyBoxMetadata)
}
function onUploaded(e: CustomEvent<ComfyImageLocation[]>) {
console.warn("ONUPLOADED!!!", e.detail)
$nodeValue = (e.detail || []).map(comfyFileToComfyBoxMetadata)
}
function onUploadError(e: CustomEvent<any>) {
console.warn("ONUPLOADERRO!!!", e.detail)
notify(`Error uploading image to ComfyUI: ${e.detail}`)
$nodeValue = []
}
function onClear(e: CustomEvent<ComfyImageLocation[]>) {
console.warn("ONCLEAR!!!", e.detail)
$nodeValue = []
}
</script>
<div class="wrapper gradio-file comfy-image-upload" style={widget.attrs.style}>
{#if widget && node}
<ImageUpload value={$nodeValue || []}
bind:imgWidth
bind:imgHeight
bind:fileCount={node.properties.fileCount}
elem_classes={widget.attrs.classes.split(",")}
style={widget.attrs.style}
label={widget.attrs.title}
on:uploading={onUploading}
on:uploaded={onUploaded}
on:upload_error={onUploadError}
on:clear={onClear}
on:change={onChange}/>
{/if}
</div>
<style lang="scss">
.comfy-image-upload {
height: var(--size-96);
:global(.block) {
height: inherit;
padding: 0;
border-radius: 0;
}
img {
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
}
</style>

View File

@@ -1,5 +1,8 @@
import { LiteGraph } from '@litegraph-ts/core';
import App from './App.svelte'; import App from './App.svelte';
LiteGraph.use_uuids = true;
const app = new App({ const app = new App({
target: document.getElementById('app'), target: document.getElementById('app'),
}) })

View File

@@ -9,6 +9,7 @@ import ComfyGraph from '$lib/ComfyGraph';
Framework7.use(Framework7Svelte); Framework7.use(Framework7Svelte);
LiteGraph.use_uuids = true;
LiteGraph.dialog_close_on_mouse_leave = false; LiteGraph.dialog_close_on_mouse_leave = false;
LiteGraph.search_hide_on_mouse_leave = false; LiteGraph.search_hide_on_mouse_leave = false;
LiteGraph.pointerevents_method = "pointer"; LiteGraph.pointerevents_method = "pointer";