Jump to node from widget properties button
This commit is contained in:
@@ -11,6 +11,7 @@ import queueState from "./stores/queueState";
|
||||
import selectionState from "./stores/selectionState";
|
||||
import templateState from "./stores/templateState";
|
||||
import { calcNodesBoundingBox } from "./utils";
|
||||
import interfaceState from "./stores/interfaceState";
|
||||
|
||||
export type SerializedGraphCanvasState = {
|
||||
offset: Vector2,
|
||||
@@ -118,13 +119,7 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||
// color = "yellow";
|
||||
// thickness = 5;
|
||||
// }
|
||||
if (ss.currentHoveredNodes.has(node.id)) {
|
||||
color = "lightblue";
|
||||
}
|
||||
else if (isRunningNode) {
|
||||
color = "#0f0";
|
||||
}
|
||||
else if (nodeErrors) {
|
||||
if (nodeErrors) {
|
||||
const hasExecutionError = nodeErrors.find(e => e.errorType === "execution");
|
||||
if (hasExecutionError) {
|
||||
blink = true;
|
||||
@@ -139,6 +134,12 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||
color = "cyan";
|
||||
thickness = 2
|
||||
}
|
||||
else if (ss.currentHoveredNodes.has(node.id)) {
|
||||
color = "lightblue";
|
||||
}
|
||||
else if (isRunningNode) {
|
||||
color = "#0f0";
|
||||
}
|
||||
|
||||
if (blink) {
|
||||
if (nodeErrors && nodeErrors.includes(this.blinkError) && this.blinkErrorTime > 0) {
|
||||
@@ -700,6 +701,8 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||
}
|
||||
|
||||
jumpToNode(node: LGraphNode) {
|
||||
interfaceState.update(s => { s.isJumpingToNode = true; return s; })
|
||||
|
||||
this.closeAllSubgraphs();
|
||||
|
||||
const subgraphs = Array.from(node.iterateParentSubgraphNodes()).reverse();
|
||||
@@ -709,6 +712,7 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||
}
|
||||
|
||||
this.centerOnNode(node);
|
||||
this.selectNode(node);
|
||||
}
|
||||
|
||||
jumpToNodeAndInput(node: LGraphNode, slotIndex: number) {
|
||||
|
||||
@@ -220,6 +220,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function openGraph(cb: () => void) {
|
||||
const newGraphSize = Math.max(50, graphSize);
|
||||
const willOpenPane = newGraphSize != graphSize
|
||||
graphSize = newGraphSize
|
||||
|
||||
if (willOpenPane) {
|
||||
const graphPane = getGraphPane();
|
||||
if (graphPane) {
|
||||
graphPane.addEventListener("transitionend", cb, { once: true })
|
||||
await tick()
|
||||
}
|
||||
else {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
else {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
async function showError(promptIDWithError: PromptID) {
|
||||
hideError();
|
||||
|
||||
@@ -247,23 +267,7 @@
|
||||
app.lCanvas.jumpToFirstError();
|
||||
}
|
||||
|
||||
const newGraphSize = Math.max(50, graphSize);
|
||||
const willOpenPane = newGraphSize != graphSize
|
||||
graphSize = newGraphSize
|
||||
|
||||
if (willOpenPane) {
|
||||
const graphPane = getGraphPane();
|
||||
if (graphPane) {
|
||||
graphPane.addEventListener("transitionend", jumpToError, { once: true })
|
||||
await tick()
|
||||
}
|
||||
else {
|
||||
jumpToError()
|
||||
}
|
||||
}
|
||||
else {
|
||||
jumpToError()
|
||||
}
|
||||
await openGraph(jumpToError)
|
||||
}
|
||||
|
||||
function hideError() {
|
||||
@@ -273,7 +277,8 @@
|
||||
}
|
||||
|
||||
setContext(WORKFLOWS_VIEW, {
|
||||
showError
|
||||
showError,
|
||||
openGraph
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
import ComfyProperties from "./ComfyProperties.svelte";
|
||||
import ComfyQueue from "./ComfyQueue.svelte";
|
||||
import ComfyTemplates from "./ComfyTemplates.svelte";
|
||||
import { SvelteComponent } from "svelte";
|
||||
import { SvelteComponent } from "svelte";
|
||||
import { capitalize } from "$lib/utils";
|
||||
|
||||
export let app: ComfyApp
|
||||
export let mode: ComfyPaneMode = "none";
|
||||
@@ -40,7 +41,7 @@
|
||||
{:else if mode === "graph"}
|
||||
<ComfyGraphView {app} />
|
||||
{:else if mode === "properties"}
|
||||
<ComfyProperties workflow={$workflowState.activeWorkflow} />
|
||||
<ComfyProperties {app} workflow={$workflowState.activeWorkflow} />
|
||||
{:else if mode === "templates"}
|
||||
<ComfyTemplates {app} />
|
||||
{:else if mode === "queue"}
|
||||
@@ -55,6 +56,7 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<button class="mode-button ternary"
|
||||
disabled={mode === theMode}
|
||||
title={capitalize(theMode)}
|
||||
class:selected={mode === theMode}
|
||||
on:click={() => switchMode(theMode)}>
|
||||
<svelte:component this={icon} width="100%" height="100%" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { LGraphNode } from "@litegraph-ts/core"
|
||||
import { type IDragItem, type WidgetLayout, ALL_ATTRIBUTES, type AttributesSpec, type WritableLayoutStateStore } from "$lib/stores/layoutStates"
|
||||
import uiState from "$lib/stores/uiState"
|
||||
import interfaceState from "$lib/stores/interfaceState"
|
||||
import workflowState from "$lib/stores/workflowState"
|
||||
import layoutStates from "$lib/stores/layoutStates"
|
||||
import selectionState from "$lib/stores/selectionState"
|
||||
@@ -11,8 +12,12 @@
|
||||
import ComfyNumberProperty from "./ComfyNumberProperty.svelte";
|
||||
import ComfyComboProperty from "./ComfyComboProperty.svelte";
|
||||
import type { ComfyWidgetNode } from "$lib/nodes/widgets";
|
||||
import type { ComfyBoxWorkflow } from "$lib/stores/workflowState";
|
||||
import type { ComfyBoxWorkflow } from "$lib/stores/workflowState";
|
||||
import { Diagram3 } from "svelte-bootstrap-icons";
|
||||
import { getContext } from "svelte";
|
||||
import { WORKFLOWS_VIEW } from "./ComfyBoxWorkflowsView.svelte";
|
||||
|
||||
export let app: ComfyApp
|
||||
export let workflow: ComfyBoxWorkflow | null;
|
||||
|
||||
let layoutState: WritableLayoutStateStore | null = null
|
||||
@@ -22,30 +27,44 @@
|
||||
let target: IDragItem | null = null;
|
||||
let node: LGraphNode | null = null;
|
||||
|
||||
$: if (layoutState) {
|
||||
if ($selectionState.currentSelection.length > 0) {
|
||||
node = null;
|
||||
const targetId = $selectionState.currentSelection.slice(-1)[0]
|
||||
const entry = $layoutState.allItems[targetId]
|
||||
if (entry != null) {
|
||||
target = entry.dragItem
|
||||
if (target.type === "widget") {
|
||||
node = (target as WidgetLayout).node
|
||||
$: {
|
||||
if ($interfaceState.isJumpingToNode) {
|
||||
$interfaceState.isJumpingToNode = false;
|
||||
}
|
||||
{
|
||||
if (layoutState) {
|
||||
if ($selectionState.currentSelection.length > 0) {
|
||||
node = null;
|
||||
const targetId = $selectionState.currentSelection.slice(-1)[0]
|
||||
const entry = $layoutState.allItems[targetId]
|
||||
if (entry != null) {
|
||||
target = entry.dragItem
|
||||
if (target.type === "widget") {
|
||||
node = (target as WidgetLayout).node
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ($selectionState.currentSelectionNodes.length > 0) {
|
||||
target = null;
|
||||
node = $selectionState.currentSelectionNodes[0]
|
||||
|
||||
if (node != null && layoutState != null) {
|
||||
const dragItem = layoutState.findLayoutForNode(node.id);
|
||||
if (dragItem != null) {
|
||||
target = dragItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
target = null
|
||||
node = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
target = null;
|
||||
node = null;
|
||||
}
|
||||
}
|
||||
else if ($selectionState.currentSelectionNodes.length > 0) {
|
||||
target = null;
|
||||
node = $selectionState.currentSelectionNodes[0]
|
||||
}
|
||||
else {
|
||||
target = null
|
||||
node = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
target = null;
|
||||
node = null;
|
||||
}
|
||||
|
||||
$: if (target) {
|
||||
@@ -291,195 +310,244 @@
|
||||
console.warn("[ComfyProperties] doRefreshPanel")
|
||||
$layoutStates.refreshPropsPanel += 1;
|
||||
}
|
||||
|
||||
const workflowsViewContext = getContext(WORKFLOWS_VIEW) as any;
|
||||
|
||||
async function jumpToNode() {
|
||||
if (!workflowsViewContext) {
|
||||
// strange svelte bug caused by HMR
|
||||
// https://github.com/sveltejs/svelte/issues/8655
|
||||
console.error("[ComfyProperties] No workflows view context!")
|
||||
return;
|
||||
}
|
||||
|
||||
if (app?.lCanvas == null || workflow == null || node == null)
|
||||
return;
|
||||
|
||||
const activeWorkflow = workflowState.setActiveWorkflow(app.lCanvas, workflow.id);
|
||||
|
||||
if (activeWorkflow == null || !activeWorkflow.graph.getNodeByIdRecursive(node.id))
|
||||
return;
|
||||
|
||||
await workflowsViewContext.openGraph(() => {
|
||||
app.lCanvas.jumpToNode(node);
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="props">
|
||||
<div class="top">
|
||||
<div class="target-name">
|
||||
<span>
|
||||
<span class="title">{target?.attrs?.title || node?.title || "Workflow"}<span>
|
||||
<div class="props-scroller">
|
||||
<div class="top">
|
||||
<div class="target-name">
|
||||
<div class="target-title-wrapper">
|
||||
<span class="title">{target?.attrs?.title || node?.title || "Workflow"}</span>
|
||||
{#if targetType !== ""}
|
||||
<span class="type">({targetType})</span>
|
||||
{/if}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{#if node != null}
|
||||
<div class="target-name-button">
|
||||
<button class="mode-button ternary"
|
||||
disabled={node == null}
|
||||
title="View in Graph"
|
||||
on:click={jumpToNode}
|
||||
>
|
||||
<Diagram3 width="100%" height="100%" />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="props-entries">
|
||||
{#if workflow != null && layoutState != null}
|
||||
{#key workflow.id}
|
||||
{#key $layoutStates.refreshPropsPanel}
|
||||
{#each ALL_ATTRIBUTES as category(category.categoryName)}
|
||||
<div class="category-name">
|
||||
<span>
|
||||
<span class="title">{category.categoryName}</span>
|
||||
</span>
|
||||
</div>
|
||||
{#each category.specs as spec(spec.id)}
|
||||
{#if validWidgetAttribute(spec, target)}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={getAttribute(target, spec)}
|
||||
on:change={(e) => updateAttribute(spec, target, e.detail)}
|
||||
on:input={(e) => updateAttribute(spec, target, e.detail)}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
label={spec.name}
|
||||
max_lines={spec.multiline ? 5 : 1}
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
<div class="props-entries">
|
||||
{#if workflow != null && layoutState != null}
|
||||
{#key workflow.id}
|
||||
{#key $layoutStates.refreshPropsPanel}
|
||||
{#each ALL_ATTRIBUTES as category(category.categoryName)}
|
||||
<div class="category-name">
|
||||
<span>
|
||||
<span class="title">{category.categoryName}</span>
|
||||
</span>
|
||||
</div>
|
||||
{#each category.specs as spec(spec.id)}
|
||||
{#if validWidgetAttribute(spec, target)}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={getAttribute(target, spec)}
|
||||
on:change={(e) => updateAttribute(spec, target, e.detail)}
|
||||
on:input={(e) => updateAttribute(spec, target, e.detail)}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
label={spec.name}
|
||||
max_lines={spec.multiline ? 5 : 1}
|
||||
/>
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={getAttribute(target, spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateAttribute(spec, target, e.detail)}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
label={spec.name}
|
||||
/>
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={getAttribute(target, spec)}
|
||||
values={spec.values}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateAttribute(spec, target, e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if node}
|
||||
{#if validNodeProperty(spec, node)}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={getProperty(node, spec)}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
on:input={(e) => updateProperty(spec, e.detail)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
max_lines={spec.multiline ? 5 : 1}
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={getProperty(node, spec)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
/>
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={getProperty(node, spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
/>
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
name={spec.name}
|
||||
value={getProperty(node, spec)}
|
||||
value={getAttribute(target, spec)}
|
||||
values={spec.values}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
on:change={(e) => updateAttribute(spec, target, e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if validNodeVar(spec, node)}
|
||||
{:else if node}
|
||||
{#if validNodeProperty(spec, node)}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={getProperty(node, spec)}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
on:input={(e) => updateProperty(spec, e.detail)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
max_lines={spec.multiline ? 5 : 1}
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={getProperty(node, spec)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
/>
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={getProperty(node, spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
/>
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
name={spec.name}
|
||||
value={getProperty(node, spec)}
|
||||
values={spec.values}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateProperty(spec, e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if validNodeVar(spec, node)}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={getVar(node, spec)}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
on:input={(e) => updateVar(spec, e.detail)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
max_lines={spec.multiline ? 5 : 1}
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={getVar(node, spec)}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
label={spec.name}
|
||||
/>
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={getVar(node, spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
/>
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
name={spec.name}
|
||||
value={getVar(node, spec)}
|
||||
values={spec.values}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{:else if !node && !target && validWorkflowAttribute(spec)}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={getVar(node, spec)}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
on:input={(e) => updateVar(spec, e.detail)}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
on:input={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
max_lines={spec.multiline ? 5 : 1}
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={getVar(node, spec)}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
label={spec.name}
|
||||
/>
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={getVar(node, spec)}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
/>
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
name={spec.name}
|
||||
value={getVar(node, spec)}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
values={spec.values}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateVar(spec, e.detail)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if !node && !target && validWorkflowAttribute(spec)}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={getWorkflowAttribute(spec)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
on:input={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
label={spec.name}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
max_lines={spec.multiline ? 5 : 1}
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={getWorkflowAttribute(spec)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
label={spec.name}
|
||||
/>
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
/>
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
name={spec.name}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
values={spec.values}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
{/each}
|
||||
{/key}
|
||||
{/key}
|
||||
{/key}
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
$bottom-bar-height: 2.5rem;
|
||||
|
||||
.props {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.props-scroller {
|
||||
width: 100%;
|
||||
height: calc(100% - $bottom-bar-height);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.props-entry {
|
||||
padding-bottom: 0.5rem;
|
||||
@@ -491,7 +559,6 @@
|
||||
|
||||
.target-name {
|
||||
background: var(--input-background-fill);
|
||||
border-color: var(--input-border-color);
|
||||
border-color: var(--input-border-color);
|
||||
|
||||
.title {
|
||||
@@ -501,6 +568,48 @@
|
||||
padding-left: 0.25rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
> .target-title-wrapper {
|
||||
padding: 0.8rem 0 0.8rem 1.0rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
> span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
> .target-name-button {
|
||||
padding: 0.5rem;
|
||||
.mode-button {
|
||||
color: var(--comfy-accent-soft);
|
||||
height: $bottom-bar-height;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
margin: 1.0rem;
|
||||
padding: 0.5rem;
|
||||
margin-left: auto;
|
||||
|
||||
@include square-button;
|
||||
|
||||
color: var(--neutral-300);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
filter: brightness(120%) !important;
|
||||
}
|
||||
&:active:not(:disabled) {
|
||||
filter: brightness(50%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,13 +627,5 @@
|
||||
color: var(--neutral-500);
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
/* width: 100%;
|
||||
height: auto;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 0.5em; */
|
||||
}
|
||||
|
||||
@include disable-inputs;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import LGraphCanvas from "@litegraph-ts/core/src/LGraphCanvas";
|
||||
import ComfyGraphNode from "./ComfyGraphNode";
|
||||
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
|
||||
import ComfyWidgets from "$lib/widgets"
|
||||
import type { ComfyWidgetNode } from "$lib/nodes/widgets";
|
||||
import { BuiltInSlotShape, BuiltInSlotType, LiteGraph, type SerializedLGraphNode } from "@litegraph-ts/core";
|
||||
@@ -8,10 +8,19 @@ import type { ComfyInputConfig } from "$lib/IComfyInputSlot";
|
||||
import { iterateNodeDefOutputs, type ComfyNodeDef, iterateNodeDefInputs } from "$lib/ComfyNodeDef";
|
||||
import type { SerializedPromptOutput } from "$lib/utils";
|
||||
|
||||
export interface ComfyBackendNodeProperties extends ComfyGraphNodeProperties {
|
||||
noOutputDisplay: boolean
|
||||
}
|
||||
|
||||
/*
|
||||
* Base class for any node with configuration sent by the backend.
|
||||
*/
|
||||
export class ComfyBackendNode extends ComfyGraphNode {
|
||||
override properties: ComfyBackendNodeProperties = {
|
||||
tags: [],
|
||||
noOutputDisplay: false
|
||||
}
|
||||
|
||||
comfyClass: string;
|
||||
comfyNodeDef: ComfyNodeDef;
|
||||
displayName: string | null;
|
||||
@@ -37,6 +46,10 @@ export class ComfyBackendNode extends ComfyGraphNode {
|
||||
}
|
||||
}
|
||||
|
||||
get isOutputNode(): boolean {
|
||||
return this.comfyNodeDef.output_node;
|
||||
}
|
||||
|
||||
// comfy class -> input name -> input config
|
||||
private static defaultInputConfigs: Record<string, Record<string, ComfyInputConfig>> = {}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ export type InterfaceState = {
|
||||
indicatorValue: any,
|
||||
|
||||
graphTransitioning: boolean
|
||||
isJumpingToNode: boolean
|
||||
}
|
||||
|
||||
type InterfaceStateOps = {
|
||||
@@ -25,7 +26,8 @@ const store: Writable<InterfaceState> = writable(
|
||||
showIndicator: false,
|
||||
indicatorValue: null,
|
||||
|
||||
graphTransitioning: false
|
||||
graphTransitioning: false,
|
||||
isJumpingToNode: false,
|
||||
})
|
||||
|
||||
const debounceDrag = debounce(() => { store.update(s => { s.showIndicator = false; return s }) }, 1000)
|
||||
|
||||
@@ -812,8 +812,8 @@ type LayoutStateOps = {
|
||||
moveItem: (target: IDragItem, to: ContainerLayout, index?: number) => void,
|
||||
groupItems: (dragItemIDs: DragItemID[], attrs?: Partial<Attributes>) => ContainerLayout,
|
||||
ungroup: (container: ContainerLayout) => void,
|
||||
findLayoutEntryForNode: (nodeId: ComfyNodeID) => DragItemEntry | null,
|
||||
findLayoutForNode: (nodeId: ComfyNodeID) => IDragItem | null,
|
||||
findLayoutEntryForNode: (nodeId: NodeID) => DragItemEntry | null,
|
||||
findLayoutForNode: (nodeId: NodeID) => IDragItem | null,
|
||||
iterateBreadthFirst: (id?: DragItemID | null) => Iterable<DragItemEntry>,
|
||||
serialize: () => SerializedLayoutState,
|
||||
serializeAtRoot: (rootID: DragItemID) => SerializedLayoutState,
|
||||
@@ -1216,7 +1216,7 @@ function createRaw(workflow: ComfyBoxWorkflow | null = null): WritableLayoutStat
|
||||
store.set(state)
|
||||
}
|
||||
|
||||
function findLayoutEntryForNode(nodeId: ComfyNodeID): DragItemEntry | null {
|
||||
function findLayoutEntryForNode(nodeId: NodeID): DragItemEntry | null {
|
||||
const state = get(store)
|
||||
const found = Object.entries(state.allItems).find(pair =>
|
||||
pair[1].dragItem.type === "widget"
|
||||
@@ -1226,7 +1226,7 @@ function createRaw(workflow: ComfyBoxWorkflow | null = null): WritableLayoutStat
|
||||
return null;
|
||||
}
|
||||
|
||||
function findLayoutForNode(nodeId: ComfyNodeID): WidgetLayout | null {
|
||||
function findLayoutForNode(nodeId: NodeID): WidgetLayout | null {
|
||||
const found = findLayoutEntryForNode(nodeId);
|
||||
if (!found)
|
||||
return null;
|
||||
@@ -1511,16 +1511,12 @@ function getLayoutByDragItemID(dragItemID: DragItemID): WritableLayoutStateStore
|
||||
return Object.values(get(layoutStates).all).find(l => get(l).allItems[dragItemID] != null)
|
||||
}
|
||||
|
||||
function getDragItemByNode(node: LGraphNode): WidgetLayout | null {
|
||||
function getDragItemByNode(node: LGraphNode): IDragItem | null {
|
||||
const layout = getLayoutByNode(node);
|
||||
if (layout == null)
|
||||
return null;
|
||||
|
||||
const entry = get(layout).allItemsByNode[node.id]
|
||||
if (entry && entry.dragItem.type === "widget")
|
||||
return entry.dragItem as WidgetLayout;
|
||||
|
||||
return null;
|
||||
return layout.findLayoutForNode(node.id);
|
||||
}
|
||||
|
||||
export type LayoutStateStores = {
|
||||
@@ -1543,7 +1539,7 @@ export type LayoutStateStoresOps = {
|
||||
getLayoutByGraph: (graph: LGraph) => WritableLayoutStateStore | null,
|
||||
getLayoutByNode: (node: LGraphNode) => WritableLayoutStateStore | null,
|
||||
getLayoutByDragItemID: (dragItemID: DragItemID) => WritableLayoutStateStore | null,
|
||||
getDragItemByNode: (node: LGraphNode) => WidgetLayout | null,
|
||||
getDragItemByNode: (node: LGraphNode) => IDragItem | null,
|
||||
}
|
||||
|
||||
export type WritableLayoutStateStores = Writable<LayoutStateStores> & LayoutStateStoresOps;
|
||||
|
||||
@@ -404,7 +404,7 @@ function setActiveWorkflow(canvas: ComfyGraphCanvas, index: number | WorkflowIns
|
||||
|
||||
const workflow = state.openedWorkflows[index]
|
||||
if (workflow.id === state.activeWorkflowID)
|
||||
return;
|
||||
return state.activeWorkflow;
|
||||
|
||||
if (state.activeWorkflow != null)
|
||||
state.activeWorkflow.stop("app")
|
||||
|
||||
@@ -92,34 +92,40 @@ body {
|
||||
|
||||
&.primary {
|
||||
background: var(--button-primary-background-fill);
|
||||
&:hover {
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--button-primary-background-fill-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: var(--button-secondary-background-fill);
|
||||
&:hover {
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--button-secondary-background-fill-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&.ternary {
|
||||
background: var(--panel-background-fill);
|
||||
&:hover {
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--block-background-fill);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover:not(:disabled) {
|
||||
filter: brightness(85%);
|
||||
}
|
||||
&:active {
|
||||
&:active:not(:disabled) {
|
||||
filter: brightness(50%)
|
||||
}
|
||||
&.selected {
|
||||
&.selected:not(:disabled) {
|
||||
filter: brightness(80%)
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: var(--neutral-700);
|
||||
color: var(--neutral-400);
|
||||
opacity: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin disable-input {
|
||||
|
||||
Reference in New Issue
Block a user