Workflow creation/modified state
This commit is contained in:
@@ -12,6 +12,8 @@ import selectionState from "./stores/selectionState";
|
|||||||
import type { WritableLayoutStateStore } from "./stores/layoutStates";
|
import type { WritableLayoutStateStore } from "./stores/layoutStates";
|
||||||
import type { WorkflowInstID } from "./components/ComfyApp";
|
import type { WorkflowInstID } from "./components/ComfyApp";
|
||||||
import layoutStates from "./stores/layoutStates";
|
import layoutStates from "./stores/layoutStates";
|
||||||
|
import type { ComfyWorkflow } from "./stores/workflowState";
|
||||||
|
import workflowState from "./stores/workflowState";
|
||||||
|
|
||||||
type ComfyGraphEvents = {
|
type ComfyGraphEvents = {
|
||||||
configured: (graph: LGraph) => void
|
configured: (graph: LGraph) => void
|
||||||
@@ -29,6 +31,12 @@ export default class ComfyGraph extends LGraph {
|
|||||||
|
|
||||||
workflowID: WorkflowInstID | null = null;
|
workflowID: WorkflowInstID | null = null;
|
||||||
|
|
||||||
|
get workflow(): ComfyWorkflow | null {
|
||||||
|
if (this.workflowID == null)
|
||||||
|
return null;
|
||||||
|
return workflowState.getWorkflow(this.workflowID)
|
||||||
|
}
|
||||||
|
|
||||||
constructor(workflowID?: WorkflowInstID) {
|
constructor(workflowID?: WorkflowInstID) {
|
||||||
super();
|
super();
|
||||||
this.workflowID = workflowID;
|
this.workflowID = workflowID;
|
||||||
@@ -39,6 +47,9 @@ export default class ComfyGraph extends LGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override onBeforeChange(graph: LGraph, info: any) {
|
override onBeforeChange(graph: LGraph, info: any) {
|
||||||
|
if (this.workflow != null)
|
||||||
|
this.workflow.notifyModified()
|
||||||
|
|
||||||
console.debug("BeforeChange", info);
|
console.debug("BeforeChange", info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +81,9 @@ export default class ComfyGraph extends LGraph {
|
|||||||
this.doAddNode(node, layoutState, options);
|
this.doAddNode(node, layoutState, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.workflow != null)
|
||||||
|
this.workflow.notifyModified()
|
||||||
|
|
||||||
this.eventBus.emit("nodeAdded", node);
|
this.eventBus.emit("nodeAdded", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,9 +94,9 @@ export default class ComfyGraph extends LGraph {
|
|||||||
layoutState.nodeAdded(node, options)
|
layoutState.nodeAdded(node, options)
|
||||||
|
|
||||||
// All nodes whether they come from base litegraph or ComfyBox should
|
// All nodes whether they come from base litegraph or ComfyBox should
|
||||||
// have tags added to them. Can't override serialization for existing
|
// have tags added to them. Can't override serialization for litegraph's
|
||||||
// node types to add `tags` as a new field so putting it in properties
|
// base node types to add `tags` as a new field so putting it in
|
||||||
// is better.
|
// properties is better.
|
||||||
if (node.properties.tags == null)
|
if (node.properties.tags == null)
|
||||||
node.properties.tags = []
|
node.properties.tags = []
|
||||||
|
|
||||||
@@ -161,6 +175,9 @@ export default class ComfyGraph extends LGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ************** RECURSION ALERT ! **************
|
// ************** RECURSION ALERT ! **************
|
||||||
|
|
||||||
|
if (this.workflow != null)
|
||||||
|
this.workflow.notifyModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
override onNodeRemoved(node: LGraphNode, options: LGraphRemoveNodeOptions) {
|
override onNodeRemoved(node: LGraphNode, options: LGraphRemoveNodeOptions) {
|
||||||
@@ -182,10 +199,21 @@ export default class ComfyGraph extends LGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.workflow != null)
|
||||||
|
this.workflow.notifyModified()
|
||||||
|
|
||||||
this.eventBus.emit("nodeRemoved", node);
|
this.eventBus.emit("nodeRemoved", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override onInputsOutputsChange() {
|
||||||
|
if (this.workflow != null)
|
||||||
|
this.workflow.notifyModified()
|
||||||
|
}
|
||||||
|
|
||||||
override onNodeConnectionChange(kind: LConnectionKind, node: LGraphNode, slot: SlotIndex, targetNode: LGraphNode, targetSlot: SlotIndex) {
|
override onNodeConnectionChange(kind: LConnectionKind, node: LGraphNode, slot: SlotIndex, targetNode: LGraphNode, targetSlot: SlotIndex) {
|
||||||
|
if (this.workflow != null)
|
||||||
|
this.workflow.notifyModified()
|
||||||
|
|
||||||
// console.debug("ConnectionChange", node);
|
// console.debug("ConnectionChange", node);
|
||||||
this.eventBus.emit("nodeConnectionChanged", kind, node, slot, targetNode, targetSlot);
|
this.eventBus.emit("nodeConnectionChanged", kind, node, slot, targetNode, targetSlot);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { ComfyReroute } from "./nodes";
|
|||||||
import type { Progress } from "./components/ComfyApp";
|
import type { Progress } from "./components/ComfyApp";
|
||||||
import selectionState from "./stores/selectionState";
|
import selectionState from "./stores/selectionState";
|
||||||
import type ComfyGraph from "./ComfyGraph";
|
import type ComfyGraph from "./ComfyGraph";
|
||||||
|
import layoutStates from "./stores/layoutStates";
|
||||||
|
|
||||||
export type SerializedGraphCanvasState = {
|
export type SerializedGraphCanvasState = {
|
||||||
offset: Vector2,
|
offset: Vector2,
|
||||||
@@ -286,12 +287,15 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
|||||||
selectionState.update(ss => {
|
selectionState.update(ss => {
|
||||||
ss.currentSelectionNodes = Object.values(nodes)
|
ss.currentSelectionNodes = Object.values(nodes)
|
||||||
ss.currentSelection = []
|
ss.currentSelection = []
|
||||||
const ls = get(this.comfyGraph.layoutState)
|
const layoutState = layoutStates.getLayoutByGraph(this.graph);
|
||||||
|
if (layoutState) {
|
||||||
|
const ls = get(layoutState)
|
||||||
for (const node of ss.currentSelectionNodes) {
|
for (const node of ss.currentSelectionNodes) {
|
||||||
const widget = ls.allItemsByNode[node.id]
|
const widget = ls.allItemsByNode[node.id]
|
||||||
if (widget)
|
if (widget)
|
||||||
ss.currentSelection.push(widget.dragItem.id)
|
ss.currentSelection.push(widget.dragItem.id)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ss
|
return ss
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -303,12 +307,15 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
|||||||
ss.currentHoveredNodes.add(node.id)
|
ss.currentHoveredNodes.add(node.id)
|
||||||
}
|
}
|
||||||
ss.currentHovered.clear()
|
ss.currentHovered.clear()
|
||||||
const ls = get(this.comfyGraph.layoutState)
|
const layoutState = layoutStates.getLayoutByGraph(this.graph);
|
||||||
|
if (layoutState) {
|
||||||
|
const ls = get(layoutState)
|
||||||
for (const nodeID of ss.currentHoveredNodes) {
|
for (const nodeID of ss.currentHoveredNodes) {
|
||||||
const widget = ls.allItemsByNode[nodeID]
|
const widget = ls.allItemsByNode[nodeID]
|
||||||
if (widget)
|
if (widget)
|
||||||
ss.currentHovered.add(widget.dragItem.id)
|
ss.currentHovered.add(widget.dragItem.id)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ss
|
return ss
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,12 +186,7 @@ export default class ComfyApp {
|
|||||||
// Load previous workflow
|
// Load previous workflow
|
||||||
let restored = false;
|
let restored = false;
|
||||||
try {
|
try {
|
||||||
const json = localStorage.getItem("workflow");
|
restored = await this.loadStateFromLocalStorage();
|
||||||
if (json) {
|
|
||||||
const state = JSON.parse(json) as SerializedAppState;
|
|
||||||
await this.openWorkflow(state)
|
|
||||||
restored = true;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error loading previous workflow", err);
|
console.error("Error loading previous workflow", err);
|
||||||
notify(`Error loading previous workflow:\n${err}`, { type: "error", timeout: null })
|
notify(`Error loading previous workflow:\n${err}`, { type: "error", timeout: null })
|
||||||
@@ -202,10 +197,6 @@ export default class ComfyApp {
|
|||||||
await this.initDefaultWorkflow();
|
await this.initDefaultWorkflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
workflowState.createNewWorkflow(this.lCanvas);
|
|
||||||
workflowState.createNewWorkflow(this.lCanvas);
|
|
||||||
workflowState.createNewWorkflow(this.lCanvas);
|
|
||||||
|
|
||||||
// Save current workflow automatically
|
// Save current workflow automatically
|
||||||
// setInterval(this.saveStateToLocalStorage.bind(this), 1000);
|
// setInterval(this.saveStateToLocalStorage.bind(this), 1000);
|
||||||
|
|
||||||
@@ -256,17 +247,15 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveStateToLocalStorage() {
|
saveStateToLocalStorage() {
|
||||||
const workflow = workflowState.getActiveWorkflow();
|
|
||||||
if (workflow == null) {
|
|
||||||
notify("No active workflow!", { type: "error" })
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
uiState.update(s => { s.isSavingToLocalStorage = true; return s; })
|
uiState.update(s => { s.isSavingToLocalStorage = true; return s; })
|
||||||
const savedWorkflow = this.serialize(workflow);
|
const workflows = get(workflowState).openedWorkflows
|
||||||
const json = JSON.stringify(savedWorkflow);
|
const savedWorkflows = workflows.map(w => this.serialize(w));
|
||||||
localStorage.setItem("workflow", json)
|
const json = JSON.stringify(savedWorkflows);
|
||||||
|
localStorage.setItem("workflows", json)
|
||||||
|
for (const workflow of workflows)
|
||||||
|
workflow.isModified = false;
|
||||||
|
workflowState.set(get(workflowState));
|
||||||
notify("Saved to local storage.")
|
notify("Saved to local storage.")
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -277,6 +266,17 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadStateFromLocalStorage(): Promise<boolean> {
|
||||||
|
const json = localStorage.getItem("workflows");
|
||||||
|
if (!json) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const workflows = JSON.parse(json) as SerializedAppState[];
|
||||||
|
for (const workflow of workflows)
|
||||||
|
await this.openWorkflow(workflow)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static node_type_overrides: Record<string, typeof ComfyBackendNode> = {}
|
static node_type_overrides: Record<string, typeof ComfyBackendNode> = {}
|
||||||
static widget_type_overrides: Record<string, typeof SvelteComponentDev> = {}
|
static widget_type_overrides: Record<string, typeof SvelteComponentDev> = {}
|
||||||
|
|
||||||
@@ -525,6 +525,11 @@ export default class ComfyApp {
|
|||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createNewWorkflow(index: number) {
|
||||||
|
workflowState.createNewWorkflow(this.lCanvas, undefined, true);
|
||||||
|
selectionState.clear();
|
||||||
|
}
|
||||||
|
|
||||||
closeWorkflow(index: number) {
|
closeWorkflow(index: number) {
|
||||||
workflowState.closeWorkflow(this.lCanvas, index);
|
workflowState.closeWorkflow(this.lCanvas, index);
|
||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
@@ -602,6 +607,9 @@ export default class ComfyApp {
|
|||||||
|
|
||||||
download(filename, json, "application/json")
|
download(filename, json, "application/json")
|
||||||
|
|
||||||
|
workflow.isModified = false;
|
||||||
|
workflowState.set(get(workflowState));
|
||||||
|
|
||||||
console.debug(jsonToJsObject(json))
|
console.debug(jsonToJsObject(json))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -170,6 +170,8 @@
|
|||||||
if (spec.refreshPanelOnChange) {
|
if (spec.refreshPanelOnChange) {
|
||||||
doRefreshPanel()
|
doRefreshPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workflow.notifyModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProperty(node: LGraphNode, spec: AttributesSpec) {
|
function getProperty(node: LGraphNode, spec: AttributesSpec) {
|
||||||
@@ -205,6 +207,8 @@
|
|||||||
|
|
||||||
if (spec.refreshPanelOnChange)
|
if (spec.refreshPanelOnChange)
|
||||||
doRefreshPanel()
|
doRefreshPanel()
|
||||||
|
|
||||||
|
workflow.notifyModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVar(node: LGraphNode, spec: AttributesSpec) {
|
function getVar(node: LGraphNode, spec: AttributesSpec) {
|
||||||
@@ -241,6 +245,8 @@
|
|||||||
if (spec.refreshPanelOnChange) {
|
if (spec.refreshPanelOnChange) {
|
||||||
doRefreshPanel()
|
doRefreshPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workflow.notifyModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWorkflowAttribute(spec: AttributesSpec): any {
|
function getWorkflowAttribute(spec: AttributesSpec): any {
|
||||||
@@ -275,6 +281,8 @@
|
|||||||
|
|
||||||
// if (spec.refreshPanelOnChange)
|
// if (spec.refreshPanelOnChange)
|
||||||
doRefreshPanel()
|
doRefreshPanel()
|
||||||
|
|
||||||
|
workflow.notifyModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
function doRefreshPanel() {
|
function doRefreshPanel() {
|
||||||
|
|||||||
@@ -150,9 +150,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeWorkflow(event: Event, index: number) {
|
function createNewWorkflow() {
|
||||||
|
app.createNewWorkflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWorkflow(event: Event, index: number, workflow: ComfyWorkflow) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation()
|
event.stopImmediatePropagation()
|
||||||
|
|
||||||
|
if (workflow.isModified) {
|
||||||
|
if (!confirm("This workflow has unsaved changes. Are you sure you want to close it?"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
app.closeWorkflow(index);
|
app.closeWorkflow(index);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -189,13 +199,22 @@
|
|||||||
<button class="workflow-tab"
|
<button class="workflow-tab"
|
||||||
class:selected={index === $workflowState.activeWorkflowIdx}
|
class:selected={index === $workflowState.activeWorkflowIdx}
|
||||||
on:click={() => app.setActiveWorkflow(index)}>
|
on:click={() => app.setActiveWorkflow(index)}>
|
||||||
<span class="workflow-tab-title">{workflow.attrs.title}</span>
|
<span class="workflow-tab-title">
|
||||||
|
{workflow.attrs.title}
|
||||||
|
{#if workflow.isModified}
|
||||||
|
*
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
<button class="workflow-close-button"
|
<button class="workflow-close-button"
|
||||||
on:click={(e) => closeWorkflow(e, index)}>
|
on:click={(e) => closeWorkflow(e, index, workflow)}>
|
||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
|
<button class="workflow-add-new-button"
|
||||||
|
on:click={createNewWorkflow}>
|
||||||
|
➕
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="bottombar">
|
<div id="bottombar">
|
||||||
<div class="bottombar-content">
|
<div class="bottombar-content">
|
||||||
@@ -373,7 +392,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: var(--size-2);
|
gap: var(--size-4);
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-right: 1px solid var(--neutral-600);
|
border-right: 1px solid var(--neutral-600);
|
||||||
@@ -392,8 +411,10 @@
|
|||||||
|
|
||||||
> .workflow-close-button {
|
> .workflow-close-button {
|
||||||
display:block;
|
display:block;
|
||||||
width: 1.5rem;
|
width: 1.2rem;
|
||||||
height: 1.5rem;
|
height: 1.2rem;
|
||||||
|
font-size: 13px;
|
||||||
|
margin: auto;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
opacity: 50%;
|
opacity: 50%;
|
||||||
background: var(--neutral-500);
|
background: var(--neutral-500);
|
||||||
@@ -405,6 +426,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workflow-add-new-button {
|
||||||
|
background: var(--neutral-700);
|
||||||
|
filter: brightness(80%);
|
||||||
|
color: var(--neutral-500);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-top: 3px solid var(--neutral-600);
|
||||||
|
border-left: 1px solid var(--neutral-600);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--size-4);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(100%);
|
||||||
|
border-top-color: var(--neutral-600);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#bottombar {
|
#bottombar {
|
||||||
|
|||||||
@@ -58,6 +58,14 @@
|
|||||||
const title = widget.node.type.replace("/", "-").replace(".", "-")
|
const title = widget.node.type.replace("/", "-").replace(".", "-")
|
||||||
return `widget--${title}`
|
return `widget--${title}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _startDrag(e: MouseEvent | TouchEvent) {
|
||||||
|
startDrag(e, layoutState)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _stopDrag(e: MouseEvent | TouchEvent) {
|
||||||
|
stopDrag(e, layoutState)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@@ -85,7 +93,13 @@
|
|||||||
<div class="handle handle-hidden" class:hidden={!edit} />
|
<div class="handle handle-hidden" class:hidden={!edit} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if showHandles || hovered}
|
{#if showHandles || hovered}
|
||||||
<div class="handle handle-widget" class:hovered data-drag-item-id={widget.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/>
|
<div class="handle handle-widget"
|
||||||
|
class:hovered
|
||||||
|
data-drag-item-id={widget.id}
|
||||||
|
on:mousedown={_startDrag}
|
||||||
|
on:touchstart={_startDrag}
|
||||||
|
on:mouseup={_stopDrag}
|
||||||
|
on:touchend={_stopDrag}/>
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
{/key}
|
{/key}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action';
|
|||||||
import type { ComfyNodeID } from '$lib/api';
|
import type { ComfyNodeID } from '$lib/api';
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import type { ComfyWidgetNode } from '$lib/nodes/widgets';
|
import type { ComfyWidgetNode } from '$lib/nodes/widgets';
|
||||||
import type { ComfyWorkflow, WorkflowInstID } from '$lib/components/ComfyApp';
|
|
||||||
import type ComfyGraph from '$lib/ComfyGraph';
|
import type ComfyGraph from '$lib/ComfyGraph';
|
||||||
import type { WorkflowAttributes } from './workflowState';
|
import type { ComfyWorkflow, WorkflowAttributes, WorkflowInstID } from './workflowState';
|
||||||
|
import workflowState from './workflowState';
|
||||||
|
|
||||||
function isComfyWidgetNode(node: LGraphNode): node is ComfyWidgetNode {
|
function isComfyWidgetNode(node: LGraphNode): node is ComfyWidgetNode {
|
||||||
return "svelteComponentType" in node
|
return "svelteComponentType" in node
|
||||||
@@ -670,6 +670,7 @@ type LayoutStateOps = {
|
|||||||
deserialize: (data: SerializedLayoutState, graph: LGraph) => void,
|
deserialize: (data: SerializedLayoutState, graph: LGraph) => void,
|
||||||
initDefaultLayout: () => void,
|
initDefaultLayout: () => void,
|
||||||
onStartConfigure: () => void
|
onStartConfigure: () => void
|
||||||
|
notifyWorkflowModified: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SerializedLayoutState = {
|
export type SerializedLayoutState = {
|
||||||
@@ -770,6 +771,7 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
|
|
||||||
console.debug("[layoutState] addContainer", state)
|
console.debug("[layoutState] addContainer", state)
|
||||||
store.set(state)
|
store.set(state)
|
||||||
|
notifyWorkflowModified();
|
||||||
// runOnChangedForWidgetDefaults(dragItem)
|
// runOnChangedForWidgetDefaults(dragItem)
|
||||||
return dragItem;
|
return dragItem;
|
||||||
}
|
}
|
||||||
@@ -801,6 +803,7 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
|
|
||||||
console.debug("[layoutState] addWidget", state)
|
console.debug("[layoutState] addWidget", state)
|
||||||
moveItem(dragItem, parent, index)
|
moveItem(dragItem, parent, index)
|
||||||
|
notifyWorkflowModified();
|
||||||
// runOnChangedForWidgetDefaults(dragItem)
|
// runOnChangedForWidgetDefaults(dragItem)
|
||||||
return dragItem;
|
return dragItem;
|
||||||
}
|
}
|
||||||
@@ -834,6 +837,7 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
delete state.allItemsByNode[widget.node.id]
|
delete state.allItemsByNode[widget.node.id]
|
||||||
}
|
}
|
||||||
delete state.allItems[id]
|
delete state.allItems[id]
|
||||||
|
notifyWorkflowModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) {
|
function nodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) {
|
||||||
@@ -946,6 +950,7 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
state.allItems[target.id].parent = toEntry.dragItem;
|
state.allItems[target.id].parent = toEntry.dragItem;
|
||||||
|
|
||||||
console.debug("[layoutState] Move child", target, toEntry, index)
|
console.debug("[layoutState] Move child", target, toEntry, index)
|
||||||
|
notifyWorkflowModified();
|
||||||
|
|
||||||
store.set(state)
|
store.set(state)
|
||||||
}
|
}
|
||||||
@@ -976,6 +981,7 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
|
|
||||||
console.debug("[layoutState] Grouped", container, parent, state.allItems[container.id].children, index)
|
console.debug("[layoutState] Grouped", container, parent, state.allItems[container.id].children, index)
|
||||||
|
|
||||||
|
notifyWorkflowModified();
|
||||||
store.set(state)
|
store.set(state)
|
||||||
return container
|
return container
|
||||||
}
|
}
|
||||||
@@ -1033,18 +1039,20 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
allItems: {},
|
allItems: {},
|
||||||
allItemsByNode: {},
|
allItemsByNode: {},
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: false,
|
isConfiguring: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const root = addContainer(null, { direction: "horizontal", title: "" });
|
const root = addContainer(null, { direction: "horizontal", title: "" });
|
||||||
const left = addContainer(root, { direction: "vertical", title: "" });
|
const left = addContainer(root, { direction: "vertical", title: "" });
|
||||||
const right = addContainer(root, { direction: "vertical", title: "" });
|
const right = addContainer(root, { direction: "vertical", title: "" });
|
||||||
|
|
||||||
const state = get(store)
|
store.update(s => {
|
||||||
state.root = root;
|
s.root = root;
|
||||||
store.set(state)
|
s.isConfiguring = false;
|
||||||
|
return s;
|
||||||
|
})
|
||||||
|
|
||||||
console.debug("[layoutState] initDefault", state)
|
console.debug("[layoutState] initDefault")
|
||||||
}
|
}
|
||||||
|
|
||||||
function serialize(): SerializedLayoutState {
|
function serialize(): SerializedLayoutState {
|
||||||
@@ -1143,6 +1151,11 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notifyWorkflowModified() {
|
||||||
|
if (!get(store).isConfiguring)
|
||||||
|
workflow.notifyModified();
|
||||||
|
}
|
||||||
|
|
||||||
const layoutStateStore: WritableLayoutStateStore =
|
const layoutStateStore: WritableLayoutStateStore =
|
||||||
{
|
{
|
||||||
...store,
|
...store,
|
||||||
@@ -1161,7 +1174,8 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
initDefaultLayout,
|
initDefaultLayout,
|
||||||
onStartConfigure,
|
onStartConfigure,
|
||||||
serialize,
|
serialize,
|
||||||
deserialize
|
deserialize,
|
||||||
|
notifyWorkflowModified
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutStates.update(s => {
|
layoutStates.update(s => {
|
||||||
|
|||||||
@@ -69,7 +69,12 @@ export class ComfyWorkflow {
|
|||||||
/*
|
/*
|
||||||
* Global workflow attributes
|
* Global workflow attributes
|
||||||
*/
|
*/
|
||||||
attrs: WorkflowAttributes
|
attrs: WorkflowAttributes;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* True if an unsaved modification has been detected on this workflow
|
||||||
|
*/
|
||||||
|
isModified: boolean = false;
|
||||||
|
|
||||||
get layout(): WritableLayoutStateStore | null {
|
get layout(): WritableLayoutStateStore | null {
|
||||||
return layoutStates.getLayout(this.id)
|
return layoutStates.getLayout(this.id)
|
||||||
@@ -89,6 +94,11 @@ export class ComfyWorkflow {
|
|||||||
this.graph = new ComfyGraph(this.id);
|
this.graph = new ComfyGraph(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyModified() {
|
||||||
|
this.isModified = true;
|
||||||
|
store.set(get(store));
|
||||||
|
}
|
||||||
|
|
||||||
start(key: string, canvas: ComfyGraphCanvas) {
|
start(key: string, canvas: ComfyGraphCanvas) {
|
||||||
if (this.canvases[key] != null)
|
if (this.canvases[key] != null)
|
||||||
throw new Error(`This workflow is already being displayed on canvas ${key}`)
|
throw new Error(`This workflow is already being displayed on canvas ${key}`)
|
||||||
|
|||||||
@@ -79,9 +79,11 @@ export function startDrag(evt: MouseEvent, layoutState: WritableLayoutStateStore
|
|||||||
|
|
||||||
layoutState.set(ls)
|
layoutState.set(ls)
|
||||||
selectionState.set(ss)
|
selectionState.set(ss)
|
||||||
|
layoutState.notifyWorkflowModified();
|
||||||
};
|
};
|
||||||
|
|
||||||
export function stopDrag(evt: MouseEvent, layoutState: WritableLayoutStateStore) {
|
export function stopDrag(evt: MouseEvent, layoutState: WritableLayoutStateStore) {
|
||||||
|
layoutState.notifyWorkflowModified();
|
||||||
};
|
};
|
||||||
|
|
||||||
export function graphToGraphVis(graph: LGraph): string {
|
export function graphToGraphVis(graph: LGraph): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user