Image and radio widgets

This commit is contained in:
space-nuko
2023-05-08 01:25:10 -05:00
parent b53d04286b
commit 584119433d
17 changed files with 332 additions and 94 deletions

View File

@@ -1,4 +1,4 @@
import { LConnectionKind, LGraph, LGraphNode, type INodeSlot, type SlotIndex, LiteGraph, getStaticProperty, type LGraphAddNodeOptions } from "@litegraph-ts/core";
import { LConnectionKind, LGraph, LGraphNode, type INodeSlot, type SlotIndex, LiteGraph, getStaticProperty, type LGraphAddNodeOptions, LGraphCanvas } from "@litegraph-ts/core";
import GraphSync from "./GraphSync";
import EventEmitter from "events";
import type TypedEmitter from "typed-emitter";
@@ -53,6 +53,17 @@ export default class ComfyGraph extends LGraph {
layoutState.nodeAdded(node)
this.graphSync.onNodeAdded(node);
// All nodes whether they come from base litegraph or ComfyBox should
// have tags added to them. Can't override serialization for existing
// node types to add `tags` as anew field so putting it in properties is better.
if (node.properties.tags == null)
node.properties.tags = []
if ((node as any).canInheritSlotTypes && node.inputs.length > 1) {
node.color ||= LGraphCanvas.node_colors["green"].color;
node.bgColor ||= LGraphCanvas.node_colors["green"].bgColor;
}
if ("outputProperties" in node) {
const widgetNode = node as ComfyWidgetNode;
for (const propName of widgetNode.outputProperties) {

View File

@@ -166,7 +166,8 @@
}
:global(.label-wrap > span:not(.icon)) {
color: var(--block-title-text-color);
/* color: var(--block-title-text-color); */
font-size: 16px;
}
.handle {

View File

@@ -548,8 +548,6 @@ export default class ComfyApp {
break;
}
console.debug("[graphToPrompt] consider link", JSON.stringify(link), parent.id)
if (nextLink && !seen[nextLink.id]) {
seen[nextLink.id] = true
const inputNode = parent.graph.getNodeById(nextLink.origin_id) as ComfyGraphNode;
@@ -558,7 +556,7 @@ export default class ComfyApp {
parent = null;
}
else {
console.debug("[graphToPrompt] Traverse upstream link", JSON.stringify(link), parent.id, inputNode?.id, inputNode?.isBackendNode)
console.debug("[graphToPrompt] Traverse upstream link", parent.id, inputNode?.id, inputNode?.isBackendNode)
link = nextLink;
parent = inputNode;
}
@@ -571,7 +569,7 @@ export default class ComfyApp {
if (tag && !hasTag(parent, tag))
continue;
console.debug("[graphToPrompt] final link", JSON.stringify(link), parent.id, node.id)
console.debug("[graphToPrompt] final link", parent.id, node.id)
const input = node.inputs[i]
// TODO can null be a legitimate value in some cases?
// Nodes like CLIPLoader will never have a value in the frontend, hence "null".

View File

@@ -1,21 +1,13 @@
import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, BuiltInSlotType, type ITextWidget, type SerializedLGraphNode, NodeMode, type IToggleWidget } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
import { Watch } from "@litegraph-ts/nodes-basic";
import type { SerializedPrompt } from "$lib/components/ComfyApp";
import { toast } from '@zerodevx/svelte-toast'
import type { GalleryOutput } from "./ComfyWidgetNodes";
import { get } from "svelte/store";
import queueState from "$lib/stores/queueState";
import notify from "$lib/notify";
import layoutState from "$lib/stores/layoutState";
export interface ComfyQueueEventsProperties extends Record<any, any> {
}
import queueState from "$lib/stores/queueState";
import { BuiltInSlotType, LiteGraph, NodeMode, type ITextWidget, type IToggleWidget, type SerializedLGraphNode, type SlotLayout } from "@litegraph-ts/core";
import { get } from "svelte/store";
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
import type { GalleryOutput } from "./ComfyWidgetNodes";
export class ComfyQueueEvents extends ComfyGraphNode {
override properties: ComfyQueueEventsProperties = {
}
static slotLayout: SlotLayout = {
outputs: [
{ name: "beforeQueued", type: BuiltInSlotType.EVENT },
@@ -57,7 +49,7 @@ LiteGraph.registerNodeType({
type: "actions/queue_events"
})
export interface ComfyStoreImagesActionProperties extends Record<any, any> {
export interface ComfyStoreImagesActionProperties extends ComfyGraphNodeProperties {
images: GalleryOutput | null
}
@@ -96,13 +88,14 @@ LiteGraph.registerNodeType({
type: "actions/store_images"
})
export interface ComfyCopyActionProperties extends Record<any, any> {
export interface ComfyCopyActionProperties extends ComfyGraphNodeProperties {
value: any
}
export class ComfyCopyAction extends ComfyGraphNode {
override properties: ComfyCopyActionProperties = {
value: null
value: null,
tags: []
}
static slotLayout: SlotLayout = {
@@ -148,7 +141,7 @@ LiteGraph.registerNodeType({
type: "actions/copy"
})
export interface ComfySwapActionProperties extends Record<any, any> {
export interface ComfySwapActionProperties extends ComfyGraphNodeProperties {
}
export class ComfySwapAction extends ComfyGraphNode {
@@ -182,13 +175,14 @@ LiteGraph.registerNodeType({
type: "actions/swap"
})
export interface ComfyNotifyActionProperties extends Record<any, any> {
export interface ComfyNotifyActionProperties extends ComfyGraphNodeProperties {
message: string
}
export class ComfyNotifyAction extends ComfyGraphNode {
override properties: ComfyNotifyActionProperties = {
message: "Nya."
message: "Nya.",
tags: []
}
static slotLayout: SlotLayout = {
@@ -213,7 +207,7 @@ LiteGraph.registerNodeType({
type: "actions/notify"
})
export interface ComfyExecuteSubgraphActionProperties extends Record<any, any> {
export interface ComfyExecuteSubgraphActionProperties extends ComfyGraphNodeProperties {
tag: string | null,
}
@@ -253,7 +247,7 @@ LiteGraph.registerNodeType({
type: "actions/execute_subgraph"
})
export interface ComfySetNodeModeActionProperties extends Record<any, any> {
export interface ComfySetNodeModeActionProperties extends ComfyGraphNodeProperties {
targetTags: string,
enable: boolean,
}
@@ -261,7 +255,8 @@ export interface ComfySetNodeModeActionProperties extends Record<any, any> {
export class ComfySetNodeModeAction extends ComfyGraphNode {
override properties: ComfySetNodeModeActionProperties = {
targetTags: "",
enable: false
enable: false,
tags: []
}
static slotLayout: SlotLayout = {

View File

@@ -17,7 +17,15 @@ export type DefaultWidgetLayout = {
inputs?: Record<number, DefaultWidgetSpec>,
}
export interface ComfyGraphNodeProperties extends Record<string, any> {
tags: string[]
}
export default class ComfyGraphNode extends LGraphNode {
override properties: ComfyGraphNodeProperties = {
tags: []
}
isBackendNode?: boolean;
beforeQueued?(subgraph: string | null): void;
@@ -58,6 +66,11 @@ export default class ComfyGraphNode extends LGraphNode {
return null;
}
constructor(title?: string) {
super(title)
this.addProperty("tags", [], "array")
}
private inheritSlotTypes(type: LConnectionKind, isConnected: boolean) {
// Prevent multiple connections to different types when we have no input
if (isConnected && type === LConnectionKind.OUTPUT) {
@@ -261,6 +274,7 @@ export default class ComfyGraphNode extends LGraphNode {
comfyInput.defaultWidgetNode = widgetNode.class as any
}
}
this.saveUserState = (o as any).saveUserState;
if (this.saveUserState == null)
this.saveUserState = true

View File

@@ -1,8 +1,8 @@
import { BuiltInSlotType, LiteGraph, type ITextWidget, type SlotLayout, clamp, type PropertyLayout, type IComboWidget } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
import type { GalleryOutput } from "./ComfyWidgetNodes";
export interface ComfyImageCacheNodeProperties extends Record<any, any> {
export interface ComfyImageCacheNodeProperties extends ComfyGraphNodeProperties {
images: GalleryOutput | null,
index: number,
filenames: Record<number, { filename: string | null, status: ImageCacheState }>,
@@ -18,6 +18,7 @@ type ImageCacheState = "none" | "uploading" | "failed" | "cached"
*/
export default class ComfyImageCacheNode extends ComfyGraphNode {
override properties: ComfyImageCacheNodeProperties = {
tags: [],
images: null,
index: 0,
filenames: {},

View File

@@ -1,8 +1,8 @@
import { BuiltInSlotType, LiteGraph, NodeMode, type INodeInputSlot, type SlotLayout, type INodeOutputSlot, LLink, LConnectionKind, type ITextWidget } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
import { Watch } from "@litegraph-ts/nodes-basic";
export interface ComfyPickFirstNodeProperties extends Record<any, any> {
export interface ComfyPickFirstNodeProperties extends ComfyGraphNodeProperties {
acceptNullLinkData: boolean
}
@@ -19,6 +19,7 @@ function nextLetter(s: string): string {
export default class ComfyPickFirstNode extends ComfyGraphNode {
override properties: ComfyPickFirstNodeProperties = {
tags: [],
acceptNullLinkData: false
}
@@ -136,10 +137,12 @@ export default class ComfyPickFirstNode extends ComfyGraphNode {
if (this.isValidLink(link)) {
// Copy frontend-only inputs.
const node = this.getInputNode(index);
if (node != null && !(node as any).isBackendNode) {
if (node != null) {
this.selected = index;
if (!(node as any).isBackendNode) {
this.displayWidget.value = Watch.toString(link.data)
this.setOutputData(0, link.data)
}
return
}
}

View File

@@ -1,7 +1,7 @@
import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, NodeMode } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
export interface ComfyRerouteProperties extends Record<any, any> {
export interface ComfyRerouteProperties extends ComfyGraphNodeProperties {
showOutputText: boolean;
horizontal: boolean;
}
@@ -22,6 +22,7 @@ export default class ComfyReroute extends ComfyGraphNode {
override collapsable: boolean = false;
override properties: ComfyRerouteProperties = {
tags: [],
showOutputText: ComfyReroute.defaultVisibility,
horizontal: false
}

View File

@@ -1,12 +1,13 @@
import { BuiltInSlotType, LConnectionKind, LLink, LiteGraph, NodeMode, type INodeInputSlot, type SlotLayout, type INodeOutputSlot } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
export interface ComfySelectorProperties extends Record<any, any> {
export interface ComfySelectorProperties extends ComfyGraphNodeProperties {
value: any
}
export default class ComfySelector extends ComfyGraphNode {
override properties: ComfySelectorProperties = {
tags: [],
value: null
}
@@ -78,12 +79,13 @@ LiteGraph.registerNodeType({
type: "utils/selector"
})
export interface ComfySelectorTwoProperties extends Record<any, any> {
export interface ComfySelectorTwoProperties extends ComfyGraphNodeProperties {
value: any
}
export class ComfySelectorTwo extends ComfyGraphNode {
override properties: ComfySelectorTwoProperties = {
tags: [],
value: null
}

View File

@@ -1,10 +1,10 @@
import { BuiltInSlotType, LiteGraph, type SlotLayout } from "@litegraph-ts/core";
import ComfyGraphNode, { type DefaultWidgetLayout } from "./ComfyGraphNode";
import ComfyGraphNode, { type ComfyGraphNodeProperties, type DefaultWidgetLayout } from "./ComfyGraphNode";
import { clamp } from "$lib/utils";
import ComboWidget from "$lib/widgets/ComboWidget.svelte";
import { ComfyComboNode } from "./ComfyWidgetNodes";
export interface ComfyValueControlProperties extends Record<any, any> {
export interface ComfyValueControlProperties extends ComfyGraphNodeProperties {
value: any,
action: "fixed" | "increment" | "decrement" | "randomize",
min: number,
@@ -16,6 +16,7 @@ const INT_MAX = 1125899906842624;
export default class ComfyValueControl extends ComfyGraphNode {
override properties: ComfyValueControlProperties = {
tags: [],
value: null,
action: "fixed",
min: -INT_MAX,

View File

@@ -1,21 +1,34 @@
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, NodeMode } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
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, NodeMode, type INumberWidget } from "@litegraph-ts/core";
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
import type { SvelteComponentDev } from "svelte/internal";
import { Watch } from "@litegraph-ts/nodes-basic";
import type IComfyInputSlot from "$lib/IComfyInputSlot";
import { writable, type Unsubscriber, type Writable, get } from "svelte/store";
import { clamp, convertComfyOutputToGradio, range } from "$lib/utils"
import layoutState from "$lib/stores/layoutState";
import type { FileData as GradioFileData } from "@gradio/upload";
import queueState from "$lib/stores/queueState";
import ComboWidget from "$lib/widgets/ComboWidget.svelte";
import RangeWidget from "$lib/widgets/RangeWidget.svelte";
import TextWidget from "$lib/widgets/TextWidget.svelte";
import GalleryWidget from "$lib/widgets/GalleryWidget.svelte";
import ButtonWidget from "$lib/widgets/ButtonWidget.svelte";
import CheckboxWidget from "$lib/widgets/CheckboxWidget.svelte";
import type { SvelteComponentDev } from "svelte/internal";
import { Watch } from "@litegraph-ts/nodes-basic";
import type IComfyInputSlot from "$lib/IComfyInputSlot";
import { writable, type Unsubscriber, type Writable, get } from "svelte/store";
import { clamp, range } from "$lib/utils"
import layoutState from "$lib/stores/layoutState";
import type { FileData as GradioFileData } from "@gradio/upload";
import queueState from "$lib/stores/queueState";
import RadioWidget from "$lib/widgets/RadioWidget.svelte";
export interface ComfyWidgetProperties extends Record<string, any> {
/*
* NOTE: If you want to add a new widget but it has the same input/output type
* as another one of the existing widgets, best to create a new "variant" of
* that widget instead.
*
* - Go to layoutState, look for `ALL_ATTRIBUTES,` insert or find a "variant"
* attribute and set `validNodeTypes` to the type of the litegraph node
* - Add a new entry in the `values` array, like "knob" or "dial" for ComfySliderWidget
* - Add an {#if widget.attrs.variant === <...>} statement in the corresponding Svelte component
*/
export interface ComfyWidgetProperties extends ComfyGraphNodeProperties {
defaultValue: any
}
@@ -98,9 +111,9 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
this.notifyPropsChanged();
// Also need to notify the parent container since it's what controls the
// hidden state of the widget
const layoutEntry = layoutState.findLayoutEntryForNode(this.id)
if (layoutEntry && layoutEntry.parent)
layoutEntry.parent.attrsChanged.set(get(layoutEntry.parent.attrsChanged) + 1)
// const layoutEntry = layoutState.findLayoutEntryForNode(this.id)
// if (layoutEntry && layoutEntry.parent)
// layoutEntry.parent.attrsChanged.set(get(layoutEntry.parent.attrsChanged) + 1)
return result;
}
@@ -131,7 +144,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
/*
* Logic to run if this widget can be treated as output (slider, combo, text)
*/
override onExecute() {
override onExecute(param: any, options: object) {
if (this.copyFromInputLink) {
if (this.inputs.length >= this.inputIndex) {
const data = this.getInputData(this.inputIndex)
@@ -186,6 +199,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
}
notifyPropsChanged() {
console.debug("propsChanged", this)
this.propsChanged.set(get(this.propsChanged) + 1)
}
@@ -248,6 +262,7 @@ export interface ComfySliderProperties extends ComfyWidgetProperties {
export class ComfySliderNode extends ComfyWidgetNode<number> {
override properties: ComfySliderProperties = {
tags: [],
defaultValue: 0,
min: 0,
max: 10,
@@ -312,6 +327,7 @@ export interface ComfyComboProperties extends ComfyWidgetProperties {
export class ComfyComboNode extends ComfyWidgetNode<string> {
override properties: ComfyComboProperties = {
tags: [],
defaultValue: "A",
values: ["A", "B", "C", "D"]
}
@@ -404,6 +420,7 @@ export interface ComfyTextProperties extends ComfyWidgetProperties {
export class ComfyTextNode extends ComfyWidgetNode<string> {
override properties: ComfyTextProperties = {
tags: [],
defaultValue: "",
multiline: false
}
@@ -462,6 +479,7 @@ export interface ComfyGalleryProperties extends ComfyWidgetProperties {
export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
override properties: ComfyGalleryProperties = {
tags: [],
defaultValue: [],
index: 0,
updateMode: "replace"
@@ -514,7 +532,7 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
const data = param as GalleryOutput
console.debug("[ComfyGalleryNode] Received output!", data)
const galleryItems: GradioFileData[] = this.convertItems(data)
const galleryItems: GradioFileData[] = convertComfyOutputToGradio(data)
if (this.properties.updateMode === "append") {
const currentValue = get(this.value)
@@ -532,18 +550,6 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
return `Images: ${value?.length || 0}`
}
private convertItems(output: GalleryOutput): GradioFileData[] {
return output.images.map(r => {
// TODO configure backend URL
const url = `http://${location.hostname}:8188` // TODO make configurable
const params = new URLSearchParams(r)
return {
name: null,
data: url + "/view?" + params
}
});
}
override setValue(value: any) {
if (Array.isArray(value)) {
super.setValue(value)
@@ -572,6 +578,7 @@ export interface ComfyButtonProperties extends ComfyWidgetProperties {
export class ComfyButtonNode extends ComfyWidgetNode<boolean> {
override properties: ComfyButtonProperties = {
tags: [],
defaultValue: false,
param: "bang"
}
@@ -614,6 +621,7 @@ export interface ComfyCheckboxProperties extends ComfyWidgetProperties {
export class ComfyCheckboxNode extends ComfyWidgetNode<boolean> {
override properties: ComfyCheckboxProperties = {
tags: [],
defaultValue: false,
}
@@ -646,3 +654,63 @@ LiteGraph.registerNodeType({
desc: "Checkbox that stores a boolean value",
type: "ui/checkbox"
})
export interface ComfyRadioProperties extends ComfyWidgetProperties {
choices: string[]
}
export class ComfyRadioNode extends ComfyWidgetNode<string> {
override properties: ComfyRadioProperties = {
tags: [],
choices: ["Choice A", "Choice B", "Choice C"],
defaultValue: "Choice A",
}
static slotLayout: SlotLayout = {
outputs: [
{ name: "value", type: "string" },
{ name: "index", type: "number" },
{ name: "changed", type: BuiltInSlotType.EVENT },
]
}
override svelteComponentType = RadioWidget;
override defaultValue = "";
override changedIndex = 2;
indexWidget: INumberWidget;
index = 0;
constructor(name?: string) {
super(name, "Choice A")
this.indexWidget = this.addWidget("number", "Index", this.index)
this.indexWidget.disabled = true;
}
override onExecute(param: any, options: object) {
super.onExecute(param, options);
this.setOutputData(1, this.index)
}
override setValue(value: string) {
const index = this.properties.choices.indexOf(value)
if (index == -1)
return;
this.index = index;
this.indexWidget.value = index;
const changed = value != get(this.value);
super.setValue(value)
if (changed)
this.triggerSlot(2, { value: value, index: index })
}
}
LiteGraph.registerNodeType({
class: ComfyRadioNode,
title: "UI.Radio",
desc: "Radio that outputs a string and index",
type: "ui/radio"
})

View File

@@ -394,6 +394,18 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
values: ["large", "small"],
defaultValue: "large"
},
// Gallery
{
name: "variant",
type: "enum",
location: "widget",
editable: true,
validNodeTypes: ["ui/gallery"],
values: ["gallery", "image"],
defaultValue: "gallery",
refreshPanelOnChange: true
},
]
},
{
@@ -483,7 +495,7 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
defaultValue: "bang"
},
// gallery
// Gallery
{
name: "updateMode",
type: "enum",
@@ -494,6 +506,18 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
defaultValue: "replace"
},
// Radio
{
name: "choices",
type: "string",
location: "nodeProps",
editable: true,
validNodeTypes: ["ui/radio"],
defaultValue: ["Choice A", "Choice B", "Choice C"],
serialize: serializeStringArray,
deserialize: deserializeStringArray,
},
// Workflow
{
name: "defaultSubgraph",

View File

@@ -6,6 +6,7 @@ import { get } from "svelte/store"
import layoutState from "$lib/stores/layoutState"
import type { SvelteComponentDev } from "svelte/internal";
import type { SerializedLGraph } from "@litegraph-ts/core";
import type { GalleryOutput } from "./nodes/ComfyWidgetNodes";
export function clamp(n: number, min: number, max: number): number {
return Math.min(Math.max(n, min), max)
@@ -91,7 +92,7 @@ export function promptToGraphVis(prompt: SerializedPrompt): string {
}
else {
// Value
out += `"${id}-${inpName}-${typeof i}" -> "${outNode.title}"\n`
out += `"${id}-${inpName}-${i}" -> "${outNode.title}"\n`
}
}
}
@@ -118,3 +119,15 @@ export const debounce = (callback: Function, wait = 250) => {
timeout = setTimeout(next, wait);
};
};
export function convertComfyOutputToGradio(output: GalleryOutput): GradioFileData[] {
return output.images.map(r => {
// TODO configure backend URL
const url = `http://${location.hostname}:8188` // TODO make configurable
const params = new URLSearchParams(r)
return {
name: null,
data: url + "/view?" + params
}
});
}

View File

@@ -32,7 +32,12 @@
{#key $attrsChanged}
{#if node !== null}
<Block>
<Checkbox disabled={isDisabled(widget)} label={widget.attrs.title} bind:value={$nodeValue} on:select={onSelect} />
<Checkbox
disabled={isDisabled(widget)}
label={widget.attrs.title}
bind:value={$nodeValue}
on:select={onSelect}
/>
</Block>
{/if}
{/key}

View File

@@ -136,7 +136,9 @@
:global(.svelte-select) {
width: auto;
max-width: 30rem;
max-width: 16rem;
--font-size: 13px;
--height: 32px;
}
:global(.svelte-select-list) {

View File

@@ -1,7 +1,8 @@
<script lang="ts">
import { ImageViewer } from "$lib/ImageViewer";
import { Block } from "@gradio/atoms";
import { Block, BlockLabel, Empty } from "@gradio/atoms";
import { Gallery } from "@gradio/gallery";
import { Image } from "@gradio/icons";
import type { Styles } from "@gradio/utils";
import type { WidgetLayout } from "$lib/stores/layoutState";
import type { Writable } from "svelte/store";
@@ -123,8 +124,26 @@
}
</script>
<div class="wrapper comfy-gallery-widget gradio-gallery" bind:this={element}>
{#if widget && node && nodeValue && $nodeValue != null}
{#if widget.attrs.variant === "image"}
<div class="wrapper comfy-image-widget" bind:this={element}>
<Block variant="solid" padding={false}>
{#if widget.attrs.title}
<BlockLabel
show_label={true}
Icon={Image}
label={widget.attrs.title || "Image"}
/>
{/if}
{#if $nodeValue.length > 0}
<img src={$nodeValue[$nodeValue.length-1].data}/>
{:else}
<Empty size="large" unpadded_box={true}><Image /></Empty>
{/if}
</Block>
</div>
{:else}
<div class="wrapper comfy-gallery-widget gradio-gallery" bind:this={element}>
<Block variant="solid" padding={false}>
<div class="padding">
<Gallery
@@ -138,8 +157,9 @@
/>
</div>
</Block>
{/if}
</div>
{/if}
{/if}
<style lang="scss">
.wrapper {
@@ -148,13 +168,28 @@
:global(> .block) {
border-radius: 0px !important;
}
:global(button.thumbnail-lg) {
width: var(--size-32);
}
&.comfy-image-widget {
aspect-ratio: 1/1;
:global(> .block) {
height: 100%;
:global(img) {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
}
.padding {
height: 30rem;
}
.wrapper :global(button.thumbnail-lg) {
width: var(--size-32);
}
</style>

View File

@@ -0,0 +1,64 @@
<script lang="ts">
import type { ComfyRadioNode } from "$lib/nodes/ComfyWidgetNodes";
import { type WidgetLayout } from "$lib/stores/layoutState";
import { Block } from "@gradio/atoms";
import { Radio } from "@gradio/form";
import { get, type Writable, writable } from "svelte/store";
import { isDisabled } from "./utils"
import type { SelectData } from "@gradio/utils";
import { clamp } from "$lib/utils";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyRadioNode | null = null;
let nodeValue: Writable<string> | null = null;
let propsChanged: Writable<number> | null = null;
let attrsChanged: Writable<number> | null = null;
$: widget && setNodeValue(widget);
function setNodeValue(widget: WidgetLayout) {
if (widget) {
node = widget.node as ComfyRadioNode
nodeValue = node.value;
attrsChanged = widget.attrsChanged;
}
};
$: node && $propsChanged && clampIndex();
function clampIndex() {
node.index = clamp(node.index, 0, node.properties.choices?.length || 0)
}
function onSelect(e: CustomEvent<SelectData>) {
$nodeValue = e.detail.value
node.setValue($nodeValue)
node.index = e.detail.index as number
navigator.vibrate(20)
}
</script>
<div class="wrapper gradio-checkbox">
<div class="inner">
{#key $propsChanged}
{#key $attrsChanged}
{#if node !== null && node.properties.choices}
<Block>
<Radio
elem_id="radio"
choices={node.properties.choices}
disabled={isDisabled(widget)}
label={widget.attrs.title}
value={$nodeValue}
on:select={onSelect}
/>
</Block>
{/if}
{/key}
{/key}
</div>
</div>
<style lang="scss">
</style>