Checkbox widget

This commit is contained in:
space-nuko
2023-05-06 19:53:33 -05:00
parent 0cf675f847
commit 707d6174dd
10 changed files with 210 additions and 32 deletions

View File

@@ -439,7 +439,7 @@ export default class ComfyApp {
const n = workflow.nodes.find((n) => n.id === node_.id); const n = workflow.nodes.find((n) => n.id === node_.id);
if (!node_.isBackendNode) { if (!node_.isBackendNode) {
console.debug("Not serializing node: ", node_.type) // console.debug("Not serializing node: ", node_.type)
continue; continue;
} }
@@ -564,8 +564,8 @@ export default class ComfyApp {
} }
} }
console.warn({ workflow, output }) // console.debug({ workflow, output })
console.warn(promptToGraphVis({ workflow, output })) // console.debug(promptToGraphVis({ workflow, output }))
return { workflow, output }; return { workflow, output };
} }

View File

@@ -99,12 +99,17 @@
if (spec.canShow) if (spec.canShow)
return spec.canShow(widget); return spec.canShow(widget);
if (widget.type === "widget" && spec.validNodeTypes) { if (spec.validNodeTypes) {
if (widget.type === "widget") {
const node = (widget as WidgetLayout).node const node = (widget as WidgetLayout).node
if (!node) if (!node)
return false; return false;
return spec.validNodeTypes.indexOf(node.type) !== -1; return spec.validNodeTypes.indexOf(node.type) !== -1;
} }
else if (widget.type === "container") {
return false;
}
}
return spec.name in widget.attrs return spec.name in widget.attrs
} }

View File

@@ -112,7 +112,7 @@ export class ComfyCopyAction extends ComfyGraphNode {
{ name: "copy", type: BuiltInSlotType.ACTION } { name: "copy", type: BuiltInSlotType.ACTION }
], ],
outputs: [ outputs: [
{ name: "out", type: "*" } { name: "out", type: BuiltInSlotType.EVENT }
], ],
} }
@@ -130,13 +130,15 @@ export class ComfyCopyAction extends ComfyGraphNode {
} }
override onExecute() { override onExecute() {
if (this.getInputLink(0))
this.setProperty("value", this.getInputData(0)) this.setProperty("value", this.getInputData(0))
} }
override onAction(action: any, param: any) { override onAction(action: any, param: any) {
if (action === "copy") {
this.setProperty("value", this.getInputData(0)) this.setProperty("value", this.getInputData(0))
this.setOutputData(0, this.properties.value) this.triggerSlot(0, this.properties.value)
console.log("setData", this.properties.value) }
}; };
} }

View File

