Imagefilethings
This commit is contained in:
2
klecks
2
klecks
Submodule klecks updated: a1cb8064a0...a36de3203f
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
})
|
})
|
||||||
35
src/lib/nodes/ComfyPickImageNode.ts
Normal file
35
src/lib/nodes/ComfyPickImageNode.ts
Normal 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"
|
||||||
|
})
|
||||||
@@ -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"
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
@@ -284,7 +288,7 @@ export function comfyFileToComfyBoxMetadata(comfyUIFile: ComfyImageLocation): Co
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Converts a ComfyUI file into an annotated filepath. Backend nodes like
|
* Converts a ComfyUI file into an annotated filepath. Backend nodes like
|
||||||
* LoadImage support syntax like "subfolder/image.png[output]" to specify which
|
* LoadImage support syntax like "subfolder/image.png [output]" to specify which
|
||||||
* image folder to load from.
|
* image folder to load from.
|
||||||
*/
|
*/
|
||||||
export function comfyFileToAnnotatedFilepath(comfyUIFile: ComfyImageLocation): string {
|
export function comfyFileToAnnotatedFilepath(comfyUIFile: ComfyImageLocation): string {
|
||||||
@@ -292,7 +296,7 @@ export function comfyFileToAnnotatedFilepath(comfyUIFile: ComfyImageLocation): s
|
|||||||
if (comfyUIFile.subfolder != "")
|
if (comfyUIFile.subfolder != "")
|
||||||
path = comfyUIFile.subfolder + "/";
|
path = comfyUIFile.subfolder + "/";
|
||||||
|
|
||||||
path += `${comfyUIFile.filename}[${comfyUIFile.type}]`
|
path += `${comfyUIFile.filename} [${comfyUIFile.type}]`
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
|
||||||
|
|||||||
@@ -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'),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
Reference in New Issue
Block a user