Toggle nodes/containers on and off by tags
This commit is contained in:
Submodule litegraph updated: fd575bf9a2...e7df2b2b75
@@ -47,6 +47,7 @@
|
||||
"@litegraph-ts/core": "workspace:*",
|
||||
"@litegraph-ts/nodes-basic": "workspace:*",
|
||||
"@litegraph-ts/nodes-events": "workspace:*",
|
||||
"@litegraph-ts/nodes-logic": "workspace:*",
|
||||
"@litegraph-ts/nodes-math": "workspace:*",
|
||||
"@litegraph-ts/nodes-strings": "workspace:*",
|
||||
"@litegraph-ts/tsconfig": "workspace:*",
|
||||
|
||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -46,6 +46,9 @@ importers:
|
||||
'@litegraph-ts/nodes-events':
|
||||
specifier: workspace:*
|
||||
version: link:litegraph/packages/nodes-events
|
||||
'@litegraph-ts/nodes-logic':
|
||||
specifier: workspace:*
|
||||
version: link:litegraph/packages/nodes-logic
|
||||
'@litegraph-ts/nodes-math':
|
||||
specifier: workspace:*
|
||||
version: link:litegraph/packages/nodes-math
|
||||
@@ -806,6 +809,22 @@ importers:
|
||||
specifier: ^4.2.1
|
||||
version: 4.3.1
|
||||
|
||||
litegraph/packages/nodes-logic:
|
||||
dependencies:
|
||||
'@litegraph-ts/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
devDependencies:
|
||||
'@litegraph-ts/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:../tsconfig
|
||||
typescript:
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3
|
||||
vite:
|
||||
specifier: ^4.2.1
|
||||
version: 4.3.1
|
||||
|
||||
litegraph/packages/nodes-math:
|
||||
dependencies:
|
||||
'@litegraph-ts/core':
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
import GraphPage from './mobile/routes/graph.svelte';
|
||||
import ListSubWorkflowsPage from './mobile/routes/list-subworkflows.svelte';
|
||||
import SubWorkflowPage from './mobile/routes/subworkflow.svelte';
|
||||
import HellPage from './mobile/routes/hell.svelte';
|
||||
import type { Framework7Parameters } from "framework7/types";
|
||||
import type { Framework7Parameters } from "framework7/types";
|
||||
|
||||
export let app: ComfyApp;
|
||||
|
||||
@@ -83,13 +82,6 @@
|
||||
props: { app }
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/hell/',
|
||||
component: HellPage,
|
||||
options: {
|
||||
props: { app }
|
||||
}
|
||||
},
|
||||
],
|
||||
popup: {
|
||||
closeOnEscape: true,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||
import { startDrag, stopDrag } from "$lib/utils"
|
||||
import type { Writable } from "svelte/store";
|
||||
import { isHidden } from "$lib/widgets/utils";
|
||||
|
||||
export let container: ContainerLayout | null = null;
|
||||
export let zIndex: number = 0;
|
||||
@@ -73,7 +74,7 @@
|
||||
on:finalize="{handleFinalize}"
|
||||
>
|
||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
||||
{@const hidden = item?.attrs?.hidden}
|
||||
{@const hidden = isHidden(item)}
|
||||
<div class="animation-wrapper"
|
||||
class:hidden={hidden}
|
||||
animate:flip={{duration:flipDurationMs}}
|
||||
@@ -86,7 +87,7 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if container.attrs.hidden && edit}
|
||||
{#if isHidden(container) && edit}
|
||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||
{/if}
|
||||
{#if showHandles}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||
import { startDrag, stopDrag } from "$lib/utils"
|
||||
import type { Writable } from "svelte/store";
|
||||
import { isHidden } from "$lib/widgets/utils";
|
||||
|
||||
export let container: ContainerLayout | null = null;
|
||||
export let zIndex: number = 0;
|
||||
@@ -75,7 +76,7 @@
|
||||
on:finalize="{handleFinalize}"
|
||||
>
|
||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
||||
{@const hidden = item?.attrs?.hidden}
|
||||
{@const hidden = isHidden(item)}
|
||||
<div class="animation-wrapper"
|
||||
class:hidden={hidden}
|
||||
animate:flip={{duration:flipDurationMs}}
|
||||
@@ -88,7 +89,7 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if container.attrs.hidden && edit}
|
||||
{#if isHidden(container) && edit}
|
||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||
{/if}
|
||||
{#if showHandles}
|
||||
@@ -239,6 +240,10 @@
|
||||
flex-grow: 100;
|
||||
}
|
||||
|
||||
.handle-hidden {
|
||||
background-color: #40404080;
|
||||
}
|
||||
|
||||
.handle-widget:hover {
|
||||
background-color: #add8e680;
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
})
|
||||
|
||||
async function doRefreshCombos() {
|
||||
await app.refreshComboInNodes()
|
||||
await app.refreshComboInNodes(true)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import type TypedEmitter from "typed-emitter";
|
||||
// Import nodes
|
||||
import "@litegraph-ts/nodes-basic"
|
||||
import "@litegraph-ts/nodes-events"
|
||||
import "@litegraph-ts/nodes-logic"
|
||||
import "@litegraph-ts/nodes-math"
|
||||
import "@litegraph-ts/nodes-strings"
|
||||
import "$lib/nodes/index"
|
||||
@@ -70,6 +71,27 @@ export type Progress = {
|
||||
max: number
|
||||
}
|
||||
|
||||
function isActiveBackendNode(node: ComfyGraphNode, tag: string | null): boolean {
|
||||
if (!node.isBackendNode)
|
||||
return false;
|
||||
|
||||
if (tag && !hasTag(node, tag)) {
|
||||
console.debug("Skipping tagged node", tag, node.properties.tags, node)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.mode === NodeMode.NEVER) {
|
||||
// Don't serialize muted nodes
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function hasTag(node: LGraphNode, tag: string): boolean {
|
||||
return "tags" in node.properties && node.properties.tags.indexOf(tag) !== -1
|
||||
}
|
||||
|
||||
export default class ComfyApp {
|
||||
api: ComfyAPI;
|
||||
rootEl: HTMLDivElement | null = null;
|
||||
@@ -443,23 +465,12 @@ export default class ComfyApp {
|
||||
for (const node_ of this.lGraph.computeExecutionOrder<ComfyGraphNode>(false, null)) {
|
||||
const n = workflow.nodes.find((n) => n.id === node_.id);
|
||||
|
||||
if (!node_.isBackendNode) {
|
||||
// console.debug("Not serializing node: ", node_.type)
|
||||
if (!isActiveBackendNode(node_, tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const node = node_ as ComfyBackendNode;
|
||||
|
||||
if (tag && node.tags.indexOf(tag) === -1) {
|
||||
console.debug("Skipping tagged node", tag, node.tags)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.mode === NodeMode.NEVER) {
|
||||
// Don't serialize muted nodes
|
||||
continue;
|
||||
}
|
||||
|
||||
const inputs = {};
|
||||
|
||||
// Store all link values
|
||||
@@ -469,7 +480,11 @@ export default class ComfyApp {
|
||||
const inputLink = node.getInputLink(i)
|
||||
const inputNode = node.getInputNode(i)
|
||||
|
||||
if (inputNode && tag && "tags" in inputNode && (inputNode.tags as string[]).indexOf(tag) === -1) {
|
||||
// We don't check tags for non-backend nodes.
|
||||
// Just check for node inactivity (so you can toggle groups of
|
||||
// tagged frontend nodes on/off)
|
||||
if (inputNode && inputNode.mode === NodeMode.NEVER) {
|
||||
console.debug("Skipping inactive node", inputNode)
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -515,7 +530,7 @@ export default class ComfyApp {
|
||||
const isValidParent = (parent: ComfyGraphNode) => {
|
||||
if (!parent || parent.isBackendNode)
|
||||
return false;
|
||||
if ("tags" in parent && (parent.tags as string[]).indexOf(tag) === -1)
|
||||
if (tag && !hasTag(parent, tag))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -525,8 +540,8 @@ export default class ComfyApp {
|
||||
if (link && !seen[link.id]) {
|
||||
seen[link.id] = true
|
||||
const inputNode = parent.getInputNode(link.origin_slot) as ComfyGraphNode;
|
||||
if (inputNode && "tags" in inputNode && tag && (inputNode.tags as string[]).indexOf(tag) === -1) {
|
||||
console.debug("Skipping tagged parent node", tag, node.tags)
|
||||
if (inputNode && tag && !hasTag(inputNode, tag)) {
|
||||
console.debug("Skipping tagged parent node", tag, node.properties.tags)
|
||||
parent = null;
|
||||
}
|
||||
else {
|
||||
@@ -538,7 +553,7 @@ export default class ComfyApp {
|
||||
}
|
||||
|
||||
if (link && parent && parent.isBackendNode) {
|
||||
if ("tags" in parent && tag && (parent.tags as string[]).indexOf(tag) === -1)
|
||||
if (tag && !hasTag(parent, tag))
|
||||
continue;
|
||||
|
||||
const input = node.inputs[i]
|
||||
@@ -669,7 +684,7 @@ export default class ComfyApp {
|
||||
/**
|
||||
* Refresh combo list on whole nodes
|
||||
*/
|
||||
async refreshComboInNodes() {
|
||||
async refreshComboInNodes(flashUI: boolean = false) {
|
||||
const defs = await this.api.getNodeDefs();
|
||||
|
||||
for (let nodeNum in this.lGraph._nodes) {
|
||||
@@ -687,11 +702,13 @@ export default class ComfyApp {
|
||||
const inputNode = node.getInputNode(index)
|
||||
|
||||
if (inputNode && "doAutoConfig" in inputNode) {
|
||||
const comfyInputNode = inputNode as nodes.ComfyWidgetNode;
|
||||
comfyInputNode.doAutoConfig(comfyInput)
|
||||
if (!comfyInput.config.values.includes(get(comfyInputNode.value))) {
|
||||
comfyInputNode.setValue(comfyInput.config.defaultValue || comfyInput.config.values[0])
|
||||
const comfyComboNode = inputNode as nodes.ComfyComboNode;
|
||||
comfyComboNode.doAutoConfig(comfyInput)
|
||||
if (!comfyInput.config.values.includes(get(comfyComboNode.value))) {
|
||||
comfyComboNode.setValue(comfyInput.config.defaultValue || comfyInput.config.values[0])
|
||||
}
|
||||
if (flashUI)
|
||||
comfyComboNode.comboRefreshed.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
value = spec.deserialize(value)
|
||||
|
||||
target.attrs[name] = value
|
||||
target.attrsChanged.set(!get(target.attrsChanged))
|
||||
target.attrsChanged.set(get(target.attrsChanged) + 1)
|
||||
|
||||
if (node && "propsChanged" in node) {
|
||||
const comfyNode = node as ComfyWidgetNode
|
||||
@@ -151,27 +151,39 @@
|
||||
|
||||
console.warn(spec)
|
||||
if (spec.refreshPanelOnChange) {
|
||||
console.error("A! refresh")
|
||||
$refreshPanel += 1;
|
||||
doRefreshPanel()
|
||||
}
|
||||
}
|
||||
|
||||
function getProperty(node: LGraphNode, spec: AttributesSpec) {
|
||||
let value = node.properties[spec.name]
|
||||
if (value == null)
|
||||
value = spec.defaultValue
|
||||
else if (spec.serialize)
|
||||
value = spec.serialize(value)
|
||||
console.debug("[ComfyProperties] getProperty", spec, value, node)
|
||||
return value
|
||||
}
|
||||
|
||||
function updateProperty(spec: AttributesSpec, value: any) {
|
||||
if (node == null || !spec.editable)
|
||||
return
|
||||
|
||||
const name = spec.name
|
||||
console.warn("updateProperty", name, value)
|
||||
console.warn("[ComfyProperties] updateProperty", name, value)
|
||||
|
||||
if (spec.deserialize)
|
||||
value = spec.deserialize(value)
|
||||
|
||||
node.properties[name] = value;
|
||||
|
||||
if ("propsChanged" in node) {
|
||||
const comfyNode = node as ComfyWidgetNode
|
||||
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
|
||||
comfyNode.notifyPropsChanged();
|
||||
}
|
||||
|
||||
if (spec.refreshPanelOnChange)
|
||||
$refreshPanel += 1;
|
||||
doRefreshPanel()
|
||||
}
|
||||
|
||||
function getVar(node: LGraphNode, spec: AttributesSpec) {
|
||||
@@ -201,8 +213,14 @@
|
||||
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
|
||||
}
|
||||
|
||||
if (spec.refreshPanelOnChange)
|
||||
$refreshPanel += 1;
|
||||
if (spec.refreshPanelOnChange) {
|
||||
doRefreshPanel()
|
||||
}
|
||||
}
|
||||
|
||||
function doRefreshPanel() {
|
||||
console.warn("[ComfyProperties] doRefreshPanel")
|
||||
$refreshPanel += 1;
|
||||
}
|
||||
|
||||
function updateWorkflowAttribute(spec: AttributesSpec, value: any) {
|
||||
@@ -214,6 +232,9 @@
|
||||
|
||||
$layoutState.attrs[name] = value
|
||||
$layoutState = $layoutState
|
||||
|
||||
if (spec.refreshPanelOnChange)
|
||||
doRefreshPanel()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -281,7 +302,7 @@
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={node.properties[spec.name] || spec.defaultValue}
|
||||
value={getProperty(node, spec)}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
on:input={(e) => updateProperty(spec, e.detail)}
|
||||
label={spec.name}
|
||||
@@ -290,7 +311,7 @@
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={node.properties[spec.name] || spec.defaultValue}
|
||||
value={getProperty(node, spec)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
@@ -298,7 +319,7 @@
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={node.properties[spec.name] || spec.defaultValue}
|
||||
value={getProperty(node, spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
@@ -308,7 +329,7 @@
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
name={spec.name}
|
||||
value={node.properties[spec.name] || spec.defaultValue}
|
||||
value={getProperty(node, spec)}
|
||||
values={spec.values}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<span>Node: {getNodeInfo($queueState.runningNodeId)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<ProgressBar value={$queueState.progress?.value} max={$queueState.progress?.max} />
|
||||
<ProgressBar value={$queueState.progress?.value} max={$queueState.progress?.max} styles="height: 30px;" />
|
||||
</div>
|
||||
{/if}
|
||||
{#if typeof $queueState.queueRemaining === "number" && $queueState.queueRemaining > 0}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
import { flip } from 'svelte/animate';
|
||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||
import { startDrag, stopDrag } from "$lib/utils"
|
||||
import type { Writable } from "svelte/store";
|
||||
import { isHidden } from "$lib/widgets/utils";
|
||||
|
||||
export let container: ContainerLayout | null = null;
|
||||
export let zIndex: number = 0;
|
||||
@@ -34,12 +36,14 @@
|
||||
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
|
||||
{@const dragDisabled = zIndex === 0 || $layoutState.currentSelection.length > 2 || !$uiState.uiUnlocked}
|
||||
{#key $attrsChanged}
|
||||
{#if container.attrs.variant === "tabs"}
|
||||
<TabsContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||
{:else if container.attrs.variant === "accordion"}
|
||||
<AccordionContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||
{:else}
|
||||
<BlockContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||
{#if edit || !isHidden(container)}
|
||||
{#if container.attrs.variant === "tabs"}
|
||||
<TabsContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||
{:else if container.attrs.variant === "accordion"}
|
||||
<AccordionContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||
{:else}
|
||||
<BlockContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||
{/if}
|
||||
{/if}
|
||||
{/key}
|
||||
{/if}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||
import { startDrag, stopDrag } from "$lib/utils"
|
||||
import type { Writable } from "svelte/store";
|
||||
import { isHidden } from "$lib/widgets/utils";
|
||||
|
||||
export let container: ContainerLayout | null = null;
|
||||
export let zIndex: number = 0;
|
||||
@@ -86,7 +87,7 @@
|
||||
on:finalize="{handleFinalize}"
|
||||
>
|
||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item, i(item.id)}
|
||||
{@const hidden = item?.attrs?.hidden}
|
||||
{@const hidden = isHidden(item)}
|
||||
{@const tabName = getTabName(container, i)}
|
||||
<div class="animation-wrapper"
|
||||
class:hidden={hidden}
|
||||
@@ -104,7 +105,7 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if container.attrs.hidden && edit}
|
||||
{#if isHidden(container) && edit}
|
||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||
{/if}
|
||||
{#if showHandles}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import Container from "./Container.svelte"
|
||||
import { type Writable } from "svelte/store"
|
||||
import type { ComfyWidgetNode } from "$lib/nodes";
|
||||
import { NodeMode } from "@litegraph-ts/core";
|
||||
import { isHidden } from "$lib/widgets/utils";
|
||||
|
||||
export let dragItem: IDragItem | null = null;
|
||||
export let zIndex: number = 0;
|
||||
@@ -64,17 +66,18 @@
|
||||
{/key}
|
||||
{:else if widget && widget.node}
|
||||
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
|
||||
{@const hidden = isHidden(widget)}
|
||||
{#key $attrsChanged}
|
||||
{#key $propsChanged}
|
||||
<div class="widget {widget.attrs.classes} {getWidgetClass()}"
|
||||
class:edit={edit}
|
||||
class:selected={$uiState.uiUnlocked && $layoutState.currentSelection.includes(widget.id)}
|
||||
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id}
|
||||
class:hidden={widget.attrs.hidden}
|
||||
class:hidden={hidden}
|
||||
>
|
||||
<svelte:component this={widget.node.svelteComponentType} {widget} {isMobile} />
|
||||
</div>
|
||||
{#if widget.attrs.hidden && edit}
|
||||
{#if hidden && edit}
|
||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||
{/if}
|
||||
{#if showHandles}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, BuiltInSlotType, type ITextWidget, type SerializedLGraphNode } from "@litegraph-ts/core";
|
||||
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";
|
||||
@@ -7,6 +7,7 @@ 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> {
|
||||
}
|
||||
@@ -251,3 +252,86 @@ LiteGraph.registerNodeType({
|
||||
desc: "Runs a part of the graph based on a tag",
|
||||
type: "actions/execute_subgraph"
|
||||
})
|
||||
|
||||
export interface ComfySetNodeModeActionProperties extends Record<any, any> {
|
||||
targetTags: string,
|
||||
enable: boolean,
|
||||
}
|
||||
|
||||
export class ComfySetNodeModeAction extends ComfyGraphNode {
|
||||
override properties: ComfySetNodeModeActionProperties = {
|
||||
targetTags: "",
|
||||
enable: false
|
||||
}
|
||||
|
||||
static slotLayout: SlotLayout = {
|
||||
inputs: [
|
||||
{ name: "enabled", type: "boolean" },
|
||||
{ name: "set", type: BuiltInSlotType.ACTION },
|
||||
],
|
||||
}
|
||||
|
||||
displayWidget: ITextWidget;
|
||||
enableWidget: IToggleWidget;
|
||||
|
||||
constructor(title?: string) {
|
||||
super(title)
|
||||
this.displayWidget = this.addWidget("text", "Tags", this.properties.targetTags, "targetTags")
|
||||
this.enableWidget = this.addWidget("toggle", "Enable", this.properties.enable, "enable")
|
||||
}
|
||||
|
||||
override onPropertyChanged(property: any, value: any) {
|
||||
if (property === "enabled") {
|
||||
this.enableWidget.value = value
|
||||
}
|
||||
}
|
||||
|
||||
override onExecute() {
|
||||
const enabled = this.getInputData(0)
|
||||
if (typeof enabled === "boolean")
|
||||
this.setProperty("enabled", enabled)
|
||||
}
|
||||
|
||||
override onAction(action: any, param: any) {
|
||||
let enabled = this.properties.enabled
|
||||
if (typeof param === "object" && "enabled" in param)
|
||||
enabled = param["enabled"]
|
||||
|
||||
const tags = this.properties.targetTags.split(",").map(s => s.trim());
|
||||
|
||||
for (const node of this.graph._nodes) {
|
||||
if ("tags" in node.properties) {
|
||||
const comfyNode = node as ComfyGraphNode;
|
||||
const hasTag = tags.some(t => comfyNode.properties.tags.indexOf(t) != -1);
|
||||
if (hasTag) {
|
||||
let newMode: NodeMode;
|
||||
if (enabled) {
|
||||
newMode = NodeMode.ALWAYS;
|
||||
} else {
|
||||
newMode = NodeMode.NEVER;
|
||||
}
|
||||
node.changeMode(newMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of Object.values(get(layoutState).allItems)) {
|
||||
if (entry.dragItem.type === "container") {
|
||||
const container = entry.dragItem;
|
||||
const hasTag = tags.some(t => container.attrs.tags.indexOf(t) != -1);
|
||||
if (hasTag) {
|
||||
container.attrs.hidden = !enabled;
|
||||
console.warn("Cont", container.attrs.tags, tags, hasTag, container.attrs, enabled)
|
||||
}
|
||||
container.attrsChanged.set(get(container.attrsChanged) + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType({
|
||||
class: ComfySetNodeModeAction,
|
||||
title: "Comfy.SetNodeModeAction",
|
||||
desc: "Sets a group of nodes/UI containers as enabled/disabled based on their tags (comma-separated)",
|
||||
type: "actions/set_node_mode"
|
||||
})
|
||||
|
||||
@@ -32,12 +32,6 @@ export class ComfyBackendNode extends ComfyGraphNode {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tags this node belongs to
|
||||
* Allows you to run subsections of the graph
|
||||
*/
|
||||
tags: string[] = []
|
||||
|
||||
private static defaultInputConfigs: Record<string, Record<string, ComfyInputConfig>> = {}
|
||||
|
||||
private setup(nodeData: any) {
|
||||
@@ -88,7 +82,6 @@ export class ComfyBackendNode extends ComfyGraphNode {
|
||||
|
||||
override onSerialize(o: SerializedLGraphNode) {
|
||||
super.onSerialize(o);
|
||||
(o as any).tags = this.tags
|
||||
for (const input of o.inputs) {
|
||||
// strip user-identifying data, it will be reinstantiated later
|
||||
if ((input as any).config != null) {
|
||||
@@ -100,8 +93,6 @@ export class ComfyBackendNode extends ComfyGraphNode {
|
||||
override onConfigure(o: SerializedLGraphNode) {
|
||||
super.onConfigure(o);
|
||||
|
||||
this.tags = (o as any).tags || []
|
||||
|
||||
const configs = ComfyBackendNode.defaultInputConfigs[o.type]
|
||||
for (let index = 0; index < this.inputs.length; index++) {
|
||||
const input = this.inputs[index] as IComfyInputSlot
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ComfyInputConfig } from "$lib/IComfyInputSlot";
|
||||
import type { SerializedPrompt } from "$lib/components/ComfyApp";
|
||||
import type ComfyWidget from "$lib/components/widgets/ComfyWidget";
|
||||
import { LGraph, LGraphNode, LiteGraph, type SerializedLGraphNode, type Vector2 } from "@litegraph-ts/core";
|
||||
import { LGraph, LGraphNode, LiteGraph, NodeMode, type SerializedLGraphNode, type Vector2 } from "@litegraph-ts/core";
|
||||
import type { SvelteComponentDev } from "svelte/internal";
|
||||
import type { ComfyWidgetNode } from "./ComfyWidgetNodes";
|
||||
import type IComfyInputSlot from "$lib/IComfyInputSlot";
|
||||
|
||||
124
src/lib/nodes/ComfyPickFirstNode.ts
Normal file
124
src/lib/nodes/ComfyPickFirstNode.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { BuiltInSlotType, LiteGraph, NodeMode, type INodeInputSlot, type SlotLayout, type INodeOutputSlot, LLink, LConnectionKind, type ITextWidget } from "@litegraph-ts/core";
|
||||
import ComfyGraphNode from "./ComfyGraphNode";
|
||||
import { Watch } from "@litegraph-ts/nodes-basic";
|
||||
|
||||
export interface ComfyPickFirstNodeProperties extends Record<any, any> {
|
||||
acceptNullLinkData: boolean
|
||||
}
|
||||
|
||||
function nextLetter(s: string): string {
|
||||
return s.replace(/([a-zA-Z])[^a-zA-Z]*$/, function(a) {
|
||||
var c = a.charCodeAt(0);
|
||||
switch (c) {
|
||||
case 90: return 'A';
|
||||
case 122: return 'a';
|
||||
default: return String.fromCharCode(++c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default class ComfyPickFirstNode extends ComfyGraphNode {
|
||||
override properties: ComfyPickFirstNodeProperties = {
|
||||
acceptNullLinkData: false
|
||||
}
|
||||
|
||||
static slotLayout: SlotLayout = {
|
||||
inputs: [
|
||||
{ name: "A", type: "*" },
|
||||
{ name: "B", type: "*" },
|
||||
],
|
||||
outputs: [
|
||||
{ name: "out", type: "*" }
|
||||
],
|
||||
}
|
||||
|
||||
private selected: number = -1;
|
||||
|
||||
displayWidget: ITextWidget;
|
||||
|
||||
constructor(title?: string) {
|
||||
super(title);
|
||||
this.displayWidget = this.addWidget("text", "Value", "")
|
||||
this.displayWidget.disabled = true;
|
||||
}
|
||||
|
||||
override onDrawBackground(ctx: CanvasRenderingContext2D) {
|
||||
if (this.flags.collapsed || this.selected === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.fillStyle = "#AFB";
|
||||
var y = (this.selected) * LiteGraph.NODE_SLOT_HEIGHT + 6;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(50, y);
|
||||
ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT);
|
||||
ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5);
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
override onConnectionsChange(
|
||||
type: LConnectionKind,
|
||||
slotIndex: number,
|
||||
isConnected: boolean,
|
||||
link: LLink,
|
||||
ioSlot: (INodeInputSlot | INodeOutputSlot)
|
||||
) {
|
||||
if (type !== LConnectionKind.INPUT)
|
||||
return;
|
||||
|
||||
if (isConnected) {
|
||||
if (link != null && slotIndex === this.inputs.length - 1) {
|
||||
// Add a new input
|
||||
const lastInputName = this.inputs[this.inputs.length - 1].name
|
||||
const inputName = nextLetter(lastInputName);
|
||||
this.addInput(inputName, "*")
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.getInputLink(this.inputs.length - 1) != null)
|
||||
return;
|
||||
|
||||
// Remove empty inputs
|
||||
for (let i = this.inputs.length - 2; i > 0; i--) {
|
||||
if (i <= 0)
|
||||
break;
|
||||
|
||||
if (this.getInputLink(i) == null)
|
||||
this.removeInput(i)
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
let name = "A"
|
||||
for (let i = 0; i < this.inputs.length; i++) {
|
||||
this.inputs[i].name = name;
|
||||
name = nextLetter(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override onExecute() {
|
||||
for (let index = 0; index < this.inputs.length; index++) {
|
||||
const link = this.getInputLink(index);
|
||||
if (link != null && (link.data != null || this.properties.acceptNullLinkData)) {
|
||||
const node = this.getInputNode(index);
|
||||
if (node != null && node.mode === NodeMode.ALWAYS) {
|
||||
this.selected = index;
|
||||
this.displayWidget.value = Watch.toString(link.data)
|
||||
this.setOutputData(0, link.data)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.selected = -1;
|
||||
this.setOutputData(0, null)
|
||||
}
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType({
|
||||
class: ComfyPickFirstNode,
|
||||
title: "Comfy.PickFirst",
|
||||
desc: "Picks the first active input connected to this node (top to bottom)",
|
||||
type: "utils/pick_first"
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
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 { 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 ComboWidget from "$lib/widgets/ComboWidget.svelte";
|
||||
import RangeWidget from "$lib/widgets/RangeWidget.svelte";
|
||||
@@ -93,6 +93,17 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
||||
return Watch.toString(value)
|
||||
}
|
||||
|
||||
override changeMode(modeTo: NodeMode): boolean {
|
||||
const result = super.changeMode(modeTo);
|
||||
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)
|
||||
return result;
|
||||
}
|
||||
|
||||
private onValueUpdated(value: any) {
|
||||
console.debug("[Widget] valueUpdated", this, value)
|
||||
this.displayWidget.value = this.formatValue(value)
|
||||
@@ -171,6 +182,10 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
||||
console.debug("Property copy", input, this.properties)
|
||||
|
||||
this.setValue(get(this.value))
|
||||
this.notifyPropsChanged();
|
||||
}
|
||||
|
||||
notifyPropsChanged() {
|
||||
this.propsChanged.set(get(this.propsChanged) + 1)
|
||||
}
|
||||
|
||||
@@ -200,7 +215,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
||||
}
|
||||
|
||||
// Force reactivity change so the frontend can be updated with the new props
|
||||
this.propsChanged.set(get(this.propsChanged) + 1)
|
||||
this.notifyPropsChanged();
|
||||
}
|
||||
|
||||
clampOneConfig(input: IComfyInputSlot) { }
|
||||
@@ -314,8 +329,11 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
|
||||
override svelteComponentType = ComboWidget
|
||||
override defaultValue = "A";
|
||||
|
||||
comboRefreshed: Writable<boolean>;
|
||||
|
||||
constructor(name?: string) {
|
||||
super(name, "A")
|
||||
this.comboRefreshed = writable(false)
|
||||
}
|
||||
|
||||
onConnectOutput(
|
||||
@@ -613,7 +631,7 @@ export class ComfyCheckboxNode extends ComfyWidgetNode<boolean> {
|
||||
const changed = value != get(this.value);
|
||||
super.setValue(Boolean(value))
|
||||
if (changed)
|
||||
this.triggerSlot(1)
|
||||
this.triggerSlot(1, value)
|
||||
}
|
||||
|
||||
constructor(name?: string) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export { default as ComfyReroute } from "./ComfyReroute"
|
||||
export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes"
|
||||
export { ComfyQueueEvents, ComfyCopyAction, ComfySwapAction, ComfyNotifyAction, ComfyStoreImagesAction, ComfyExecuteSubgraphAction } from "./ComfyActionNodes"
|
||||
export { default as ComfyPickFirstNode } from "./ComfyPickFirstNode"
|
||||
export { default as ComfyValueControl } from "./ComfyValueControl"
|
||||
export { default as ComfySelector } from "./ComfySelector"
|
||||
export { default as ComfyImageCacheNode } from "./ComfyImageCacheNode"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { get, writable } from 'svelte/store';
|
||||
import type { Readable, Writable } from 'svelte/store';
|
||||
import type ComfyApp from "$lib/components/ComfyApp"
|
||||
import type { LGraphNode, IWidget, LGraph } from "@litegraph-ts/core"
|
||||
import { type LGraphNode, type IWidget, type LGraph, NodeMode } from "@litegraph-ts/core"
|
||||
import { dndzone, SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action';
|
||||
import type { ComfyWidgetNode } from '$lib/nodes';
|
||||
|
||||
@@ -116,6 +116,12 @@ export type Attributes = {
|
||||
*/
|
||||
containerVariant?: "block" | "hidden",
|
||||
|
||||
/*
|
||||
* Tags for hiding containers with
|
||||
* For WidgetLayouts this will be ignored, it will use node.properties.tags instead
|
||||
*/
|
||||
tags: string[],
|
||||
|
||||
/*
|
||||
* If true, don't show this component in the UI
|
||||
*/
|
||||
@@ -142,6 +148,12 @@ export type Attributes = {
|
||||
*/
|
||||
variant?: string,
|
||||
|
||||
/*
|
||||
* What state to set this widget to in the frontend if its corresponding
|
||||
* node is disabled in the graph.
|
||||
*/
|
||||
nodeDisabledState: "visible" | "disabled" | "hidden",
|
||||
|
||||
/*********************************************/
|
||||
/* Special attributes for widgets/containers */
|
||||
/*********************************************/
|
||||
@@ -154,6 +166,9 @@ export type Attributes = {
|
||||
buttonSize?: "large" | "small"
|
||||
}
|
||||
|
||||
/*
|
||||
* Defines something that can be edited in the properties side panel.
|
||||
*/
|
||||
export type AttributesSpec = {
|
||||
/*
|
||||
* ID necessary for svelte's keyed each, autoset at the top level in this source file.
|
||||
@@ -256,9 +271,13 @@ export type AttributesCategorySpec = {
|
||||
|
||||
export type AttributesSpecList = AttributesCategorySpec[]
|
||||
|
||||
const serializeStringArray = (arg: string[]) => arg.join(",")
|
||||
const serializeStringArray = (arg: string[]) => {
|
||||
if (arg == null)
|
||||
arg = []
|
||||
return arg.join(",")
|
||||
}
|
||||
const deserializeStringArray = (arg: string) => {
|
||||
if (arg === "")
|
||||
if (arg === "" || arg == null)
|
||||
return []
|
||||
return arg.split(",").map(s => s.trim())
|
||||
}
|
||||
@@ -315,6 +334,15 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
||||
defaultValue: "",
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "nodeDisabledState",
|
||||
type: "enum",
|
||||
location: "widget",
|
||||
editable: true,
|
||||
values: ["visible", "disabled", "hidden"],
|
||||
defaultValue: "disabled",
|
||||
canShow: (di: IDragItem) => di.type === "widget"
|
||||
},
|
||||
|
||||
// Container variants
|
||||
{
|
||||
@@ -372,15 +400,6 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
||||
categoryName: "behavior",
|
||||
specs: [
|
||||
// Node variables
|
||||
{
|
||||
name: "tags",
|
||||
type: "string",
|
||||
location: "nodeVars",
|
||||
editable: true,
|
||||
defaultValue: [],
|
||||
serialize: serializeStringArray,
|
||||
deserialize: deserializeStringArray
|
||||
},
|
||||
{
|
||||
name: "saveUserState",
|
||||
type: "boolean",
|
||||
@@ -388,6 +407,39 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
||||
editable: true,
|
||||
defaultValue: true,
|
||||
},
|
||||
{
|
||||
name: "mode",
|
||||
type: "enum",
|
||||
location: "nodeVars",
|
||||
editable: true,
|
||||
values: ["ALWAYS", "NEVER"],
|
||||
defaultValue: "ALWAYS",
|
||||
serialize: (s) => s === NodeMode.ALWAYS ? "ALWAYS" : "NEVER",
|
||||
deserialize: (m) => m === "ALWAYS" ? NodeMode.ALWAYS : NodeMode.NEVER
|
||||
},
|
||||
|
||||
// Node properties
|
||||
{
|
||||
name: "tags",
|
||||
type: "string",
|
||||
location: "nodeProps",
|
||||
editable: true,
|
||||
defaultValue: [],
|
||||
serialize: serializeStringArray,
|
||||
deserialize: deserializeStringArray
|
||||
},
|
||||
|
||||
// Container tags are contained in the widget attributes
|
||||
{
|
||||
name: "tags",
|
||||
type: "string",
|
||||
location: "widget",
|
||||
editable: true,
|
||||
defaultValue: [],
|
||||
serialize: serializeStringArray,
|
||||
deserialize: deserializeStringArray,
|
||||
canShow: (di: IDragItem) => di.type === "container"
|
||||
},
|
||||
|
||||
// Range
|
||||
{
|
||||
@@ -457,14 +509,23 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
||||
// This is needed so the specs can be iterated with svelte's keyed #each.
|
||||
let i = 0;
|
||||
for (const cat of Object.values(ALL_ATTRIBUTES)) {
|
||||
for (const val of Object.values(cat.specs)) {
|
||||
val.id = i;
|
||||
for (const spec of Object.values(cat.specs)) {
|
||||
spec.id = i;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
export { ALL_ATTRIBUTES };
|
||||
|
||||
const defaultWidgetAttributes: Attributes = {} as any
|
||||
for (const cat of Object.values(ALL_ATTRIBUTES)) {
|
||||
for (const spec of Object.values(cat.specs)) {
|
||||
if (spec.location === "widget" && spec.defaultValue != null) {
|
||||
defaultWidgetAttributes[spec.name] = spec.defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Something that can be dragged around in the frontend - a widget or a container.
|
||||
*/
|
||||
@@ -494,7 +555,7 @@ export interface IDragItem {
|
||||
* Hackish thing to indicate to Svelte that an attribute changed.
|
||||
* TODO Use Writeable<Attributes> instead!
|
||||
*/
|
||||
attrsChanged: Writable<boolean>
|
||||
attrsChanged: Writable<number>
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -528,7 +589,8 @@ type LayoutStateOps = {
|
||||
groupItems: (dragItems: IDragItem[], attrs?: Partial<Attributes>) => ContainerLayout,
|
||||
ungroup: (container: ContainerLayout) => void,
|
||||
getCurrentSelection: () => IDragItem[],
|
||||
findLayoutForNode: (nodeId: number) => IDragItem | null;
|
||||
findLayoutEntryForNode: (nodeId: number) => DragItemEntry | null,
|
||||
findLayoutForNode: (nodeId: number) => IDragItem | null,
|
||||
serialize: () => SerializedLayoutState,
|
||||
deserialize: (data: SerializedLayoutState, graph: LGraph) => void,
|
||||
initDefaultLayout: () => void,
|
||||
@@ -575,13 +637,10 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes>
|
||||
const dragItem: ContainerLayout = {
|
||||
type: "container",
|
||||
id: `${state.currentId++}`,
|
||||
attrsChanged: writable(false),
|
||||
attrsChanged: writable(0),
|
||||
attrs: {
|
||||
...defaultWidgetAttributes,
|
||||
title: "Container",
|
||||
direction: "vertical",
|
||||
classes: "",
|
||||
containerVariant: "block",
|
||||
flexGrow: 100,
|
||||
...attrs
|
||||
}
|
||||
}
|
||||
@@ -602,12 +661,11 @@ function addWidget(parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partia
|
||||
type: "widget",
|
||||
id: `${state.currentId++}`,
|
||||
node: node,
|
||||
attrsChanged: writable(false),
|
||||
attrsChanged: writable(0),
|
||||
attrs: {
|
||||
...defaultWidgetAttributes,
|
||||
title: widgetName,
|
||||
direction: "horizontal",
|
||||
classes: "",
|
||||
flexGrow: 100,
|
||||
nodeDisabledState: "disabled",
|
||||
...attrs
|
||||
}
|
||||
}
|
||||
@@ -778,16 +836,23 @@ function ungroup(container: ContainerLayout) {
|
||||
store.set(state)
|
||||
}
|
||||
|
||||
function findLayoutForNode(nodeId: number): WidgetLayout | null {
|
||||
function findLayoutEntryForNode(nodeId: number): DragItemEntry | null {
|
||||
const state = get(store)
|
||||
const found = Object.entries(state.allItems).find(pair =>
|
||||
pair[1].dragItem.type === "widget"
|
||||
&& (pair[1].dragItem as WidgetLayout).node.id === nodeId)
|
||||
if (found)
|
||||
return found[1].dragItem as WidgetLayout
|
||||
return found[1]
|
||||
return null;
|
||||
}
|
||||
|
||||
function findLayoutForNode(nodeId: number): WidgetLayout | null {
|
||||
const found = findLayoutEntryForNode(nodeId);
|
||||
if (!found)
|
||||
return null;
|
||||
return found.dragItem as WidgetLayout
|
||||
}
|
||||
|
||||
function initDefaultLayout() {
|
||||
store.set({
|
||||
root: null,
|
||||
@@ -869,8 +934,8 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
|
||||
const dragItem: IDragItem = {
|
||||
type: entry.dragItem.type,
|
||||
id: entry.dragItem.id,
|
||||
attrs: entry.dragItem.attrs,
|
||||
attrsChanged: writable(false)
|
||||
attrs: { ...defaultWidgetAttributes, ...entry.dragItem.attrs },
|
||||
attrsChanged: writable(0)
|
||||
};
|
||||
|
||||
const dragEntry: DragItemEntry = {
|
||||
@@ -939,6 +1004,7 @@ const layoutStateStore: WritableLayoutStateStore =
|
||||
nodeRemoved,
|
||||
getCurrentSelection,
|
||||
groupItems,
|
||||
findLayoutEntryForNode,
|
||||
findLayoutForNode,
|
||||
ungroup,
|
||||
initDefaultLayout,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||
import { Button } from "@gradio/button";
|
||||
import { get, type Writable, writable } from "svelte/store";
|
||||
import { isDisabled } from "./utils"
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
let node: ComfyButtonNode | null = null;
|
||||
@@ -32,9 +33,9 @@
|
||||
|
||||
<div class="wrapper gradio-button">
|
||||
{#key $attrsChanged}
|
||||
{#if node !== null}
|
||||
{#if widget !== null && node !== null}
|
||||
<Button
|
||||
disabled={widget.attrs.disabled}
|
||||
disabled={isDisabled(widget)}
|
||||
on:click={onClick}
|
||||
variant={widget.attrs.buttonVariant || "primary"}
|
||||
size={widget.attrs.buttonSize === "small" ? "sm" : "lg"}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { ComfyCheckboxNode } from "$lib/nodes/ComfyWidgetNodes";
|
||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||
import { Block } from "@gradio/atoms";
|
||||
import { Block } from "@gradio/atoms";
|
||||
import { Checkbox } from "@gradio/form";
|
||||
import { get, type Writable, writable } from "svelte/store";
|
||||
import { isDisabled } from "./utils"
|
||||
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
@@ -31,7 +32,7 @@
|
||||
{#key $attrsChanged}
|
||||
{#if node !== null}
|
||||
<Block>
|
||||
<Checkbox disabled={widget.attrs.disabled} 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}
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
import type { ComfyComboNode } from "$lib/nodes/index";
|
||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import { isDisabled } from "./utils"
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
let node: ComfyComboNode | null = null;
|
||||
let nodeValue: Writable<string> | null = null;
|
||||
let propsChanged: Writable<number> | null = null;
|
||||
let comboRefreshed: Writable<boolean> | null = null;
|
||||
let wasComboRefreshed: boolean = false;
|
||||
let option: any
|
||||
|
||||
export let debug: boolean = false;
|
||||
@@ -34,6 +37,9 @@
|
||||
node = widget.node as ComfyComboNode
|
||||
nodeValue = node.value;
|
||||
propsChanged = node.propsChanged;
|
||||
comboRefreshed = node.comboRefreshed;
|
||||
if ($comboRefreshed)
|
||||
flashOnRefreshed();
|
||||
setOption($nodeValue) // don't react on option
|
||||
}
|
||||
}
|
||||
@@ -46,6 +52,12 @@
|
||||
$nodeValue = option.value;
|
||||
}
|
||||
|
||||
$: $comboRefreshed && flashOnRefreshed();
|
||||
|
||||
function flashOnRefreshed() {
|
||||
setTimeout(() => ($comboRefreshed = false), 1000);
|
||||
}
|
||||
|
||||
function getLinkValue() {
|
||||
if (!node)
|
||||
return "???";
|
||||
@@ -64,46 +76,39 @@
|
||||
input.blur();
|
||||
navigator.vibrate(20)
|
||||
}
|
||||
|
||||
let lastPropsChanged: number = 0;
|
||||
let werePropsChanged: boolean = false;
|
||||
|
||||
$: if ($propsChanged !== lastPropsChanged) {
|
||||
werePropsChanged = true;
|
||||
lastPropsChanged = $propsChanged;
|
||||
setTimeout(() => (werePropsChanged = false), 2000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="wrapper comfy-combo" class:updated={werePropsChanged}>
|
||||
<div class="wrapper comfy-combo" class:updated={$comboRefreshed}>
|
||||
{#key $propsChanged}
|
||||
{#if node !== null && nodeValue !== null}
|
||||
<label>
|
||||
{#if widget.attrs.title !== ""}
|
||||
<BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
|
||||
{/if}
|
||||
<Select
|
||||
bind:value={option}
|
||||
items={node.properties.values}
|
||||
disabled={widget.attrs.disabled || node.properties.values.length === 0}
|
||||
clearable={false}
|
||||
showChevron={true}
|
||||
inputAttributes={{ autocomplete: 'off' }}
|
||||
bind:input
|
||||
on:change
|
||||
on:focus={onFocus}
|
||||
on:select={onSelect}
|
||||
on:filter
|
||||
on:blur
|
||||
/>
|
||||
{#if debug}
|
||||
<div>Value: {option?.value}</div>
|
||||
<div>Items: {node.properties.values}</div>
|
||||
<div>NodeValue: {$nodeValue}</div>
|
||||
<div>LinkValue: {getLinkValue()}</div>
|
||||
{/if}
|
||||
</label>
|
||||
{/if}
|
||||
{#key $comboRefreshed}
|
||||
{#if node !== null && nodeValue !== null}
|
||||
<label>
|
||||
{#if widget.attrs.title !== ""}
|
||||
<BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
|
||||
{/if}
|
||||
<Select
|
||||
bind:value={option}
|
||||
items={node.properties.values}
|
||||
disabled={isDisabled(widget) || node.properties.values.length === 0}
|
||||
clearable={false}
|
||||
showChevron={true}
|
||||
inputAttributes={{ autocomplete: 'off' }}
|
||||
bind:input
|
||||
on:change
|
||||
on:focus={onFocus}
|
||||
on:select={onSelect}
|
||||
on:filter
|
||||
on:blur
|
||||
/>
|
||||
{#if debug}
|
||||
<div>Value: {option?.value}</div>
|
||||
<div>Items: {node.properties.values}</div>
|
||||
<div>NodeValue: {$nodeValue}</div>
|
||||
<div>LinkValue: {getLinkValue()}</div>
|
||||
{/if}
|
||||
</label>
|
||||
{/if}
|
||||
{/key}
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import { debounce } from "$lib/utils";
|
||||
import interfaceState from "$lib/stores/interfaceState";
|
||||
import { isDisabled } from "./utils"
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
let node: ComfySliderNode | null = null;
|
||||
@@ -104,7 +105,7 @@
|
||||
{#if node !== null && option !== null}
|
||||
<Range
|
||||
bind:value={option}
|
||||
disabled={widget.attrs.disabled}
|
||||
disabled={isDisabled(widget)}
|
||||
minimum={node.properties.min}
|
||||
maximum={node.properties.max}
|
||||
step={node.properties.step}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import type { ComfyComboNode } from "$lib/nodes/index";
|
||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import { isDisabled } from "./utils"
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
let node: ComfyComboNode | null = null;
|
||||
@@ -33,7 +34,7 @@
|
||||
<TextBox
|
||||
bind:value={$nodeValue}
|
||||
label={widget.attrs.title}
|
||||
disabled={widget.attrs.disabled}
|
||||
disabled={isDisabled(widget)}
|
||||
lines={node.properties.multiline ? 5 : 1}
|
||||
max_lines={node.properties.multiline ? 5 : 1}
|
||||
show_label={widget.attrs.title !== ""}
|
||||
|
||||
26
src/lib/widgets/utils.ts
Normal file
26
src/lib/widgets/utils.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { IDragItem } from "$lib/stores/layoutState";
|
||||
import layoutState from "$lib/stores/layoutState";
|
||||
import { NodeMode } from "@litegraph-ts/core";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
export function isDisabled(widget: IDragItem) {
|
||||
if (widget.attrs.disabled)
|
||||
return true;
|
||||
|
||||
if (widget.type === "widget") {
|
||||
return widget.attrs.nodeDisabledState === "disabled" && widget.node.mode === NodeMode.NEVER
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isHidden(widget: IDragItem) {
|
||||
if (widget.attrs.hidden)
|
||||
return true;
|
||||
|
||||
if (widget.type === "widget") {
|
||||
return widget.attrs.nodeDisabledState === "hidden" && widget.node.mode === NodeMode.NEVER
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -33,8 +33,5 @@
|
||||
<ListItem link="/graph/" title="Show Node Graph">
|
||||
<i class="icon icon-f7" slot="media" />
|
||||
</ListItem>
|
||||
<ListItem link="/hell/" title="🔥 HELL 🔥">
|
||||
<i class="icon icon-f7" slot="media" />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Page>
|
||||
|
||||
Reference in New Issue
Block a user