@@ -1,4 +1,4 @@
import { BuiltInSlotType, LiteGraph, type ITextWidget, type SlotLayout, clamp } from "@litegraph-ts/core"; import { BuiltInSlotType, LiteGraph, type ITextWidget, type SlotLayout, clamp, type PropertyLayout, type IComboWidget } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode"; import ComfyGraphNode from "./ComfyGraphNode";
import type { GalleryOutput } from "./ComfyWidgetNodes"; import type { GalleryOutput } from "./ComfyWidgetNodes";
@@ -6,7 +6,8 @@ export interface ComfyImageCacheNodeProperties extends Record<any, any> {
images: GalleryOutput | null, images: GalleryOutput | null,
index: number, index: number,
filenames: Record<number, { filename: string | null, status: ImageCacheState }>, filenames: Record<number, { filename: string | null, status: ImageCacheState }>,
genNumber: number genNumber: number,
updateMode: "replace" | "append"
} }
type ImageCacheState = "none" | "uploading" | "failed" | "cached" type ImageCacheState = "none" | "uploading" | "failed" | "cached"
@@ -20,7 +21,8 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
images: null, images: null,
index: 0, index: 0,
filenames: {}, filenames: {},
genNumber: 0 genNumber: 0,
updateMode: "replace"
} }
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
@@ -36,11 +38,15 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
] ]
} }
static propertyLayout: PropertyLayout = [
{ name: "updateMode", defaultValue: "replace", type: "enum", options: { values: ["replace", "append"] } }
]
private _uploadPromise: Promise<void> | null = null; private _uploadPromise: Promise<void> | null = null;
private _state: ImageCacheState = "none"
stateWidget: ITextWidget; stateWidget: ITextWidget;
filenameWidget: ITextWidget; filenameWidget: ITextWidget;
modeWidget: IComboWidget;
constructor(name?: string) { constructor(name?: string) {
super(name) super(name)
@@ -57,6 +63,14 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
"" ""
); );
this.filenameWidget.disabled = true; this.filenameWidget.disabled = true;
this.modeWidget = this.addWidget<IComboWidget>(
"combo",
"Mode",
this.properties.updateMode,
null,
{ property: "updateMode", values: ["replace", "append"] }
);
} }
override onPropertyChanged(property: string, value: any, prevValue?: any) { override onPropertyChanged(property: string, value: any, prevValue?: any) {
@@ -66,13 +80,23 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
else else
this.properties.index = 0 this.properties.index = 0
} }
else if (property === "updateMode") {
this.modeWidget.value = value;
}
this.updateWidgets()
}
private updateWidgets() {
if (this.properties.filenames && this.properties.images) { if (this.properties.filenames && this.properties.images) {
const fileCount = this.properties.images.images.length; const fileCount = this.properties.images.images.length;
const cachedCount = Object.keys(this.properties.filenames).length const cachedCount = Object.keys(this.properties.filenames).length
console.warn(cachedCount, this.properties.filenames) console.warn(cachedCount, this.properties.filenames)
this.filenameWidget.value = `${fileCount} files, ${cachedCount} cached` this.filenameWidget.value = `${fileCount} files, ${cachedCount} cached`
} }
else {
this.filenameWidget.value = `No files cached`
}
} }
override onExecute() { override onExecute() {
@@ -185,6 +209,7 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
this.setProperty("images", null) this.setProperty("images", null)
this.setProperty("filenames", {}) this.setProperty("filenames", {})
this.setProperty("index", 0) this.setProperty("index", 0)
this.updateWidgets();
return return
} }
@@ -192,11 +217,24 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
if (link.data && "images" in link.data) { if (link.data && "images" in link.data) {
this.setProperty("genNumber", this.properties.genNumber + 1) this.setProperty("genNumber", this.properties.genNumber + 1)
const output = link.data as GalleryOutput;
if (this.properties.updateMode === "append" && this.properties.images != null) {
const newImages = this.properties.images.images.concat(output.images)
this.properties.images.images = newImages
this.setProperty("images", this.properties.images)
}
else {
this.setProperty("images", link.data as GalleryOutput) this.setProperty("images", link.data as GalleryOutput)
this.setProperty("filenames", {}) this.setProperty("filenames", {})
console.debug("[ComfyImageCacheNode] Received output!", link.data) }
console.debug("[ComfyImageCacheNode] Received output!", output, this.properties.updateMode, this.properties.images)
this.setIndex(0, true) this.setIndex(0, true)
} }
this.updateWidgets();
} }
} }

View File

