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/core": "workspace:*",
|
||||||
"@litegraph-ts/nodes-basic": "workspace:*",
|
"@litegraph-ts/nodes-basic": "workspace:*",
|
||||||
"@litegraph-ts/nodes-events": "workspace:*",
|
"@litegraph-ts/nodes-events": "workspace:*",
|
||||||
|
"@litegraph-ts/nodes-logic": "workspace:*",
|
||||||
"@litegraph-ts/nodes-math": "workspace:*",
|
"@litegraph-ts/nodes-math": "workspace:*",
|
||||||
"@litegraph-ts/nodes-strings": "workspace:*",
|
"@litegraph-ts/nodes-strings": "workspace:*",
|
||||||
"@litegraph-ts/tsconfig": "workspace:*",
|
"@litegraph-ts/tsconfig": "workspace:*",
|
||||||
|
|||||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -46,6 +46,9 @@ importers:
|
|||||||
'@litegraph-ts/nodes-events':
|
'@litegraph-ts/nodes-events':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:litegraph/packages/nodes-events
|
version: link:litegraph/packages/nodes-events
|
||||||
|
'@litegraph-ts/nodes-logic':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:litegraph/packages/nodes-logic
|
||||||
'@litegraph-ts/nodes-math':
|
'@litegraph-ts/nodes-math':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:litegraph/packages/nodes-math
|
version: link:litegraph/packages/nodes-math
|
||||||
@@ -806,6 +809,22 @@ importers:
|
|||||||
specifier: ^4.2.1
|
specifier: ^4.2.1
|
||||||
version: 4.3.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:
|
litegraph/packages/nodes-math:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@litegraph-ts/core':
|
'@litegraph-ts/core':
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
import GraphPage from './mobile/routes/graph.svelte';
|
import GraphPage from './mobile/routes/graph.svelte';
|
||||||
import ListSubWorkflowsPage from './mobile/routes/list-subworkflows.svelte';
|
import ListSubWorkflowsPage from './mobile/routes/list-subworkflows.svelte';
|
||||||
import SubWorkflowPage from './mobile/routes/subworkflow.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;
|
export let app: ComfyApp;
|
||||||
@@ -83,13 +82,6 @@
|
|||||||
props: { app }
|
props: { app }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/hell/',
|
|
||||||
component: HellPage,
|
|
||||||
options: {
|
|
||||||
props: { app }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
popup: {
|
popup: {
|
||||||
closeOnEscape: true,
|
closeOnEscape: true,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||||
import { startDrag, stopDrag } from "$lib/utils"
|
import { startDrag, stopDrag } from "$lib/utils"
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
|
import { isHidden } from "$lib/widgets/utils";
|
||||||
|
|
||||||
export let container: ContainerLayout | null = null;
|
export let container: ContainerLayout | null = null;
|
||||||
export let zIndex: number = 0;
|
export let zIndex: number = 0;
|
||||||
@@ -73,7 +74,7 @@
|
|||||||
on:finalize="{handleFinalize}"
|
on:finalize="{handleFinalize}"
|
||||||
>
|
>
|
||||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
{#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"
|
<div class="animation-wrapper"
|
||||||
class:hidden={hidden}
|
class:hidden={hidden}
|
||||||
animate:flip={{duration:flipDurationMs}}
|
animate:flip={{duration:flipDurationMs}}
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if container.attrs.hidden && edit}
|
{#if isHidden(container) && edit}
|
||||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if showHandles}
|
{#if showHandles}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||||
import { startDrag, stopDrag } from "$lib/utils"
|
import { startDrag, stopDrag } from "$lib/utils"
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
|
import { isHidden } from "$lib/widgets/utils";
|
||||||
|
|
||||||
export let container: ContainerLayout | null = null;
|
export let container: ContainerLayout | null = null;
|
||||||
export let zIndex: number = 0;
|
export let zIndex: number = 0;
|
||||||
@@ -75,7 +76,7 @@
|
|||||||
on:finalize="{handleFinalize}"
|
on:finalize="{handleFinalize}"
|
||||||
>
|
>
|
||||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
{#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"
|
<div class="animation-wrapper"
|
||||||
class:hidden={hidden}
|
class:hidden={hidden}
|
||||||
animate:flip={{duration:flipDurationMs}}
|
animate:flip={{duration:flipDurationMs}}
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if container.attrs.hidden && edit}
|
{#if isHidden(container) && edit}
|
||||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if showHandles}
|
{#if showHandles}
|
||||||
@@ -239,6 +240,10 @@
|
|||||||
flex-grow: 100;
|
flex-grow: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.handle-hidden {
|
||||||
|
background-color: #40404080;
|
||||||
|
}
|
||||||
|
|
||||||
.handle-widget:hover {
|
.handle-widget:hover {
|
||||||
background-color: #add8e680;
|
background-color: #add8e680;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,7 +201,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
async function doRefreshCombos() {
|
async function doRefreshCombos() {
|
||||||
await app.refreshComboInNodes()
|
await app.refreshComboInNodes(true)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type TypedEmitter from "typed-emitter";
|
|||||||
// Import nodes
|
// Import nodes
|
||||||
import "@litegraph-ts/nodes-basic"
|
import "@litegraph-ts/nodes-basic"
|
||||||
import "@litegraph-ts/nodes-events"
|
import "@litegraph-ts/nodes-events"
|
||||||
|
import "@litegraph-ts/nodes-logic"
|
||||||
import "@litegraph-ts/nodes-math"
|
import "@litegraph-ts/nodes-math"
|
||||||
import "@litegraph-ts/nodes-strings"
|
import "@litegraph-ts/nodes-strings"
|
||||||
import "$lib/nodes/index"
|
import "$lib/nodes/index"
|
||||||
@@ -70,6 +71,27 @@ export type Progress = {
|
|||||||
max: number
|
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 {
|
export default class ComfyApp {
|
||||||
api: ComfyAPI;
|
api: ComfyAPI;
|
||||||
rootEl: HTMLDivElement | null = null;
|
rootEl: HTMLDivElement | null = null;
|
||||||
@@ -443,23 +465,12 @@ export default class ComfyApp {
|
|||||||
for (const node_ of this.lGraph.computeExecutionOrder<ComfyGraphNode>(false, null)) {
|
for (const node_ of this.lGraph.computeExecutionOrder<ComfyGraphNode>(false, null)) {
|
||||||
const n = workflow.nodes.find((n) => n.id === node_.id);
|
const n = workflow.nodes.find((n) => n.id === node_.id);
|
||||||
|
|
||||||
if (!node_.isBackendNode) {
|
if (!isActiveBackendNode(node_, tag)) {
|
||||||
// console.debug("Not serializing node: ", node_.type)
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const node = node_ as ComfyBackendNode;
|
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 = {};
|
const inputs = {};
|
||||||
|
|
||||||
// Store all link values
|
// Store all link values
|
||||||
@@ -469,7 +480,11 @@ export default class ComfyApp {
|
|||||||
const inputLink = node.getInputLink(i)
|
const inputLink = node.getInputLink(i)
|
||||||
const inputNode = node.getInputNode(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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +530,7 @@ export default class ComfyApp {
|
|||||||
const isValidParent = (parent: ComfyGraphNode) => {
|
const isValidParent = (parent: ComfyGraphNode) => {
|
||||||
if (!parent || parent.isBackendNode)
|
if (!parent || parent.isBackendNode)
|
||||||
return false;
|
return false;
|
||||||
if ("tags" in parent && (parent.tags as string[]).indexOf(tag) === -1)
|
if (tag && !hasTag(parent, tag))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -525,8 +540,8 @@ export default class ComfyApp {
|
|||||||
if (link && !seen[link.id]) {
|
if (link && !seen[link.id]) {
|
||||||
seen[link.id] = true
|
seen[link.id] = true
|
||||||
const inputNode = parent.getInputNode(link.origin_slot) as ComfyGraphNode;
|
const inputNode = parent.getInputNode(link.origin_slot) as ComfyGraphNode;
|
||||||
if (inputNode && "tags" in inputNode && tag && (inputNode.tags as string[]).indexOf(tag) === -1) {
|
if (inputNode && tag && !hasTag(inputNode, tag)) {
|
||||||
console.debug("Skipping tagged parent node", tag, node.tags)
|
console.debug("Skipping tagged parent node", tag, node.properties.tags)
|
||||||
parent = null;
|
parent = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -538,7 +553,7 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (link && parent && parent.isBackendNode) {
|
if (link && parent && parent.isBackendNode) {
|
||||||
if ("tags" in parent && tag && (parent.tags as string[]).indexOf(tag) === -1)
|
if (tag && !hasTag(parent, tag))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const input = node.inputs[i]
|
const input = node.inputs[i]
|
||||||
@@ -669,7 +684,7 @@ export default class ComfyApp {
|
|||||||
/**
|
/**
|
||||||
* Refresh combo list on whole nodes
|
* Refresh combo list on whole nodes
|
||||||
*/
|
*/
|
||||||
async refreshComboInNodes() {
|
async refreshComboInNodes(flashUI: boolean = false) {
|
||||||
const defs = await this.api.getNodeDefs();
|
const defs = await this.api.getNodeDefs();
|
||||||
|
|
||||||
for (let nodeNum in this.lGraph._nodes) {
|
for (let nodeNum in this.lGraph._nodes) {
|
||||||
@@ -687,11 +702,13 @@ export default class ComfyApp {
|
|||||||
const inputNode = node.getInputNode(index)
|
const inputNode = node.getInputNode(index)
|
||||||
|
|
||||||
if (inputNode && "doAutoConfig" in inputNode) {
|
if (inputNode && "doAutoConfig" in inputNode) {
|
||||||
const comfyInputNode = inputNode as nodes.ComfyWidgetNode;
|
const comfyComboNode = inputNode as nodes.ComfyComboNode;
|
||||||
comfyInputNode.doAutoConfig(comfyInput)
|
comfyComboNode.doAutoConfig(comfyInput)
|
||||||
if (!comfyInput.config.values.includes(get(comfyInputNode.value))) {
|
if (!comfyInput.config.values.includes(get(comfyComboNode.value))) {
|
||||||
comfyInputNode.setValue(comfyInput.config.defaultValue || comfyInput.config.values[0])
|
comfyComboNode.setValue(comfyInput.config.defaultValue || comfyInput.config.values[0])
|
||||||
}
|
}
|
||||||
|
if (flashUI)
|
||||||
|
comfyComboNode.comboRefreshed.set(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
value = spec.deserialize(value)
|
value = spec.deserialize(value)
|
||||||
|
|
||||||
target.attrs[name] = value
|
target.attrs[name] = value
|
||||||
target.attrsChanged.set(!get(target.attrsChanged))
|
target.attrsChanged.set(get(target.attrsChanged) + 1)
|
||||||
|
|
||||||
if (node && "propsChanged" in node) {
|
if (node && "propsChanged" in node) {
|
||||||
const comfyNode = node as ComfyWidgetNode
|
const comfyNode = node as ComfyWidgetNode
|
||||||
@@ -151,27 +151,39 @@
|
|||||||
|
|
||||||
console.warn(spec)
|
console.warn(spec)
|
||||||
if (spec.refreshPanelOnChange) {
|
if (spec.refreshPanelOnChange) {
|
||||||
console.error("A! refresh")
|
doRefreshPanel()
|
||||||
$refreshPanel += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
function updateProperty(spec: AttributesSpec, value: any) {
|
||||||
if (node == null || !spec.editable)
|
if (node == null || !spec.editable)
|
||||||
return
|
return
|
||||||
|
|
||||||
const name = spec.name
|
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;
|
node.properties[name] = value;
|
||||||
|
|
||||||
if ("propsChanged" in node) {
|
if ("propsChanged" in node) {
|
||||||
const comfyNode = node as ComfyWidgetNode
|
const comfyNode = node as ComfyWidgetNode
|
||||||
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
|
comfyNode.notifyPropsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spec.refreshPanelOnChange)
|
if (spec.refreshPanelOnChange)
|
||||||
$refreshPanel += 1;
|
doRefreshPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVar(node: LGraphNode, spec: AttributesSpec) {
|
function getVar(node: LGraphNode, spec: AttributesSpec) {
|
||||||
@@ -201,7 +213,13 @@
|
|||||||
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
|
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spec.refreshPanelOnChange)
|
if (spec.refreshPanelOnChange) {
|
||||||
|
doRefreshPanel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doRefreshPanel() {
|
||||||
|
console.warn("[ComfyProperties] doRefreshPanel")
|
||||||
$refreshPanel += 1;
|
$refreshPanel += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +232,9 @@
|
|||||||
|
|
||||||
$layoutState.attrs[name] = value
|
$layoutState.attrs[name] = value
|
||||||
$layoutState = $layoutState
|
$layoutState = $layoutState
|
||||||
|
|
||||||
|
if (spec.refreshPanelOnChange)
|
||||||
|
doRefreshPanel()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -281,7 +302,7 @@
|
|||||||
<div class="props-entry">
|
<div class="props-entry">
|
||||||
{#if spec.type === "string"}
|
{#if spec.type === "string"}
|
||||||
<TextBox
|
<TextBox
|
||||||
value={node.properties[spec.name] || spec.defaultValue}
|
value={getProperty(node, spec)}
|
||||||
on:change={(e) => updateProperty(spec, e.detail)}
|
on:change={(e) => updateProperty(spec, e.detail)}
|
||||||
on:input={(e) => updateProperty(spec, e.detail)}
|
on:input={(e) => updateProperty(spec, e.detail)}
|
||||||
label={spec.name}
|
label={spec.name}
|
||||||
@@ -290,7 +311,7 @@
|
|||||||
/>
|
/>
|
||||||
{:else if spec.type === "boolean"}
|
{:else if spec.type === "boolean"}
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={node.properties[spec.name] || spec.defaultValue}
|
value={getProperty(node, spec)}
|
||||||
label={spec.name}
|
label={spec.name}
|
||||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||||
on:change={(e) => updateProperty(spec, e.detail)}
|
on:change={(e) => updateProperty(spec, e.detail)}
|
||||||
@@ -298,7 +319,7 @@
|
|||||||
{:else if spec.type === "number"}
|
{:else if spec.type === "number"}
|
||||||
<ComfyNumberProperty
|
<ComfyNumberProperty
|
||||||
name={spec.name}
|
name={spec.name}
|
||||||
value={node.properties[spec.name] || spec.defaultValue}
|
value={getProperty(node, spec)}
|
||||||
step={spec.step || 1}
|
step={spec.step || 1}
|
||||||
min={spec.min || -1024}
|
min={spec.min || -1024}
|
||||||
max={spec.max || 1024}
|
max={spec.max || 1024}
|
||||||
@@ -308,7 +329,7 @@
|
|||||||
{:else if spec.type === "enum"}
|
{:else if spec.type === "enum"}
|
||||||
<ComfyComboProperty
|
<ComfyComboProperty
|
||||||
name={spec.name}
|
name={spec.name}
|
||||||
value={node.properties[spec.name] || spec.defaultValue}
|
value={getProperty(node, spec)}
|
||||||
values={spec.values}
|
values={spec.values}
|
||||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||||
on:change={(e) => updateProperty(spec, e.detail)}
|
on:change={(e) => updateProperty(spec, e.detail)}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<span>Node: {getNodeInfo($queueState.runningNodeId)}</span>
|
<span>Node: {getNodeInfo($queueState.runningNodeId)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ProgressBar value={$queueState.progress?.value} max={$queueState.progress?.max} />
|
<ProgressBar value={$queueState.progress?.value} max={$queueState.progress?.max} styles="height: 30px;" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if typeof $queueState.queueRemaining === "number" && $queueState.queueRemaining > 0}
|
{#if typeof $queueState.queueRemaining === "number" && $queueState.queueRemaining > 0}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
import { flip } from 'svelte/animate';
|
import { flip } from 'svelte/animate';
|
||||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||||
import { startDrag, stopDrag } from "$lib/utils"
|
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 container: ContainerLayout | null = null;
|
||||||
export let zIndex: number = 0;
|
export let zIndex: number = 0;
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
|
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
|
||||||
{@const dragDisabled = zIndex === 0 || $layoutState.currentSelection.length > 2 || !$uiState.uiUnlocked}
|
{@const dragDisabled = zIndex === 0 || $layoutState.currentSelection.length > 2 || !$uiState.uiUnlocked}
|
||||||
{#key $attrsChanged}
|
{#key $attrsChanged}
|
||||||
|
{#if edit || !isHidden(container)}
|
||||||
{#if container.attrs.variant === "tabs"}
|
{#if container.attrs.variant === "tabs"}
|
||||||
<TabsContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
<TabsContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||||
{:else if container.attrs.variant === "accordion"}
|
{:else if container.attrs.variant === "accordion"}
|
||||||
@@ -41,5 +44,6 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<BlockContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
<BlockContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
|
||||||
{/if}
|
{/if}
|
||||||
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||||
import { startDrag, stopDrag } from "$lib/utils"
|
import { startDrag, stopDrag } from "$lib/utils"
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
|
import { isHidden } from "$lib/widgets/utils";
|
||||||
|
|
||||||
export let container: ContainerLayout | null = null;
|
export let container: ContainerLayout | null = null;
|
||||||
export let zIndex: number = 0;
|
export let zIndex: number = 0;
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
on:finalize="{handleFinalize}"
|
on:finalize="{handleFinalize}"
|
||||||
>
|
>
|
||||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item, i(item.id)}
|
{#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)}
|
{@const tabName = getTabName(container, i)}
|
||||||
<div class="animation-wrapper"
|
<div class="animation-wrapper"
|
||||||
class:hidden={hidden}
|
class:hidden={hidden}
|
||||||
@@ -104,7 +105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if container.attrs.hidden && edit}
|
{#if isHidden(container) && edit}
|
||||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if showHandles}
|
{#if showHandles}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
import Container from "./Container.svelte"
|
import Container from "./Container.svelte"
|
||||||
import { type Writable } from "svelte/store"
|
import { type Writable } from "svelte/store"
|
||||||
import type { ComfyWidgetNode } from "$lib/nodes";
|
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 dragItem: IDragItem | null = null;
|
||||||
export let zIndex: number = 0;
|
export let zIndex: number = 0;
|
||||||
@@ -64,17 +66,18 @@
|
|||||||
{/key}
|
{/key}
|
||||||
{:else if widget && widget.node}
|
{:else if widget && widget.node}
|
||||||
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
|
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
|
||||||
|
{@const hidden = isHidden(widget)}
|
||||||
{#key $attrsChanged}
|
{#key $attrsChanged}
|
||||||
{#key $propsChanged}
|
{#key $propsChanged}
|
||||||
<div class="widget {widget.attrs.classes} {getWidgetClass()}"
|
<div class="widget {widget.attrs.classes} {getWidgetClass()}"
|
||||||
class:edit={edit}
|
class:edit={edit}
|
||||||
class:selected={$uiState.uiUnlocked && $layoutState.currentSelection.includes(widget.id)}
|
class:selected={$uiState.uiUnlocked && $layoutState.currentSelection.includes(widget.id)}
|
||||||
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.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} />
|
<svelte:component this={widget.node.svelteComponentType} {widget} {isMobile} />
|
||||||
</div>
|
</div>
|
||||||
{#if widget.attrs.hidden && edit}
|
{#if hidden && edit}
|
||||||
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if showHandles}
|
{#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 ComfyGraphNode from "./ComfyGraphNode";
|
||||||
import { Watch } from "@litegraph-ts/nodes-basic";
|
import { Watch } from "@litegraph-ts/nodes-basic";
|
||||||
import type { SerializedPrompt } from "$lib/components/ComfyApp";
|
import type { SerializedPrompt } from "$lib/components/ComfyApp";
|
||||||
@@ -7,6 +7,7 @@ import type { GalleryOutput } from "./ComfyWidgetNodes";
|
|||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import queueState from "$lib/stores/queueState";
|
import queueState from "$lib/stores/queueState";
|
||||||
import notify from "$lib/notify";
|
import notify from "$lib/notify";
|
||||||
|
import layoutState from "$lib/stores/layoutState";
|
||||||
|
|
||||||
export interface ComfyQueueEventsProperties extends Record<any, any> {
|
export interface ComfyQueueEventsProperties extends Record<any, any> {
|
||||||
}
|
}
|
||||||
@@ -251,3 +252,86 @@ LiteGraph.registerNodeType({
|
|||||||
desc: "Runs a part of the graph based on a tag",
|
desc: "Runs a part of the graph based on a tag",
|
||||||
type: "actions/execute_subgraph"
|
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 static defaultInputConfigs: Record<string, Record<string, ComfyInputConfig>> = {}
|
||||||
|
|
||||||
private setup(nodeData: any) {
|
private setup(nodeData: any) {
|
||||||
@@ -88,7 +82,6 @@ export class ComfyBackendNode extends ComfyGraphNode {
|
|||||||
|
|
||||||
override onSerialize(o: SerializedLGraphNode) {
|
override onSerialize(o: SerializedLGraphNode) {
|
||||||
super.onSerialize(o);
|
super.onSerialize(o);
|
||||||
(o as any).tags = this.tags
|
|
||||||
for (const input of o.inputs) {
|
for (const input of o.inputs) {
|
||||||
// strip user-identifying data, it will be reinstantiated later
|
// strip user-identifying data, it will be reinstantiated later
|
||||||
if ((input as any).config != null) {
|
if ((input as any).config != null) {
|
||||||
@@ -100,8 +93,6 @@ export class ComfyBackendNode extends ComfyGraphNode {
|
|||||||
override onConfigure(o: SerializedLGraphNode) {
|
override onConfigure(o: SerializedLGraphNode) {
|
||||||
super.onConfigure(o);
|
super.onConfigure(o);
|
||||||
|
|
||||||
this.tags = (o as any).tags || []
|
|
||||||
|
|
||||||
const configs = ComfyBackendNode.defaultInputConfigs[o.type]
|
const configs = ComfyBackendNode.defaultInputConfigs[o.type]
|
||||||
for (let index = 0; index < this.inputs.length; index++) {
|
for (let index = 0; index < this.inputs.length; index++) {
|
||||||
const input = this.inputs[index] as IComfyInputSlot
|
const input = this.inputs[index] as IComfyInputSlot
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { ComfyInputConfig } from "$lib/IComfyInputSlot";
|
import type { ComfyInputConfig } from "$lib/IComfyInputSlot";
|
||||||
import type { SerializedPrompt } from "$lib/components/ComfyApp";
|
import type { SerializedPrompt } from "$lib/components/ComfyApp";
|
||||||
import type ComfyWidget from "$lib/components/widgets/ComfyWidget";
|
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 { SvelteComponentDev } from "svelte/internal";
|
||||||
import type { ComfyWidgetNode } from "./ComfyWidgetNodes";
|
import type { ComfyWidgetNode } from "./ComfyWidgetNodes";
|
||||||
import type IComfyInputSlot from "$lib/IComfyInputSlot";
|
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 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";
|
||||||
@@ -93,6 +93,17 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
|||||||
return Watch.toString(value)
|
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) {
|
private onValueUpdated(value: any) {
|
||||||
console.debug("[Widget] valueUpdated", this, value)
|
console.debug("[Widget] valueUpdated", this, value)
|
||||||
this.displayWidget.value = this.formatValue(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)
|
console.debug("Property copy", input, this.properties)
|
||||||
|
|
||||||
this.setValue(get(this.value))
|
this.setValue(get(this.value))
|
||||||
|
this.notifyPropsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyPropsChanged() {
|
||||||
this.propsChanged.set(get(this.propsChanged) + 1)
|
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
|
// 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) { }
|
clampOneConfig(input: IComfyInputSlot) { }
|
||||||
@@ -314,8 +329,11 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
|
|||||||
override svelteComponentType = ComboWidget
|
override svelteComponentType = ComboWidget
|
||||||
override defaultValue = "A";
|
override defaultValue = "A";
|
||||||
|
|
||||||
|
comboRefreshed: Writable<boolean>;
|
||||||
|
|
||||||
constructor(name?: string) {
|
constructor(name?: string) {
|
||||||
super(name, "A")
|
super(name, "A")
|
||||||
|
this.comboRefreshed = writable(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnectOutput(
|
onConnectOutput(
|
||||||
@@ -613,7 +631,7 @@ export class ComfyCheckboxNode extends ComfyWidgetNode<boolean> {
|
|||||||
const changed = value != get(this.value);
|
const changed = value != get(this.value);
|
||||||
super.setValue(Boolean(value))
|
super.setValue(Boolean(value))
|
||||||
if (changed)
|
if (changed)
|
||||||
this.triggerSlot(1)
|
this.triggerSlot(1, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(name?: string) {
|
constructor(name?: string) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export { default as ComfyReroute } from "./ComfyReroute"
|
export { default as ComfyReroute } from "./ComfyReroute"
|
||||||
export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes"
|
export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes"
|
||||||
export { ComfyQueueEvents, ComfyCopyAction, ComfySwapAction, ComfyNotifyAction, ComfyStoreImagesAction, ComfyExecuteSubgraphAction } from "./ComfyActionNodes"
|
export { ComfyQueueEvents, ComfyCopyAction, ComfySwapAction, ComfyNotifyAction, ComfyStoreImagesAction, ComfyExecuteSubgraphAction } from "./ComfyActionNodes"
|
||||||
|
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 ComfyImageCacheNode } from "./ComfyImageCacheNode"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
import type { Readable, Writable } from 'svelte/store';
|
import type { Readable, Writable } from 'svelte/store';
|
||||||
import type ComfyApp from "$lib/components/ComfyApp"
|
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 { dndzone, SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action';
|
||||||
import type { ComfyWidgetNode } from '$lib/nodes';
|
import type { ComfyWidgetNode } from '$lib/nodes';
|
||||||
|
|
||||||
@@ -116,6 +116,12 @@ export type Attributes = {
|
|||||||
*/
|
*/
|
||||||
containerVariant?: "block" | "hidden",
|
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
|
* If true, don't show this component in the UI
|
||||||
*/
|
*/
|
||||||
@@ -142,6 +148,12 @@ export type Attributes = {
|
|||||||
*/
|
*/
|
||||||
variant?: string,
|
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 */
|
/* Special attributes for widgets/containers */
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
@@ -154,6 +166,9 @@ export type Attributes = {
|
|||||||
buttonSize?: "large" | "small"
|
buttonSize?: "large" | "small"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Defines something that can be edited in the properties side panel.
|
||||||
|
*/
|
||||||
export type AttributesSpec = {
|
export type AttributesSpec = {
|
||||||
/*
|
/*
|
||||||
* ID necessary for svelte's keyed each, autoset at the top level in this source file.
|
* 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[]
|
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) => {
|
const deserializeStringArray = (arg: string) => {
|
||||||
if (arg === "")
|
if (arg === "" || arg == null)
|
||||||
return []
|
return []
|
||||||
return arg.split(",").map(s => s.trim())
|
return arg.split(",").map(s => s.trim())
|
||||||
}
|
}
|
||||||
@@ -315,6 +334,15 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
|||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "nodeDisabledState",
|
||||||
|
type: "enum",
|
||||||
|
location: "widget",
|
||||||
|
editable: true,
|
||||||
|
values: ["visible", "disabled", "hidden"],
|
||||||
|
defaultValue: "disabled",
|
||||||
|
canShow: (di: IDragItem) => di.type === "widget"
|
||||||
|
},
|
||||||
|
|
||||||
// Container variants
|
// Container variants
|
||||||
{
|
{
|
||||||
@@ -372,15 +400,6 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
|||||||
categoryName: "behavior",
|
categoryName: "behavior",
|
||||||
specs: [
|
specs: [
|
||||||
// Node variables
|
// Node variables
|
||||||
{
|
|
||||||
name: "tags",
|
|
||||||
type: "string",
|
|
||||||
location: "nodeVars",
|
|
||||||
editable: true,
|
|
||||||
defaultValue: [],
|
|
||||||
serialize: serializeStringArray,
|
|
||||||
deserialize: deserializeStringArray
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "saveUserState",
|
name: "saveUserState",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
@@ -388,6 +407,39 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
|||||||
editable: true,
|
editable: true,
|
||||||
defaultValue: 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
|
// Range
|
||||||
{
|
{
|
||||||
@@ -457,14 +509,23 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
|||||||
// This is needed so the specs can be iterated with svelte's keyed #each.
|
// This is needed so the specs can be iterated with svelte's keyed #each.
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const cat of Object.values(ALL_ATTRIBUTES)) {
|
for (const cat of Object.values(ALL_ATTRIBUTES)) {
|
||||||
for (const val of Object.values(cat.specs)) {
|
for (const spec of Object.values(cat.specs)) {
|
||||||
val.id = i;
|
spec.id = i;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ALL_ATTRIBUTES };
|
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.
|
* 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.
|
* Hackish thing to indicate to Svelte that an attribute changed.
|
||||||
* TODO Use Writeable<Attributes> instead!
|
* TODO Use Writeable<Attributes> instead!
|
||||||
*/
|
*/
|
||||||
attrsChanged: Writable<boolean>
|
attrsChanged: Writable<number>
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -528,7 +589,8 @@ type LayoutStateOps = {
|
|||||||
groupItems: (dragItems: IDragItem[], attrs?: Partial<Attributes>) => ContainerLayout,
|
groupItems: (dragItems: IDragItem[], attrs?: Partial<Attributes>) => ContainerLayout,
|
||||||
ungroup: (container: ContainerLayout) => void,
|
ungroup: (container: ContainerLayout) => void,
|
||||||
getCurrentSelection: () => IDragItem[],
|
getCurrentSelection: () => IDragItem[],
|
||||||
findLayoutForNode: (nodeId: number) => IDragItem | null;
|
findLayoutEntryForNode: (nodeId: number) => DragItemEntry | null,
|
||||||
|
findLayoutForNode: (nodeId: number) => IDragItem | null,
|
||||||
serialize: () => SerializedLayoutState,
|
serialize: () => SerializedLayoutState,
|
||||||
deserialize: (data: SerializedLayoutState, graph: LGraph) => void,
|
deserialize: (data: SerializedLayoutState, graph: LGraph) => void,
|
||||||
initDefaultLayout: () => void,
|
initDefaultLayout: () => void,
|
||||||
@@ -575,13 +637,10 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes>
|
|||||||
const dragItem: ContainerLayout = {
|
const dragItem: ContainerLayout = {
|
||||||
type: "container",
|
type: "container",
|
||||||
id: `${state.currentId++}`,
|
id: `${state.currentId++}`,
|
||||||
attrsChanged: writable(false),
|
attrsChanged: writable(0),
|
||||||
attrs: {
|
attrs: {
|
||||||
|
...defaultWidgetAttributes,
|
||||||
title: "Container",
|
title: "Container",
|
||||||
direction: "vertical",
|
|
||||||
classes: "",
|
|
||||||
containerVariant: "block",
|
|
||||||
flexGrow: 100,
|
|
||||||
...attrs
|
...attrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -602,12 +661,11 @@ function addWidget(parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partia
|
|||||||
type: "widget",
|
type: "widget",
|
||||||
id: `${state.currentId++}`,
|
id: `${state.currentId++}`,
|
||||||
node: node,
|
node: node,
|
||||||
attrsChanged: writable(false),
|
attrsChanged: writable(0),
|
||||||
attrs: {
|
attrs: {
|
||||||
|
...defaultWidgetAttributes,
|
||||||
title: widgetName,
|
title: widgetName,
|
||||||
direction: "horizontal",
|
nodeDisabledState: "disabled",
|
||||||
classes: "",
|
|
||||||
flexGrow: 100,
|
|
||||||
...attrs
|
...attrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -778,16 +836,23 @@ function ungroup(container: ContainerLayout) {
|
|||||||
store.set(state)
|
store.set(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
function findLayoutForNode(nodeId: number): WidgetLayout | null {
|
function findLayoutEntryForNode(nodeId: number): DragItemEntry | null {
|
||||||
const state = get(store)
|
const state = get(store)
|
||||||
const found = Object.entries(state.allItems).find(pair =>
|
const found = Object.entries(state.allItems).find(pair =>
|
||||||
pair[1].dragItem.type === "widget"
|
pair[1].dragItem.type === "widget"
|
||||||
&& (pair[1].dragItem as WidgetLayout).node.id === nodeId)
|
&& (pair[1].dragItem as WidgetLayout).node.id === nodeId)
|
||||||
if (found)
|
if (found)
|
||||||
return found[1].dragItem as WidgetLayout
|
return found[1]
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findLayoutForNode(nodeId: number): WidgetLayout | null {
|
||||||
|
const found = findLayoutEntryForNode(nodeId);
|
||||||
|
if (!found)
|
||||||
|
return null;
|
||||||
|
return found.dragItem as WidgetLayout
|
||||||
|
}
|
||||||
|
|
||||||
function initDefaultLayout() {
|
function initDefaultLayout() {
|
||||||
store.set({
|
store.set({
|
||||||
root: null,
|
root: null,
|
||||||
@@ -869,8 +934,8 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
|
|||||||
const dragItem: IDragItem = {
|
const dragItem: IDragItem = {
|
||||||
type: entry.dragItem.type,
|
type: entry.dragItem.type,
|
||||||
id: entry.dragItem.id,
|
id: entry.dragItem.id,
|
||||||
attrs: entry.dragItem.attrs,
|
attrs: { ...defaultWidgetAttributes, ...entry.dragItem.attrs },
|
||||||
attrsChanged: writable(false)
|
attrsChanged: writable(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
const dragEntry: DragItemEntry = {
|
const dragEntry: DragItemEntry = {
|
||||||
@@ -939,6 +1004,7 @@ const layoutStateStore: WritableLayoutStateStore =
|
|||||||
nodeRemoved,
|
nodeRemoved,
|
||||||
getCurrentSelection,
|
getCurrentSelection,
|
||||||
groupItems,
|
groupItems,
|
||||||
|
findLayoutEntryForNode,
|
||||||
findLayoutForNode,
|
findLayoutForNode,
|
||||||
ungroup,
|
ungroup,
|
||||||
initDefaultLayout,
|
initDefaultLayout,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||||
import { Button } from "@gradio/button";
|
import { Button } from "@gradio/button";
|
||||||
import { get, type Writable, writable } from "svelte/store";
|
import { get, type Writable, writable } from "svelte/store";
|
||||||
|
import { isDisabled } from "./utils"
|
||||||
export let widget: WidgetLayout | null = null;
|
export let widget: WidgetLayout | null = null;
|
||||||
export let isMobile: boolean = false;
|
export let isMobile: boolean = false;
|
||||||
let node: ComfyButtonNode | null = null;
|
let node: ComfyButtonNode | null = null;
|
||||||
@@ -32,9 +33,9 @@
|
|||||||
|
|
||||||
<div class="wrapper gradio-button">
|
<div class="wrapper gradio-button">
|
||||||
{#key $attrsChanged}
|
{#key $attrsChanged}
|
||||||
{#if node !== null}
|
{#if widget !== null && node !== null}
|
||||||
<Button
|
<Button
|
||||||
disabled={widget.attrs.disabled}
|
disabled={isDisabled(widget)}
|
||||||
on:click={onClick}
|
on:click={onClick}
|
||||||
variant={widget.attrs.buttonVariant || "primary"}
|
variant={widget.attrs.buttonVariant || "primary"}
|
||||||
size={widget.attrs.buttonSize === "small" ? "sm" : "lg"}
|
size={widget.attrs.buttonSize === "small" ? "sm" : "lg"}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { Block } from "@gradio/atoms";
|
import { Block } from "@gradio/atoms";
|
||||||
import { Checkbox } from "@gradio/form";
|
import { Checkbox } from "@gradio/form";
|
||||||
import { get, type Writable, writable } from "svelte/store";
|
import { get, type Writable, writable } from "svelte/store";
|
||||||
|
import { isDisabled } from "./utils"
|
||||||
|
|
||||||
export let widget: WidgetLayout | null = null;
|
export let widget: WidgetLayout | null = null;
|
||||||
export let isMobile: boolean = false;
|
export let isMobile: boolean = false;
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
{#key $attrsChanged}
|
{#key $attrsChanged}
|
||||||
{#if node !== null}
|
{#if node !== null}
|
||||||
<Block>
|
<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>
|
</Block>
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
import type { ComfyComboNode } from "$lib/nodes/index";
|
import type { ComfyComboNode } from "$lib/nodes/index";
|
||||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||||
import { get, type Writable } from "svelte/store";
|
import { get, type Writable } from "svelte/store";
|
||||||
|
import { isDisabled } from "./utils"
|
||||||
export let widget: WidgetLayout | null = null;
|
export let widget: WidgetLayout | null = null;
|
||||||
export let isMobile: boolean = false;
|
export let isMobile: boolean = false;
|
||||||
let node: ComfyComboNode | null = null;
|
let node: ComfyComboNode | null = null;
|
||||||
let nodeValue: Writable<string> | null = null;
|
let nodeValue: Writable<string> | null = null;
|
||||||
let propsChanged: Writable<number> | null = null;
|
let propsChanged: Writable<number> | null = null;
|
||||||
|
let comboRefreshed: Writable<boolean> | null = null;
|
||||||
|
let wasComboRefreshed: boolean = false;
|
||||||
let option: any
|
let option: any
|
||||||
|
|
||||||
export let debug: boolean = false;
|
export let debug: boolean = false;
|
||||||
@@ -34,6 +37,9 @@
|
|||||||
node = widget.node as ComfyComboNode
|
node = widget.node as ComfyComboNode
|
||||||
nodeValue = node.value;
|
nodeValue = node.value;
|
||||||
propsChanged = node.propsChanged;
|
propsChanged = node.propsChanged;
|
||||||
|
comboRefreshed = node.comboRefreshed;
|
||||||
|
if ($comboRefreshed)
|
||||||
|
flashOnRefreshed();
|
||||||
setOption($nodeValue) // don't react on option
|
setOption($nodeValue) // don't react on option
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,6 +52,12 @@
|
|||||||
$nodeValue = option.value;
|
$nodeValue = option.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: $comboRefreshed && flashOnRefreshed();
|
||||||
|
|
||||||
|
function flashOnRefreshed() {
|
||||||
|
setTimeout(() => ($comboRefreshed = false), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function getLinkValue() {
|
function getLinkValue() {
|
||||||
if (!node)
|
if (!node)
|
||||||
return "???";
|
return "???";
|
||||||
@@ -64,19 +76,11 @@
|
|||||||
input.blur();
|
input.blur();
|
||||||
navigator.vibrate(20)
|
navigator.vibrate(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastPropsChanged: number = 0;
|
|
||||||
let werePropsChanged: boolean = false;
|
|
||||||
|
|
||||||
$: if ($propsChanged !== lastPropsChanged) {
|
|
||||||
werePropsChanged = true;
|
|
||||||
lastPropsChanged = $propsChanged;
|
|
||||||
setTimeout(() => (werePropsChanged = false), 2000);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper comfy-combo" class:updated={werePropsChanged}>
|
<div class="wrapper comfy-combo" class:updated={$comboRefreshed}>
|
||||||
{#key $propsChanged}
|
{#key $propsChanged}
|
||||||
|
{#key $comboRefreshed}
|
||||||
{#if node !== null && nodeValue !== null}
|
{#if node !== null && nodeValue !== null}
|
||||||
<label>
|
<label>
|
||||||
{#if widget.attrs.title !== ""}
|
{#if widget.attrs.title !== ""}
|
||||||
@@ -85,7 +89,7 @@
|
|||||||
<Select
|
<Select
|
||||||
bind:value={option}
|
bind:value={option}
|
||||||
items={node.properties.values}
|
items={node.properties.values}
|
||||||
disabled={widget.attrs.disabled || node.properties.values.length === 0}
|
disabled={isDisabled(widget) || node.properties.values.length === 0}
|
||||||
clearable={false}
|
clearable={false}
|
||||||
showChevron={true}
|
showChevron={true}
|
||||||
inputAttributes={{ autocomplete: 'off' }}
|
inputAttributes={{ autocomplete: 'off' }}
|
||||||
@@ -105,6 +109,7 @@
|
|||||||
</label>
|
</label>
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { get, type Writable } from "svelte/store";
|
import { get, type Writable } from "svelte/store";
|
||||||
import { debounce } from "$lib/utils";
|
import { debounce } from "$lib/utils";
|
||||||
import interfaceState from "$lib/stores/interfaceState";
|
import interfaceState from "$lib/stores/interfaceState";
|
||||||
|
import { isDisabled } from "./utils"
|
||||||
export let widget: WidgetLayout | null = null;
|
export let widget: WidgetLayout | null = null;
|
||||||
export let isMobile: boolean = false;
|
export let isMobile: boolean = false;
|
||||||
let node: ComfySliderNode | null = null;
|
let node: ComfySliderNode | null = null;
|
||||||
@@ -104,7 +105,7 @@
|
|||||||
{#if node !== null && option !== null}
|
{#if node !== null && option !== null}
|
||||||
<Range
|
<Range
|
||||||
bind:value={option}
|
bind:value={option}
|
||||||
disabled={widget.attrs.disabled}
|
disabled={isDisabled(widget)}
|
||||||
minimum={node.properties.min}
|
minimum={node.properties.min}
|
||||||
maximum={node.properties.max}
|
maximum={node.properties.max}
|
||||||
step={node.properties.step}
|
step={node.properties.step}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import type { ComfyComboNode } from "$lib/nodes/index";
|
import type { ComfyComboNode } from "$lib/nodes/index";
|
||||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||||
import { get, type Writable } from "svelte/store";
|
import { get, type Writable } from "svelte/store";
|
||||||
|
import { isDisabled } from "./utils"
|
||||||
export let widget: WidgetLayout | null = null;
|
export let widget: WidgetLayout | null = null;
|
||||||
export let isMobile: boolean = false;
|
export let isMobile: boolean = false;
|
||||||
let node: ComfyComboNode | null = null;
|
let node: ComfyComboNode | null = null;
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
<TextBox
|
<TextBox
|
||||||
bind:value={$nodeValue}
|
bind:value={$nodeValue}
|
||||||
label={widget.attrs.title}
|
label={widget.attrs.title}
|
||||||
disabled={widget.attrs.disabled}
|
disabled={isDisabled(widget)}
|
||||||
lines={node.properties.multiline ? 5 : 1}
|
lines={node.properties.multiline ? 5 : 1}
|
||||||
max_lines={node.properties.multiline ? 5 : 1}
|
max_lines={node.properties.multiline ? 5 : 1}
|
||||||
show_label={widget.attrs.title !== ""}
|
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">
|
<ListItem link="/graph/" title="Show Node Graph">
|
||||||
<i class="icon icon-f7" slot="media" />
|
<i class="icon icon-f7" slot="media" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem link="/hell/" title="🔥 HELL 🔥">
|
|
||||||
<i class="icon icon-f7" slot="media" />
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
</List>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
Reference in New Issue
Block a user