@@ -1,10 +1,11 @@
import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, LGraph, type INodeInputSlot, type ITextWidget, type INodeOutputSlot, type SerializedLGraphNode, BuiltInSlotType, type PropertyLayout } from "@litegraph-ts/core"; import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, LGraph, type INodeInputSlot, type ITextWidget, type INodeOutputSlot, type SerializedLGraphNode, BuiltInSlotType, type PropertyLayout, type IComboWidget } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode"; import ComfyGraphNode from "./ComfyGraphNode";
import ComboWidget from "$lib/widgets/ComboWidget.svelte"; import ComboWidget from "$lib/widgets/ComboWidget.svelte";
import RangeWidget from "$lib/widgets/RangeWidget.svelte"; import RangeWidget from "$lib/widgets/RangeWidget.svelte";
import TextWidget from "$lib/widgets/TextWidget.svelte"; import TextWidget from "$lib/widgets/TextWidget.svelte";
import GalleryWidget from "$lib/widgets/GalleryWidget.svelte"; import GalleryWidget from "$lib/widgets/GalleryWidget.svelte";
import ButtonWidget from "$lib/widgets/ButtonWidget.svelte"; import ButtonWidget from "$lib/widgets/ButtonWidget.svelte";
import CheckboxWidget from "$lib/widgets/CheckboxWidget.svelte";
import type { SvelteComponentDev } from "svelte/internal"; import type { SvelteComponentDev } from "svelte/internal";
import { Watch } from "@litegraph-ts/nodes-basic"; import { Watch } from "@litegraph-ts/nodes-basic";
import type IComfyInputSlot from "$lib/IComfyInputSlot"; import type IComfyInputSlot from "$lib/IComfyInputSlot";
@@ -48,8 +49,11 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
override isBackendNode = false; override isBackendNode = false;
override serialize_widgets = true; override serialize_widgets = true;
outputIndex: number | null = 0; // input slots
inputIndex: number = 0; inputIndex: number = 0;
// output slots
outputIndex: number | null = 0;
changedIndex: number | null = 1; changedIndex: number | null = 1;
displayWidget: ITextWidget; displayWidget: ITextWidget;
@@ -94,7 +98,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
if (this.outputIndex !== null && this.outputs.length >= this.outputIndex) { if (this.outputIndex !== null && this.outputs.length >= this.outputIndex) {
this.setOutputData(this.outputIndex, get(this.value)) this.setOutputData(this.outputIndex, get(this.value))
} }
if (this.changedIndex !== null & this.outputs.length >= this.changedIndex) { if (this.changedIndex !== null && this.outputs.length >= this.changedIndex) {
const changedOutput = this.outputs[this.changedIndex] const changedOutput = this.outputs[this.changedIndex]
if (changedOutput.type === BuiltInSlotType.EVENT) if (changedOutput.type === BuiltInSlotType.EVENT)
this.triggerSlot(this.changedIndex, "changed") this.triggerSlot(this.changedIndex, "changed")
@@ -118,10 +122,8 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
if (this.copyFromInputLink) { if (this.copyFromInputLink) {
if (this.inputs.length >= this.inputIndex) { if (this.inputs.length >= this.inputIndex) {
const data = this.getInputData(this.inputIndex) const data = this.getInputData(this.inputIndex)
if (data) { // TODO can "null" be a legitimate value here? if (data != null) { // TODO can "null" be a legitimate value here?
this.setValue(data) this.setValue(data)
const input = this.getInputLink(this.inputIndex)
input.data = null;
} }
} }
} }
@@ -231,7 +233,8 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
inputs: [ inputs: [
{ name: "value", type: "number" } { name: "value", type: "number" },
{ name: "store", type: BuiltInSlotType.ACTION }
], ],
outputs: [ outputs: [
{ name: "value", type: "number" }, { name: "value", type: "number" },
@@ -250,6 +253,11 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
super(name, 0) super(name, 0)
} }
override onAction(action: any, param: any) {
if (action === "store" && typeof param === "number")
this.setValue(param)
}
override setValue(value: any) { override setValue(value: any) {
if (typeof value !== "number") if (typeof value !== "number")
return; return;
@@ -283,7 +291,8 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
inputs: [ inputs: [
{ name: "value", type: "string" } { name: "value", type: "string" },
{ name: "store", type: BuiltInSlotType.ACTION }
], ],
outputs: [ outputs: [
{ name: "value", type: "string" }, { name: "value", type: "string" },
@@ -323,6 +332,11 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
return true; return true;
} }
override onAction(action: any, param: any) {
if (action === "store" && typeof param === "string")
this.setValue(param)
}
override setValue(value: any) { override setValue(value: any) {
if (typeof value !== "string" || this.properties.values.indexOf(value) === -1) if (typeof value !== "string" || this.properties.values.indexOf(value) === -1)
return; return;
@@ -358,7 +372,8 @@ export class ComfyTextNode extends ComfyWidgetNode<string> {
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
inputs: [ inputs: [
{ name: "value", type: "string" } { name: "value", type: "string" },
{ name: "store", type: BuiltInSlotType.ACTION }
], ],
outputs: [ outputs: [
{ name: "value", type: "string" }, { name: "value", type: "string" },
@@ -372,6 +387,11 @@ export class ComfyTextNode extends ComfyWidgetNode<string> {
super(name, "") super(name, "")
} }
override onAction(action: any, param: any) {
if (action === "store")
this.setValue(param)
}
override setValue(value: any) { override setValue(value: any) {
super.setValue(`${value}`) super.setValue(`${value}`)
} }
@@ -428,15 +448,24 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
override outputIndex = null; override outputIndex = null;
override changedIndex = null; override changedIndex = null;
modeWidget: IComboWidget;
constructor(name?: string) { constructor(name?: string) {
super(name, []) super(name, [])
this.modeWidget = this.addWidget("combo", "Mode", this.properties.updateMode, null, { property: "updateMode", values: ["replace", "append"] })
}
override onPropertyChanged(property: any, value: any) {
if (property === "updateMode") {
this.modeWidget.value = value;
}
} }
override onExecute() { override onExecute() {
this.setOutputData(0, this.properties.index) this.setOutputData(0, this.properties.index)
} }
override onAction(action: any) { override onAction(action: any, param: any, options: { action_call?: string }) {
if (action === "clear") { if (action === "clear") {
this.setValue([]) this.setValue([])
} }
@@ -539,3 +568,40 @@ LiteGraph.registerNodeType({
desc: "Button that triggers an event when clicked", desc: "Button that triggers an event when clicked",
type: "ui/button" type: "ui/button"
}) })
export interface ComfyCheckboxProperties extends ComfyWidgetProperties {
}
export class ComfyCheckboxNode extends ComfyWidgetNode<boolean> {
override properties: ComfyCheckboxProperties = {
defaultValue: false,
}
static slotLayout: SlotLayout = {
outputs: [
{ name: "value", type: "boolean" },
{ name: "changed", type: BuiltInSlotType.EVENT },
]
}
override svelteComponentType = CheckboxWidget;
override setValue(value: any) {
value = Boolean(value)
const changed = value != get(this.value);
super.setValue(Boolean(value))
if (changed)
this.triggerSlot(1)
}
constructor(name?: string) {
super(name, false)
}
}
LiteGraph.registerNodeType({
class: ComfyCheckboxNode,
title: "UI.Checkbox",
desc: "Checkbox that stores a boolean value",
type: "ui/checkbox"
})

View File

@@ -0,0 +1,55 @@
<script lang="ts">
import type { ComfyCheckboxNode } from "$lib/nodes/ComfyWidgetNodes";
import { type WidgetLayout } from "$lib/stores/layoutState";
import { Block } from "@gradio/atoms";
import { Checkbox } from "@gradio/form";
import { get, type Writable, writable } from "svelte/store";
export let widget: WidgetLayout | null = null;
let node: ComfyCheckboxNode | null = null;
let nodeValue: Writable<boolean> | null = null;
let attrsChanged: Writable<boolean> | null = null;
$: widget && setNodeValue(widget);
function setNodeValue(widget: WidgetLayout) {
if (widget) {
node = widget.node as ComfyCheckboxNode
nodeValue = node.value;
attrsChanged = widget.attrsChanged;
}
};
</script>
<div class="wrapper gradio-checkbox">
<div class="inner">
{#key $attrsChanged}
{#if node !== null}
<Block>
<Checkbox disabled={widget.attrs.disabled} label={widget.attrs.title} bind:value={$nodeValue} />
</Block>
{/if}
{/key}
</div>
</div>
<style lang="scss">
.wrapper {
display: flex;
flex-direction: row;
align-items: flex-end;
height: 100%;
> .inner {
padding: 2px;
width: 100%;
display: flex;
flex-direction: row;
height: min-content;
:global(> label) {
height: 100%;
}
}
}
</style>

View File

@@ -114,6 +114,11 @@
} }
} }
:global(.svelte-select) {
width: auto;
max-width: 30rem;
}
:global(.svelte-select-list) { :global(.svelte-select-list) {
z-index: var(--layer-top) !important; z-index: var(--layer-top) !important;
} }

View File

@@ -73,10 +73,13 @@
{/if} {/if}
</div> </div>
<style> <style lang="scss">
.wrapper { .wrapper {
padding: 2px;
width: 100%; width: 100%;
:global(> .block) {
border-radius: 0px !important;
}
} }
.padding { .padding {

View File

@@ -419,6 +419,10 @@ div.gradio-row>.form{
box-shadow: none !important; box-shadow: none !important;
} }
.gradio-checkbox > .inner > .block {
background-color: var(--ae-input-bg-color) !important;
}
.wrapper.gradio-textbox textarea { .wrapper.gradio-textbox textarea {
overflow-y: scroll; overflow-y: scroll;
box-sizing: border-box; box-sizing: border-box;